Jump to content

    
Sign in to follow this  
HEX

call back

Recommended Posts

Как известно в С++ можно получить адрес функции члена только конкретного класса, что не затрудняет реализовать обмен событиями между объектами через 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);

};

Share this post


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

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

Share this post


Link to post
Share on other sites
class TConcreteWindow: public IMouseEvent, public IKeyEvent, public TForm{

...

};

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

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

Share this post


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

 

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

 

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

 

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

 

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

Share this post


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

Это логично.

 

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

 

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

:a14:

 

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

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

Share this post


Link to post
Share on other sites
Или я чёта не понимаю или это оверхед по коду экономящий по одному слову 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;    
};

Share this post


Link to post
Share on other sites

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

для той же

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

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

 

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

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

Share this post


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

для той же

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

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

 

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

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

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

 

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

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

 

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

 

CUartDevice<0> _Uart;

_Uart.Start();

 

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

Share this post


Link to post
Share on other sites
А какая альтернатива? Делать switch внутри функции? Вычислять адрес во время исполнения?

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

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

 

CUartDevice _Uart(0);

_Uart.Start();

 

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

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

Share this post


Link to post
Share on other sites

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

 

template <unsigned A> class CRxBufReg{

public:

CBitsRO <A, 0, 8> Data;

CMixRegRO_Macro;

};

Share this post


Link to post
Share on other sites
Читал архивы и наткнулся... Стало интересно, прочитал, кое - что взял на вооружение. Но непонятен один момент - как устроен класс "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

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

Edited by landrey

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this