Интересная идея, я бы еще добавил сортировку не только по номерам сообщений,
но и по кодам — типа как работает COMMAND_HANDLER_EX(), COMMAND_CODE_HANDLER_EX() и т.д.
Как это сделать расширяемо и лаконично — вопрос.
Здравствуйте, WolfHound, Вы писали:
WH>Сейчас наткнулся на SWL идея там на 5+ но реализация на 3-
А почему на 3- ? Вы наверное имеели в виду, что используется неассоциативный поиск по обработчикам?
На самом деле используется двоичный поиск в предварительно отсортированном масиве — это тоже очень быстро и никаких динамических выделений
Здравствуйте, yaroslav_v, Вы писали:
_>Интересная идея, я бы еще добавил сортировку не только по номерам сообщений, _>но и по кодам — типа как работает COMMAND_HANDLER_EX(), COMMAND_CODE_HANDLER_EX() и т.д. _>Как это сделать расширяемо и лаконично — вопрос.
Я уже потом понял что функционалал связанный с поиском обработчиков можно вынести за пределы базового класса. В этом случае можно использовать многоуровневую диспетчеризацию т.е. одна карта в WndProc для сообщений первого уровня, в обработчике WM_COMMAND используется карта для поиска обработчиков по ID и в WM_NOTIFY то же самое.
Здравствуйте, Kluev, Вы писали:
K>А почему на 3- ? Вы наверное имеели в виду, что используется неассоциативный поиск по обработчикам?
Не я это понял. Но там много тонких ошибок которые рано или позно выстрелят. Много лишнего кода. Все ошибки перечислять очень долго...
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Не я это понял. Но там много тонких ошибок которые рано или позно выстрелят. Много лишнего кода. Все ошибки перечислять очень долго...
Здравствуйте, WolfHound, Вы писали:
WH>Наброски моего варианта
Сидел, выписывал все использованные С++ трюки. Насчитал штук 5 незнакомых и сразу взял на вооружение. Особенно меня порадовал параметр ссылка на массив с размером по параметру шаблона — вроде совершенно простой, но какой полезный! Правда синтаксис для меня совершенно дикий.
Кстати, при внедрении этого кода в свою библиотеку я избавился от указателей — не нужны они здесь — заменил на ссылки.
Единственное место, где указатель лучше оставить — это GetHandler, но там я сделал следующую конструкцию, чтобы указатель можно было только использовать для доступа к членам:
//! "ссылка" на класс с проверкой.template <class T> class Ref
{
T* _ref; //!< privatepublic:
Ref( T* ptr )
: _ptr (ptr) {}
operator bool() const
{ return (_ptr!=NULL); }
T* operator->() //!< non-const "reference"
{ assert(_ptr!=NULL); return _ptr; }
const T* operator->() const//!< const "reference"
{ assert(_ptr!=NULL); return _ptr; }
};
//...class Handler
{
struct SortedHandlers
{
//...
//! возвращаемый "указатель" можно только использовать
//! для доступа к членам и для проверки валидности.
Ref<Handler> GetHandler( UINT id )
{
MapTypeIter iter=handlers_.find(id);
if(iter==handlers_.end())return 0;
return iter->second;
}
};
//...
};
Ещё в одном месте инициализацию ссылки пришлось передвинуть в список инициализации.
class Handler
{
CallProxyBase& proxy_;
//...template<UINT id, class T> Handler(void (T::*fn)(Msg<id>&))
: id_(id)
//инициализировать ссылку можно только в списке инициализации
, proxy_ (StaticCallProxy<id, T>(fn))
{}
//! статический прокси вынесенный в методtemplate<UINT id, class T> static inline CallProxy&
StaticCallProxy(void (T::*fn)(Msg<id>&))
{
static CallProxy<id, T> proxy(fn);
return proxy;
}
};
Здравствуйте, lboss, Вы писали: L>Но я не могу придумать как форсировать вызов конструктора Initializer'а... Пришлось придумывать MsgResult...
Интересная методика. Осталось разобраться почему она вообще работает.
...
Ах-ха! Нашёл.
Здравствуйте, limax, Вы писали:
L>Сидел, выписывал все использованные С++ трюки. Насчитал штук 5 незнакомых и сразу взял на вооружение.
Хм я и не знал что столько трюков использовал L>Особенно меня порадовал параметр ссылка на массив с размером по параметру шаблона — вроде совершенно простой, но какой полезный! Правда синтаксис для меня совершенно дикий.
Во завернул L>Кстати, при внедрении этого кода в свою библиотеку я избавился от указателей — не нужны они здесь — заменил на ссылки.
Тоже вариант L>Единственное место, где указатель лучше оставить — это GetHandler, но там я сделал следующую конструкцию, чтобы указатель можно было только использовать для доступа к членам:
Хм Не вижу смысла. L>
L> T* _ref; //Тут _ref дальше _ptr
L>
Это мелочи... Но имена _* и __* насколько я помню зарезервированы для разработчиков компилятора.
L>Ещё в одном месте инициализацию ссылки пришлось передвинуть в список инициализации. L>
L> , proxy_ (StaticCallProxy<id, T>(fn))
L>
Явно параметры указывать не обязательно компилятор сам найдет.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Т.е. ты предлагаешь пойти по методу WinLib от Бартоша Милефски?
Я таким и пользовался года два. Перечислю недостатки, в порядке возрастания вредности:
Стандартных сообщений много, каждое хочется прописать в библиотеку. Получаем опупенную vtable для каждого класса. Согласен, это мелочь, но неприятно.
Все сообщения обрабатывающиеся таким образом должны быть прописаны ЧАСТНЫМ образом в ProcessMessage или на худой конец в WndProc (если ты конечно используешь параметры специфичные для каждого сообщения, а не тупые WPARAM/LPARAM). При расширении библиотеки нужно следить за добавлением обработчиков.
Ошибка в одной букве в имени, или неправильный порядок аргументов при переопределении обработчика — и прощай надёжность. Каждый раз нужно заглядывать в библиотечное определение и проверять, так ли ты написал.
Любое изменение определения обработчика в библиотеке сопровождается геммороем поиска всех обработчиков в клиентском коде. Т.е. "написал — не вздумай больше трогать". Монолитность старого кода — очень хреново. Именно это являлось для меня самым большим минусом , т.к. я сильно дорабатывал библиотеку в течении всего времени эксплуатации.CS>Или в извращениях есть некая прэлэсть которой я не вижу?
Основные плюсы по сравнению с WinLib:
Гибкость и независимость от библиотеки. При добавлении обработчика нового сообщения, все специфичные для сообщения вещи указываются в специализации сообщения, т.е. шаблона аргумента обработчика (те самые Msg<WM_PAINT>).
Стандарт записи обработчика в клиентском коде. Не нужно искать и записывать типы и порядок аргументов обработчика. все специфичные для сообщения параметры и методы находятся в одном аргументе.
Локальность, а в варианте от lboss
Здравствуйте, WolfHound, Вы писали:
WH>Хм я и не знал что столько трюков использовал
Ну дык... Забыл, как сам учился С++ и хлопал себя по лбу, увидев очередную полезную вещь реализованную в одной строке?
L>Единственное место, где указатель лучше оставить — это GetHandler, но там я сделал следующую конструкцию, чтобы указатель можно было только использовать для доступа к членам: WH>Хм Не вижу смысла.
Смысл в том, чтобы сделать библиотеку доступной бестолковому программисту. Т.е. нужна некоторая защита от дурака. Получить "ссылку" и проверить её валидность можно, а вот использовать объект, кроме как методами — нельзя. Сделал просто так, "для кумплекту"
L> T* _ref; //WH> Тут _ref дальше _ptr //L> угму. по памяти набивал, да и ссылка там сначала была.
WH>Это мелочи... Но имена _* и __* насколько я помню зарезервированы для разработчиков компилятора.
Старая песня: Имена __* зарезирвированы.
Имена _X*, где X — ЗАГЛАВНАЯ буква, зарезервированы. Но _abcd — можно использовать в клиентском коде.
По-моему так.
А если даже кое-где и не так, то фиг с ним. Мне намного удобнее писать (и нагляднее читать) _member, чем member_ или не дай бог m_member.
Здравствуйте, limax, Вы писали:
L>... Перечислю недостатки, в порядке возрастания вредности:
L> L>Стандартных сообщений много, каждое хочется прописать в библиотеку. Получаем опупенную vtable для каждого класса. Согласен, это мелочь, но неприятно.
Первое. Стандартных сообщений не много. десяток. ну может два.
Первичных событий вообще столько же сколько методов ввода — два : клавиатура, мышь.
Плюс события оконной системы.
Про vtable: а предлагамые map<event> или if(msg == 1) {} else if (msg == 2) {} (в стиле WTL) будут меньше места занимать и быстрее выполняться?
По этому пункту не убедил.
L>Все сообщения обрабатывающиеся таким образом должны быть прописаны ЧАСТНЫМ образом в ProcessMessage или на худой конец в WndProc (если ты конечно используешь параметры специфичные для каждого сообщения, а не тупые WPARAM/LPARAM). При расширении библиотеки нужно следить за добавлением обработчиков.
Тут я не понял совсем... Опять в плену мифа о множественности сообщений?
Ты не путай:
есть класс widget — это абстрактное child window.
и есть класс widget_behavior — драйвер специфических сообщений конкретного типа контрола. Это разные сущности.
L>Ошибка в одной букве в имени, или неправильный порядок аргументов при переопределении обработчика — и прощай надёжность. Каждый раз нужно заглядывать в библиотечное определение и проверять, так ли ты написал.
Не понял. Ты про что? Ты вообще С++ имеешь ввиду, кстати?
L>Любое изменение определения обработчика в библиотеке сопровождается геммороем поиска всех обработчиков в клиентском коде. Т.е. "написал — не вздумай больше трогать". Монолитность старого кода — очень хреново. Именно это являлось для меня самым большим минусом , т.к. я сильно дорабатывал библиотеку в течении всего времени эксплуатации.
А это ошибка проектирования и ни что другое.
Никакие templates и пр. эту проблему не решают.
L>CS>Или в извращениях есть некая прэлэсть которой я не вижу? L>Основные плюсы по сравнению с WinLib:
L> L>Гибкость и независимость от библиотеки. При добавлении обработчика нового сообщения, все специфичные для сообщения вещи указываются в специализации сообщения, т.е. шаблона аргумента обработчика (те самые Msg<WM_PAINT>). L>Стандарт записи обработчика в клиентском коде. Не нужно искать и записывать типы и порядок аргументов обработчика. все специфичные для сообщения параметры и методы находятся в одном аргументе. L>Локальность, а в варианте от lboss
(правда над ним ещё подумать надо: магия в коде — это не всегда безопасно ) и атомарность определений обработчиков для конкретного класса. L>
Я уже лет шесть по разными GUI подходами занимаюсь...
И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile
Получил массу удовольствия имплементируя.
Здравствуйте, c-smile, Вы писали:
CS>Первое. Стандартных сообщений не много. десяток. ну может два.
В WTL поддержка 171ого сообщения CS>По этому пункту не убедил.
А я?
CS>Тут я не понял совсем... Опять в плену мифа о множественности сообщений? CS>Ты не путай:
Хм... имелось в виду что в твоей системе придеться в цикле обработки сообщений прописать все сообщения которые ты будеши обрабатывать. А также придется создать механизм обработки не стандартных сообщений. В данной системе все делается едино образно и в цикле обработки сообщений придется только создать обьект WndMsg что делается до смешного тривиально.
CS>есть класс widget — это абстрактное child window. CS>и есть класс widget_behavior — драйвер специфических сообщений конкретного типа контрола. Это разные сущности.
Зачем?
CS>Не понял. Ты про что? Ты вообще С++ имеешь ввиду, кстати?
А теперь посмотри что ты написал в начале...
CS>А это ошибка проектирования и ни что другое. Это рядовая ошибка кодирования CS>Никакие templates и пр. эту проблему не решают.
Еще как решают.
CS>Я уже лет шесть по разными GUI подходами занимаюсь... CS>И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile CS>Получил массу удовольствия имплементируя.
Давай по существу ладно.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
WH>Здравствуйте, c-smile, Вы писали:
WH>В WTL поддержка 171ого сообщения
Про WTL не надо. Это партизанская либа для использования в глыбоком тылу врага.
Под поддержкой ты имеешь ввиду конструкции вида:
MESSAGE_HANDLER(WM_CREATE, OnCreate); Или все ж message crackers?
Или что-то типа SB_SETTEXT? Дык это не сообщение а имя функции.
Java AWT (сакс, но тем не менее) обходится 8 (+/- 2) events. И это правильно.
В том смысле что код обозримый получается.
(дальше поскипано т.к. не по существу :-P )
Давай определимся с терминами:
class window — то что в Win32 называется topmost window.
class widget — то что называется child window.
Очевидно что эти два класса имеют совершенно разные системы событий, например:
window::on(event_activate& evt); и widget::on(event_keyboard& evt);
Понятно что window и widget это абстрактные классы в том смысле что они описывают все окна и все контролы вообще.
События.
Давай договоримся что WM_*BUTTONDOWN, WM_MOUSEMOVE, etc. (всего мышиных кодов 15 шт) это одно событие. В смысле таксономии и общности обработки.
Т.е. вполне возможно (и удобно!) написать widget::on(event_mouse& evt).
Все остальные WM_* события тоже укладываются в такие компактные группы.
Радикально другая ситуация с нотификационными событиями типа:
WM_COMMAND и WM_NOTIFY.
Для их обработки имеет смысл организовывать нечто типа списка listeners (списка функций) в Java.
Потому как ну не станшь же писать свой класс для получения on_some_button_press.
ну и написать обертки типа list_box: public widget дело техники.
Здравствуйте, limax, Вы писали:
L>Здравствуйте, lboss, Вы писали: L>Но я не могу придумать как форсировать вызов конструктора Initializer'а... Пришлось придумывать MsgResult... L>Интересная методика. Осталось разобраться почему она вообще работает. L>... L>Ах-ха! Нашёл. L>
L>А будет оно будет работать с разделением исходника на файлы? L>Куда instance этого самого статического члена шаблона ставить?
??? Это ты про что?! Это просто метот генерации статического шаблонного поля. "с разделением исходника на файлы" — это вообще не связанно... Это аналогично статической функции внутри класса — просто генерится перекрывающийся сегмент — линкер оставляет только один из них...