Перейти к содержанию
    

Как известно в С++ можно получить адрес функции члена только конкретного класса, что не затрудняет реализовать обмен событиями между объектами через callback. Обсуждений этой темы было уже много, повторяться не буду:

http://www.rsdn.ru/article/cpp/fastdelegate.xml

http://forum.developing.ru/showthread.php?p=12952#post12952

http://alenacpp.blogspot.com/

http://www.newty.de/fpt/index.html

http://dobrokot.nm.ru/cpp/CppMethodsCallback.html

 

Из специфики встраиваемых вытикают следующие ограничения: мин. потребление памяти, минимальное время вызова, использование кучи не жалательно, переносимость.

Я лично остановился на варианте интерфейсов, плюсы и минусы:

+ прозрачная реализация в стиле ООП

+ для вызова храним только один указатель, для 32бит проц. соответственно 4 байта

+ не использем кучу

+ можно объеденить однотипные события в один интерфейс (экономим память)

- нужна потдержка множественного наследования в компиляторе

- дополнительные расходы на размер кода при множественном наследовании (но вполне допустимые)

- для каждого типа/группы событий приходиться описывать класс интерфейса

- обработка событий от разных объектов и классов получается в одном методе, приходиться использовать switch (если это кретично, то можно обрабатывать события во вложенном классе слушателе который объявлен как друг)

 

Выглядет это примерно так:

//Интерфейс события мыши

class IMouseEvent {

private:

protected:

public:

virtual void OnMouseDown(void);

virtual void OnMouseUp(void);

virtual void OnClick(void);

};

 

//Интерфейс события клавиатуры

class IKeyEvent {

private:

protected:

public:

virtual void OnKeyDown(void);

virtual void OnKeyUp(void);

virtual void OnKeyPress(void);

};

 

//Базовый класс

class TControl {

private:

protected:

public:

IMouseEvent* OnMouseEvent;

IKeyEvent* OnKeyEvent;

};

 

//Окно

class TWindow: public TControl {

private:

protected:

public:

};

 

//Конкретное окно, добавлены инетерфейсы для обработки события

class TConcreteWindow: public IMouseEvent, public IKeyEvent, public TForm{

private:

protected:

public:

void OnClick(void);

virtual void OnKeyPress(void);

};

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Замечательно. Я так понял вопроса в посте не содержится. Это хорошо. А при чем тут ARM ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ну да это не вопрос, я предлагую обсудить возможные реализации. ARM... я сейчас для армов пишу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ну да это не вопрос, я предлагую обсудить возможные реализации. ARM... я сейчас для армов пишу.

Ну тогда можно просто взять, например, Атмеловский USB Framework и посмотреть, как там сделаны коллбэки на чистых Сях. :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Дурное дело нехитрое - вызвать функцию по указателю.

Что тут обсуждать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

class TConcreteWindow: public IMouseEvent, public IKeyEvent, public TForm{

...

};

Этот вариант прекрасно будет работать в IAR в eec++. Проверено.

PS. Что накосячили БГ и компания лучше обсудить в форумах по VC++ и C++Builder, а вопросы по дальнейщей стандартизвции C++ лучше направлять Герберту Шилдту (если нужны явки и пароли, я Вам помогу). :biggrin:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как известно в С++ можно получить адрес функции члена только конкретного класса, что не затрудняет реализовать обмен событиями между объектами через callback.

 

Захотел сделать работу с периферией посредством классов. Но возникло два вопроса: как написать один код для каждого типа периферии, но чтоб он работал независимо от ее количества (к примеру UART0, UART1..) и как потом прерывания обрабатывать. Ответ был найден в параметризации шаблона класса базовым адресом в памяти. что-то вроде template<unsigned Adr> class CUart {..}; Соответственно в нем уже работать только по смещению.

 

Но тут возник интересный ньюанс: а как делать обработчик прерывания? Ведь его адрес надо записать в контроллер? Сделал так: все наследуются от одного базового сласса с виртуальной функцией Isr. В конструкторе все экземпляры регистрируются в глобальном массиве. Для каждого класса (читай номера устройства) делается статическая функция, которая и регистрируется в контроллере прерываний. А уже она по своему вызову выбирает из массива адрес соответствующего ей экзепляра и дергает его Isr.

 

Так к чему все это? Казалось бы работает, и не надо трогать.. Но нет! Ведь можно же еще уменьшить текст кода! А именно: сделать эту статическую функцию параметризируемой номером периферии и записать всего один раз template<unsigned N> void IsrExe(){ Array[N]->Isr();} Так вот мой Keil Arm не хочет адрес так описанной функции помещать в котроллер, а под MS VC проверяю - все в порядке.

 

Так что речь, пока, к сожалению, можно вести не о том, как хотелось бы, а о том, что позволяет выбранный инструментарий..

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Захотел сделать работу с периферией посредством классов. Но возникло два вопроса: как написать один код для каждого типа периферии, но чтоб он работал независимо от ее количества (к примеру UART0, UART1..) и как потом прерывания обрабатывать. Ответ был найден в параметризации шаблона класса базовым адресом в памяти. что-то вроде template<unsigned Adr> class CUart {..};

Это логично.

 

