[Trick] Обработка сообщений
От: remark Россия http://www.1024cores.net/
Дата: 06.12.06 06:18
Оценка: 173 (13)
Хочу поделиться трюком, который сам с успехом применяю уже достаточное время. Надюсь кто-то тоже найдёт его полезным. Ну и, конечно, если у кого будут какие замечания и комменты, то обсудим

Проблема

Так утомительно писать все эти:

afx_msg void OnButton1();
afx_msg void OnButton2();
...
afx_msg void OnButtonN();

 
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnButton2)
...
ON_BN_CLICKED(IDC_BUTTONN, OnButtonN)


Переключаться между h и cpp, перекомпилировать все связанные файлы, особенно если по стандарту эти обработчики надо ещё комментировать...

Решение

Что я делаю:

class MyDlg : public CDialog
{
...
  /** Обработчик всех команд
  */
  template<UINT ID> afx_msg void OnCommand();
};


В карте сообщений это теперь выглядит так:

ON_BN_CLICKED(IDC_BUTTON1, OnCommand<IDC_BUTTON1>)
ON_BN_CLICKED(IDC_BUTTON2, OnCommand<IDC_BUTTON2>)


И обработчики:

template<> void MyDlg::OnCommand<IDC_BUTTON1>()
{
...
}

template<> void MyDlg::OnCommand<IDC_BUTTON2>()
{
...
}


Что получаем

+ При добавлении и удалении обработчиков не надо менять заголочный файл и перекомпилировать связанные файлы
+ Не надо писать формальные комментарии для функций в заголочной файле, т.к. функция реально одна! (ну что тут комментировать, даже ёжику понятно, что OnCommand<IDC_BUTTON1> — это обработчик комманды IDC_BUTTON1)
+ Ещё один плюс не так очевиден, но очень приятен: достаточно часто встаёт задача по ID комманды найти её обработчик и наоборот. Обычно приходится вначале по идентификатору ID_SEND_REQUEST находить в карте сообщений функцию OnRequestSend() И потом находить тело функции OnRequestSend(). Теперь же можно по идентификатору команды сразу найти обработчик и наоборот, без лишнего шага
— Карту сообщений надо перенести в самый низ файла, что бы она было после всех обработчиков. Ну это не очень серьёзный минус. Я ни с какими реальными неудобствами не столкнулся.


Идём дальше

Во-первых, такие обработчики можно применять не только для команд. Ничто не мешает написать:

/**    Обработчик обновления элемента пользовательского интерфейса
 *    @template ID Идентификатор команды, для которой происходит обновление
 */
template<UINT ID> afx_msg void OnUpdateUI(CCmdUI* cmdUI);


И даже:

/**    Обработчик уведомления от контрола
 *    @template ControlID Идентификатор контрола, от которого приходит уведомление
 *    @template NotifyCode Код уведомления
 */
template<UINT ControlID, UINT NotifyCode>
void OnNotify(NMHDR* pNMHDR, LRESULT* pResult);


Ещё дальше

Зачастую в обработчиках команд должен присутствовать некий общий код — например, логирование и обработка исключений. Делаем следующее. Создаём базовый класс для диалогов (ну я надеюсь, Вы и так используете общие базовые классы для диалогов в приложениях ):

template<typename DerivedDlg>
class MyAppDialog : public CDialog
{
...
protected:
    // Обработчик всех комманд с общей частью
    template<UINT ID> afx_msg void OnCommandHandler()
    {
        LOG << "Command: " << ID;
        try
        {
            // Вот здесь вызываем конкретный обработчик
            static_cast<DerivedDlg*>(this)->OnCommand<ID>();
            LOG << "Success";
        }
        catch (CException* ex)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
        catch (const MyAppException& ex)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
        catch (const std::exception& ex)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
        catch (...)
        {
            LOG << "Exception...";
            AfxMessageBox("...");
        }
    }
};


Диалоги теперь наследуем от этого класса:

class MyDlg: public MyAppDialog<MyDlg>


И в карте сообщений прописываем уже базовый обработчик:

ON_BN_CLICKED(IDC_BUTTON1, OnCommandHandler<IDC_BUTTON1>)
ON_BN_CLICKED(IDC_BUTTON2, OnCommandHandler<IDC_BUTTON2>)


Всё! Все команды теперь будут автоматически содержать общую часть, её больше не надо дублировать!

Если у кого будут какие замечания и предложения по улучшению подхода (улучшить ещё можно, я уверен) буду рад обсудить.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Обработка сообщений
От: Кодт Россия  
Дата: 07.12.06 09:37
Оценка: :))) :)))
Здравствуйте, remark, Вы писали:

<>

Как из MFC сделать WTL за полчаса

... << RSDN@Home 1.2.0 alpha rev. 655>>
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Re[2]: [Trick] Обработка сообщений
От: remark Россия http://www.1024cores.net/
Дата: 07.12.06 10:46
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, remark, Вы писали:


К><>


К>

Как из MFC сделать WTL за полчаса

К>

Ключевое слово "за полчаса"
Даже за 10 минут


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Обработка сообщений
От: Vain Россия google.ru
Дата: 13.12.06 15:30
Оценка: 8 (1)
Здравствуйте, remark, Вы писали:

R>Хочу поделиться трюком, который сам с успехом применяю уже достаточное время. Надюсь кто-то тоже найдёт его полезным. Ну и, конечно, если у кого будут какие замечания и комменты, то обсудим

небольшой коммент:
class MyDlg : public CDialog
{
...
  /** Обработчик всех команд
  */
  template<UINT ID> afx_msg void OnCommand() { int __error[0]; }
};

Если у общего объявления шаблона функции в классе/неймспейсе нету тела, то чтобы вместо нелинкови (в случае вызова) была некомпиляция, можно писать тело со статик ассертом.
А если шаблон функции не в классе, то можно вместо тела добавить static
Автор: Vain
Дата: 16.11.06
в объявление.

R>
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Went: Да где угодно могут. Я не согласен с посылом, что "раз поехал в НРы, значит подверг себя какой-то особой опасности".
Re[2]: [Trick] Обработка сообщений
От: remark Россия http://www.1024cores.net/
Дата: 13.12.06 16:13
Оценка:
Здравствуйте, Vain, Вы писали:

V>Здравствуйте, remark, Вы писали:


R>>Хочу поделиться трюком, который сам с успехом применяю уже достаточное время. Надюсь кто-то тоже найдёт его полезным. Ну и, конечно, если у кого будут какие замечания и комменты, то обсудим

V>небольшой коммент:

+1
Вообще ценно, да, это будет полезно.

R>>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.