Но тут возник интересный ньюанс: а как делать обработчик прерывания? Ведь его адрес надо записать в контроллер? Сделал так: все наследуются от одного базового сласса с виртуальной функцией Isr. В конструкторе все экземпляры регистрируются в глобальном массиве. Для каждого класса (читай номера устройства) делается статическая функция, которая и регистрируется в контроллере прерываний. А уже она по своему вызову выбирает из массива адрес соответствующего ей экзепляра и дергает его Isr.

 

Так к чему все это? Казалось бы работает, и не надо трогать.. Но нет! Ведь можно же еще уменьшить текст кода! А именно: сделать эту статическую функцию параметризируемой номером периферии и записать всего один раз template<unsigned N> void IsrExe(){ Array[N]->Isr();} Так вот мой Keil Arm не хочет адрес так описанной функции помещать в котроллер, а под MS VC проверяю - все в порядке.

:a14:

 

Так что речь, пока, к сожалению, можно вести не о том, как хотелось бы, а о том, что позволяет выбранный инструментарий..

Добавлю, и желательно знать глюки и фичи используемого компилятора. :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

template<unsigned Adr> class CUart {..};

Или я чёта не понимаю или это оверхед по коду экономящий по одному слову RAM-a на экземпляр...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Или я чёта не понимаю или это оверхед по коду экономящий по одному слову RAM-a на экземпляр...

 

Следует обратить внимание на фразу "что-то вроде". Такая обертка используется для определения класса каждого регистра.

 

    template <unsigned A>    class CRxBufReg{
    public:
        CBitsRO    <A, 0,  8>    Data;
        CMixRegRO_Macro;
    };

 

Внутри класса устройства периферии определяется через typedef

 

    typedef CRxBufReg        <ECurBaseAdr + 0x00>    CRxBufReg_t;

 

соответсвенно, базовый адрес хранит сам класс устройства

 

template<unsigned N>
class CUartDevice : public IUartDeviceBase {
...
    enum {ECurBaseAdr = ERegBaseAdr + N*ERegStepAdr};
};

 

ну и как все это выглядит в использовании:

 

template<unsigned N>
BYTE CUartDevice<N>::Take()
{
    CRxBufReg_t    RxBufReg;
    BYTE Ret = RxBufReg;
    return Ret;    
};

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот я и не понимаю зачем для 3-х одинаковых UART-ов иметь в коде по три набора функций.

для той же

template<unsigned N>
BYTE CUartDevice<N>::Take()

будет три её реализации отличающихся одним числом - адресом регистра.

 

Ну и ИМХО применение шаблонов тут ведёт только к усложнению понимания кода.

Мож я чего не вижу? Просвятите?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот я и не понимаю зачем для 3-х одинаковых UART-ов иметь в коде по три набора функций.

для той же

template<unsigned N>
BYTE CUartDevice<N>::Take()

будет три её реализации отличающихся одним числом - адресом регистра.

 

А какая альтернатива? Делать switch внутри функции? Вычислять адрес во время исполнения?

И еще стоит отметить, что шаблоны превращаются в код только если их вызвать явно. Т.е. если один из Uart-ов, к примеру, не используется, то кода для него и не сгенерируется. Это, кстати, является весьма трудной задачей для Keil. Он может просто не заметить такого вызова, а потом сиди-ищи, куда код делся.

А еще он не любит прерывания на параметризируемые классы в IDE ставить, хотя по asm-коду ходит

 

Ну и ИМХО применение шаблонов тут ведёт только к усложнению понимания кода.

Мож я чего не вижу? Просвятите?

 

Пример использования:

 

CUartDevice<0> _Uart;

_Uart.Start();

 

А все остальное пользователя уже и не касается.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А какая альтернатива? Делать switch внутри функции? Вычислять адрес во время исполнения?

Зачем свич? Например храним в экземпляре класса базовый адрес, вычисления не такие уж громоздкие, а учитывая как ARM грузит константы в регистры и оверхеда на вычисления может не понадобиться. :)

А код переделываем в такой:

 

CUartDevice _Uart(0);

_Uart.Start();

 

И также все остальное пользователя уже и не касается.

Впрочем спорить не собираюсь, думал мож я чего не вижу/замечаю/понимаю.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Читал архивы и наткнулся... Стало интересно, прочитал, кое - что взял на вооружение. Но непонятен один момент - как устроен класс "CBitsRO", а конкретно каким образом он осуществляет доступ к регистру с адресом, определяемым параметром шаблона?

 

template <unsigned A> class CRxBufReg{

public:

CBitsRO <A, 0, 8> Data;

CMixRegRO_Macro;

};

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Читал архивы и наткнулся... Стало интересно, прочитал, кое - что взял на вооружение. Но непонятен один момент - как устроен класс "CBitsRO", а конкретно каким образом он осуществляет доступ к регистру с адресом, определяемым параметром шаблона?

 

Меня тоже заинтересовала эта тема! Уважаемый grau, нельзя ли поподробней?

Вот пишу (IAR AVR):

 

typedef uint8_t volatile __tiny IO_REG;

template <IO_REG &r>
void f(uint8_t mask)
{
    r |= mask;
}

int main()
{
    f<PORTB>(0x77);    
    return 0;
}

 

На это ругается компилятор: Internal Error: [any]: Unexpected exception

Что я неправильно написал?

Изменено пользователем landrey

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...