Интересная идея, я бы еще добавил сортировку не только по номерам сообщений,
но и по кодам — типа как работает 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 этого самого статического члена шаблона ставить?
??? Это ты про что?! Это просто метот генерации статического шаблонного поля. "с разделением исходника на файлы" — это вообще не связанно... Это аналогично статической функции внутри класса — просто генерится перекрывающийся сегмент — линкер оставляет только один из них...
Здравствуйте, lboss, Вы писали:
L>Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, ГоночныйВолк со товарищи!
CS>А вот у меня вопрос возник:
CS>На кой ляд все эти танцы с бубнами?
CS>
WH>Это мелочи... Но имена _* и __* насколько я помню зарезервированы для разработчиков компилятора.
В стандарт смотреть не хочу, т.к. его всё равно у меня нет , но вот из Deep C++ вписанной в MSDN:
The rules differ slightly between the two languages. In C90 and C99, the implementation reserves
any global-scope name beginning with _.
any name beginning with _ followed by an upper-case letter.
any name beginning with __.
In C++, the implementation reserves
any global-scope name beginning with _.
any name beginning with _ followed by an upper-case letter.
any name containing __.
Здравствуйте, c-smile, Вы писали:
CS>Первое. Стандартных сообщений не много. десяток. ну может два.
Во-первых, я ничего не знаю про твою реализацию, поэтому в предыдущем сообщении говорил про реализацию WinLib с relisoft.com, а там сообщений много.
Но это не важно, т.к. я уже сказал — vtable — это мелочь. В шаблонных обработчиках статические инициализаторы карт сообщений тоже немало места скушают, однако здесь размер зависит только от количества обрабатываемых классом сообщений.
L>Все сообщения должны быть прописаны ЧАСТНЫМ образом в ProcessMessage. CS>Тут я не понял совсем... Опять в плену мифа о множественности сообщений?
Все сообщения (не важно, много или мало) нужно взламывать в библиотеке и вызывать конкретные обработчики или, как у тебя, обработчики категорий. Т.е. вся обработка параметров сообщений (т.е. взлом сообщений) намертво зашита в библиотеку. Мне, лично, это не нравится.
А в шаблонном варианте взломщик можно специалировать где угодно и как угодно. Можно даже специализировать по классу окон (например MsgSpec<DummyWindow,WM_PAINT>:Msg<WM_PAINT>), чтобы иметь возможность добавлять (или наоборот убирать) что-то совсем специфическое для конкретного класса, но не в обработчик, а во взломщик.
L>Ошибка в одной букве в имени, или неправильный порядок аргументов при переопределении обработчика — и прощай надёжность. Каждый раз нужно заглядывать в библиотечное определение и проверять, так ли ты написал. CS>Не понял. Ты про что? Ты вообще С++ имеешь ввиду, кстати?
Конечно С++, что же ещё? Или у тебя редактор умеет подсвечивать виртуальные функции, не входящие в базовый класс? (Хотя можно посмотреть в дереве, но всё равно искать). Компилятор не может тебя предупредить, что ты определил новую виртуальную функцию on_mmove(ButtonState,KeyState) или wm_mousemove(KeyState,ButtonState) вместо переопределения wm_mousemove(ButtonState,KeyState). Я именно это имел в виду. Хотя в твоём варианте с on(mouse_event&) эта проблема вроде исчезает, однако, по-моему, появляется другая: при добавлении в дочернем классе одной новой функции on(death_for_all&) компилятор прячет все старые on(...).
L>Монолитность старого кода — очень хреново. CS>А это ошибка проектирования и ни что другое. CS>Никакие templates и пр. эту проблему не решают.
Любой монолитный код, каким бы удобным и хорошо спроектированным он не был по-началу, будет когда-нибудь нуждаться в переписывании. Монолитность — bad, это у некоторых уже в определениях, в том числе и у меня.
CS>Я уже лет шесть по разными GUI подходами занимаюсь...
И хорошо.
А вообще, подводя итог:
Давай закроем тему? Это бесполезный спор. Каждый останется при своём мнении и будет пользовать тем, что ему удобнее. Тем более, что я не хочу яростно защищать ни один из существующих методов обработки сообщений — у каждого своих недостатков предостаточно.
Если есть удобный рабочий вариант с виртуальными обработчиками категорий сообщений, то запости в Исходники. Он будет для кого-нибудь действительно полезнее шаблонных обработчиков.
Здравствуйте, c-smile, Вы писали: CS>Про WTL не надо. Это партизанская либа для использования в глыбоком тылу врага.
У моего друга на работе основные библиотеки ATL и WTL. Очень крупная фирма. Чего ж тут партизанского?
CS>Под поддержкой ты имеешь ввиду конструкции вида: CS>MESSAGE_HANDLER(WM_CREATE, OnCreate);
Это вообще гадость — в каждом обработчике все параметры вручную обрабатывать.
CS>class window — то что в Win32 называется topmost window. CS>class widget — то что называется child window. CS>Очевидно что эти два класса имеют совершенно разные системы событий, например: CS>window::on(event_activate& evt); и widget::on(event_keyboard& evt);
Кхм. А если у меня дочернее окно с заголовком, то это куда? Как в WinRAR окно рас/запаковки, или как почти все docking windows.
Здравствуйте, lboss, Вы писали:
L>"с разделением исходника на файлы" — это вообще не связанно... Это аналогично статической функции внутри класса — просто генерится перекрывающийся сегмент — линкер оставляет только один из них...
Угу, проверил — работает. Просто у меня в памяти остались глюки с такими трюками в старых компиляторах.
Кстати, совсем забыл указать на явную ошибку приводящую к большому leak-у в каждой карте сообщений. std::map::operator[] предназначен не для доступа, а для создания новой пары. Он создаёт новую пару с заданным ключом, и возвращает ссылку на второй элемент в паре. Константного метода не сделали специально, чтобы не было путаницы.
Здравствуйте, c-smile, Вы писали: CS>Я уже лет шесть по разными GUI подходами занимаюсь... CS>И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile CS>Получил массу удовольствия имплементируя.
Кстати, удовольствие удовольствием, но j-smile demo не закрывает процесс при закрытии окна — приходится его вручную убивать. Смотрел на Win2k.
А контролы — приятные, ничего не скажешь.
L>Кстати, совсем забыл указать на явную ошибку приводящую к большому leak-у в каждой карте сообщений. L>std::map::operator[] предназначен не для доступа, а для создания новой пары. Он создаёт новую пару с заданным ключом, и возвращает ссылку на второй элемент в паре. Константного метода не сделали специально, чтобы не было путаницы.
Боюсь и тут ты не прав — да создается новая пара — но(!) она вносится в список — и соответственно во второй раз она и возвращается (и соответственно разрушается в деструкторе) — ни какой утечки... В моём коде плюс — не вызваются лишние конструкторы итераторов, минус — замедляется поиск — так что и так и так правильно — но я подумал что с operator [] будет понятней...
Здравствуйте, lboss, Вы писали:
L>Боюсь и тут ты не прав — да создается новая пара — но(!) она вносится в список — и соответственно во второй раз она и возвращается (и соответственно разрушается в деструкторе) — ни какой утечки... В моём коде плюс — не вызваются лишние конструкторы итераторов, минус — замедляется поиск — так что и так и так правильно — но я подумал что с operator [] будет понятней...
"Не надо ля-ля"
Такую интереснейшую схему неявных карт сообщений привёл, что никому и в голову не приходила, а на таких простых вещах путаешься. в карте сообщений нужно столько записей, сколько есть обработчиков сообщений. Если больше — уже утечка. Ну и что, что она потом "возвращается". Если тебя не устраивает термин "утечка", можно использовать "чрезмерное использование ресурсов" — всё равно плохо.
сообщения окну могут слаться самые разные — никто не гарантирует, что только 200 стандартных. В частности, любой может зарегистрировать именованое сообщение в системе и использовать его для опознавания "свой-чужой" (если не больше). А попробовать цикл с посылкой сообщений от 1 до 1000000000 ты пробовал? Вылет гарантирован.
Кстати, std::map жрёт память намного быстрее Loki::AssocVector-а из-за не-in-place хранения объектов и дополнительных расходов на указатели.
какие ещё "лишние конструкторы итераторов"??? Сравни в асме _map.find() и _map[]. Все эти "лишние конструкторы итераторов" есть и в варианте с "доступом" через _map[], только с довеском в виде создания ненужных пар. К тому же, ты сам говоришь, что "замедляется поиск". А для чего доступ-то требуется? Только для поиска обработчика.
Здравствуйте, limax, Вы писали:
L>Кстати, удовольствие удовольствием, но j-smile demo не закрывает процесс при закрытии окна — приходится его вручную убивать. Смотрел на Win2k.
Бага известная. Концепт кары вообще как правило мотора не имеют .
Здравствуйте, limax, Вы писали:
L>"Не надо ля-ля" L>Такую интереснейшую схему неявных карт сообщений привёл, что никому и в голову не приходила, а на таких простых вещах путаешься.
За похвалу спасибо. За критику тоже, но:
L> L>в карте сообщений нужно столько записей, сколько есть обработчиков сообщений. Если больше — уже утечка. Ну и что, что она потом "возвращается". Если тебя не устраивает термин "утечка", можно использовать "чрезмерное использование ресурсов" — всё равно плохо.
Утечка (Leak) — это здесь — оновное это то что оно рушит программу — так как ресурс расходуется не возвратно...
В данном случае — это не leak!!! Но в любом случае — повторю — я привёл реализацию для более понятного кода...
L>сообщения окну могут слаться самые разные — никто не гарантирует, что только 200 стандартных. В частности, любой может зарегистрировать именованое сообщение в системе и использовать его для опознавания "свой-чужой" (если не больше). А попробовать цикл с посылкой сообщений от 1 до 1000000000 ты пробовал? Вылет гарантирован.
Ох... Запусти и глянь — думаю ты очень удивишься...
Код сгенерён Wizard'ом — я добавил 3 строчки (затаганы <Vadim>):
// Test1.cpp : Defines the entry point for the application.
//#include"stdafx.h"#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:#include <windows.h>
// C RunTime Header Files#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <map>
#include"resource.h"#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text
// Foward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_TEST1, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TEST1);
// Main message loop:while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage is only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_TEST1);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = (LPCSTR)IDC_TEST1;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
//<Vadim>static std::map<UINT, bool> allMsg;
allMsg[message] = true;
_stprintf(szHello, _T("%u"), allMsg.size());
//</Vadim>switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
L>Кстати, std::map жрёт память намного быстрее Loki::AssocVector-а из-за не-in-place хранения объектов и дополнительных расходов на указатели.
В данном случае это не очень важно...
L>какие ещё "лишние конструкторы итераторов"??? Сравни в асме _map.find() и _map[]. Все эти "лишние конструкторы итераторов" есть и в варианте с "доступом" через _map[], только с довеском в виде создания ненужных пар. К тому же, ты сам говоришь, что "замедляется поиск". А для чего доступ-то требуется? Только для поиска обработчика. L>
Это ты привязался к реализации... Map в быстрой реализации работает без итераторов... а find() и end() конструируют и разрушают итераторы (и тоже проводят поиск как и operator[])... Другое дело, что это хорошо оптимизится... Но опять говорю — ты прав — в реальности там проще hash table поюзать... Опять таки в стандартный STL она не входит — поэтому я для наглядности map привел...
Здравствуйте, lboss, Вы писали:
L>Утечка (Leak) — это здесь — оновное это то что оно рушит программу — так как ресурс расходуется не возвратно... L>В данном случае — это не leak!!! Но в любом случае — повторю — я привёл реализацию для более понятного кода...
Давай разбираться. Любая утёкшая память освобождает операционной системой при закрытии программы. До закрытия, утечки могут (и вероятно будут) только накапливаться.
Память выделенная статической карте сообщений будет освобождена только при закрытии программы. До закрытия, размер карты может (и вероятно будет) только увеличиваться.Я не вижу разницы между первым и вторым для пользователя, так какой толк в определении утечек?
Поэтому считаю такие расходы неприемлимыми.
L>Ох... Запусти и глянь — думаю ты очень удивишься...
Ничего удивительного. В большинстве случаев расширения карты будут незначительными, но никто не гарантирует, что они такими и останутся в каждом случае — потенциально карта может расти до объёма всей памяти. Я уже привёл пример новых сообщений. Так зачем писать недетерминированный код?
L>Это ты привязался к реализации... Map в быстрой реализации работает без итераторов... а find() и end() конструируют и разрушают итераторы (и тоже проводят поиск как и operator[])... Другое дело, что это хорошо оптимизится...
Не совсем понял смысл. В какой это быстрой реализации map работает без итераторов?
Вот выдержка из реализация std::map в VC7.1
mapped_type& operator[](const key_type& _Keyval)
{ // find element matching _Keyval or insert with default mapped
iterator _Where = this->lower_bound(_Keyval);
if (_Where == this->end() || this->comp(_Keyval, this->_Key(_Where._Mynode())))
_Where = this->insert(_Where,
value_type(_Keyval, mapped_type()));
return ((*_Where).second);
}
iterator find(const key_type& _Keyval)
{ // find an element in mutable sequence that matches _Keyval
iterator _Where = lower_bound(_Keyval);
return (_Where == end() || this->comp(_Keyval, _Key(_Where._Mynode()))
? end() : _Where);
}
И там, и там, используется итератор+lower_bound
Для сравнения, Loki::AssocVector — аналогично:
// 23.3.1.2 element access:
mapped_type& operator[](const key_type& key)
{ return insert(value_type(key, mapped_type())).first->second; }
// modifiers:
std::pair<iterator, bool> insert(const value_type& val)
{
bool found(true);
iterator i(lower_bound(val.first));
if (i == end() || this->operator()(val.first, i->first))
{
i = Base::insert(i, val);
found = false;
}
return std::make_pair(i, !found);
}
// 23.3.1.3 map operations:
iterator find(const key_type& k)
{
iterator i(lower_bound(k));
if (i != end() && this->operator()(k, i->first))
{
i = end();
}
return i;
}
L>Но опять говорю — ты прав — в реальности там проще hash table поюзать... Опять таки в стандартный STL она не входит — поэтому я для наглядности map привел...
Когда это я про хеш говорил??
Для карты сообщений, отсортированный массив или бинарное дерево подходит как нельзя лучше. Конструируется она один раз, а двоичный поиск гарантирует почти везде одинаковую скорость поиска. Что такое логарифм — так, пшик один. Для карты из 100 сообщений максимум 7 сравнений, для 1000 — 10, для миллиона — 20
Кстати, у меня остался вопрос. В форуме С/С++ его WolfHound задал, но пока никто на него не ответил.
Почему тип возвращаемого значения конструируется заранее, а тип параметров — нет?
Т.е. в RetType<T> func (ParamType<T>) RetType сконструируется, а ParamType — нет.
Это стандартное явление? Как ты на него вообще вышел?
Я тут покумекал — если это стандарт, то открываются довольно богатые возможности использования шаблонов С++, т.к. частично компенсируется отсутствие статических конструкторов. Вот только жаль, что типы параметров не конструируются.
Здравствуйте, limax, Вы писали:
L> L>Любая утёкшая память освобождает операционной системой при закрытии программы. До закрытия, утечки могут (и вероятно будут) только накапливаться. L>Память выделенная статической карте сообщений будет освобождена только при закрытии программы. До закрытия, размер карты может (и вероятно будет) только увеличиваться. L>Я не вижу разницы между первым и вторым для пользователя, так какой толк в определении утечек? L>Поэтому считаю такие расходы неприемлимыми.
Нет — основное отличие leak от неоптимального использования памяти в том, что при leak — программа в конечном итоге съедает всю памать и грохается... Это разные задачи — leak — это серъёзный баг, а неоптимальность — просто завышенные требования к ресурсам...
L><skip/> Так зачем писать недетерминированный код?
Сто раз уже сказал — для наглядности... Я вообще свой hash (он не добавляет элементы если не надо (а-ля lookup в MFC)) юзаю и не жужу...
L>Не совсем понял смысл. В какой это быстрой реализации map работает без итераторов? L>Вот выдержка из реализация std::map в VC7.1
Разачарую тебя, в VC по моему (и не только по моему) самая поганая реализация STL из тех что я видел...
Обычно пишется что-то вроде:
SecondType & operator[] (const KeyType & key)
{
TreePair * p = m_root;
<Поиск по дереву (целиком на указателях)>
<если не найдено - то вставка>
return p->second;
}
iterator find(const KeyType & key)
{
TreePair * p = m_root;
<Поиск по дереву (целиком на указателях)>
return iterator(p);
}
Итератор коструируется толкько в самом конце...
В любом случае — это всё лирика...
L>Но опять говорю — ты прав — в реальности там проще hash table поюзать... Опять таки в стандартный STL она не входит — поэтому я для наглядности map привел...
L>Когда это я про хеш говорил??
Ты прав в том, что это не оптимальный алгоритм...
L>Для карты сообщений, отсортированный массив или бинарное дерево подходит как нельзя лучше. Конструируется она один раз, а двоичный поиск гарантирует почти везде одинаковую скорость поиска. Что такое логарифм — так, пшик один. Для карты из 100 сообщений максимум 7 сравнений, для 1000 — 10, для миллиона — 20
Ох и зачем ты меня логаритмам учищь? Или это буря восторга?... Но всё равно, Hash лучше — тут довольно предсказуемые значения hash-функцию постоить можно классную...
L>Кстати, у меня остался вопрос. В форуме С/С++ его WolfHound задал, но пока никто на него не ответил. L>Почему тип возвращаемого значения конструируется заранее, а тип параметров — нет? L>Т.е. в RetType<T> func (ParamType<T>) RetType сконструируется, а ParamType — нет.
Что значит зарание? Ни чего там зарание не конструируется...
А насчет WolfHound — пусть читает стандарт — шаблоны расскрываются по мере надобности и того что не надо генерить не генерится....
L>Это стандартное явление? Как ты на него вообще вышел?
Я 12 лет уже на C++ программаю
L>Я тут покумекал — если это стандарт, то открываются довольно богатые возможности использования шаблонов С++, т.к. частично компенсируется отсутствие статических конструкторов. Вот только жаль, что типы параметров не конструируются.
Конструируются... Просто в точке вызова — если эту функцию кто-то позовет то в той точке и будет развёрнут шаблон...
Пропускаю про утечки. L>Ох и зачем ты меня логаритмам учищь? Или это буря восторга?...
А как же
L>Но всё равно, Hash лучше — тут довольно предсказуемые значения hash-функцию постоить можно классную...
А примерчик можно? И сколько может потребоваться времени на поиск в худшем случае? Если больше того самого логарифма, то меня уже не устраивает. Насколько больше потребуется памяти по сравнению с отсортированноым массивом-картой?
У меня с хешами опыта маловато (если вообще можно сказать, что есть).
Li>Почему тип возвращаемого значения конструируется заранее, а тип параметров — нет? Li>Т.е. в RetType<T> func (ParamType<T>) RetType сконструируется, а ParamType — нет. L>Что значит зарание? Ни чего там зарание не конструируется... L>шаблоны расскрываются по мере надобности и того что не надо генерить не генерится....
Если так, почему твоя идея с картами сообщений вообще работает? Ведь все эти обработчики никто не вызывает, если не сконструировать типы их возвращяемых значений. Замкнутый круг.
Здравствуйте, limax, Вы писали:
L>Но всё равно, Hash лучше — тут довольно предсказуемые значения hash-функцию постоить можно классную... L>А примерчик можно? И сколько может потребоваться времени на поиск в худшем случае? Если больше того самого логарифма, то меня уже не устраивает. Насколько больше потребуется памяти по сравнению с отсортированноым массивом-картой? L>У меня с хешами опыта маловато (если вообще можно сказать, что есть).
Это лучше книжки почитать — но скажу сразу, что int значения самые удобные и хорошо изученные для hash.
L>Если так, почему твоя идея с картами сообщений вообще работает? Ведь все эти обработчики никто не вызывает, если не сконструировать типы их возвращяемых значений. Замкнутый круг.
смотри:
class A
{
void f(Templ<int> a)
{
}
Templ<int> f1()
{
return Templ<int>();
}
};
Здравствуйте, lboss, Вы писали:
L>>Не совсем понял смысл. В какой это быстрой реализации map работает без итераторов? L>>Вот выдержка из реализация std::map в VC7.1
L>Разачарую тебя, в VC по моему (и не только по моему) самая поганая реализация STL из тех что я видел...
L>Обычно пишется что-то вроде:
L>
L>SecondType & operator[] (const KeyType & key)
L>{
L> TreePair * p = m_root;
L> <Поиск по дереву (целиком на указателях)>
L> <если не найдено - то вставка>
L> return p->second;
L>}
L>iterator find(const KeyType & key)
L>{
L> TreePair * p = m_root;
L> <Поиск по дереву (целиком на указателях)>
L> return iterator(p);
L>}
L>
Итератор — это ведь может быть класс с одним полем, указателем. Соответственно, время работы с ним (конструирование/копирование/уничтожение) вроде не должно отличаться от скорости работы с указателем (компилятор все проинлайнит, т.е реализация с итератором и указателем не будут отличаться). Или это не так?
И еще: Твое утверждение насчет поганной STL у VC обоснованно для VC7.1? Т.е, имеет ли смысл менять STL у VC7.1?
7. О чем невозможно говорить, о том следует молчать.
Здравствуйте, WFrag, Вы писали:
WF>Итератор — это ведь может быть класс с одним полем, указателем. Соответственно, время работы с ним (конструирование/копирование/уничтожение) вроде не должно отличаться от скорости работы с указателем (компилятор все проинлайнит, т.е реализация с итератором и указателем не будут отличаться). Или это не так?
WF>И еще: Твое утверждение насчет поганной STL у VC обоснованно для VC7.1? Т.е, имеет ли смысл менять STL у VC7.1?
Здравствуйте, limax, Вы писали:
L>А зачем всё это? Чем лучше предыдущего варианта? Комментарии?
Поддержка нескольких обработчиков для обного сообщения. Они будут вызваны в порядке перечисления в списке обработчиков. Причем если один из обработчиков сказал что он обработал это сообщение то другие его(сообщение)не получат. Можно еще исключения глушить... но я подумал что это не нужно.
L>Кстати, lboss-овскую неявную карту сообщений прикрутить не захотел? Я использую уже.
Мне показалось что это не очень удобно. Ибо у меня нет жилания в каждом обработчике писать return. Вот если будут статические конструкторы и шаблонные typedef'ы тогда другое дело.
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Поддержка нескольких обработчиков для обного сообщения. Они будут вызваны в порядке перечисления в списке обработчиков. Причем если один из обработчиков сказал что он обработал это сообщение то другие его(сообщение)не получат. Можно еще исключения глушить... но я подумал что это не нужно.
Тоже интересно, только не могу придумать, где это может понадобиться. Всё равно ведь для базового класса пишешь вызов ||Window::ProcessMessage(msg);
А вот overhead карты растёт.
L>>Кстати, lboss-овскую неявную карту сообщений прикрутить не захотел? Я использую уже. WH>Мне показалось что это не очень удобно. Ибо у меня нет жилания в каждом обработчике писать return.
На мой взгляд, возращать везде bool нагляднее, чем устанавливать флажок в Msg.
Кстати, ещё можно и со специализацией MsgOut (MsgResult в оригинале) поизвращаться.
Очень гибкая схема получается. Вот только проблема всех гибкостей — найти применение.
Ха! Задействовав вместо map аналог multimap можно организовать те же цепочки. Хе-хе, причём как! Просто учитывая порядок декларации обработчиков. Интересно, С++ гарантирует конструирование шаблонов возвращяемых значений в порядке декларации методов класса или нет?
MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);
MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);//в каком порядке будут сконструированы MsgOut?
Правда всё равно не вижу, зачем такая цепочка может понадобиться.
Здравствуйте, limax, Вы писали:
L>Тоже интересно, только не могу придумать, где это может понадобиться.
Я тоже но просто тренеруюсь. L>Всё равно ведь для базового класса пишешь вызов ||Window::ProcessMessage(msg); В смысле? Ведь если сообщение перехвачено то в базовый класс оно не пойдет. L>А вот overhead карты растёт.
Да какой там оверхед особенно если учесть что большенство обработчаков будут давольно тяжолыми.
L>На мой взгляд, возращать везде bool нагляднее, чем устанавливать флажок в Msg.
Не флажок надо заменить на метод. L>Кстати, ещё можно и со специализацией MsgOut (MsgResult в оригинале) поизвращаться. L>Очень гибкая схема получается. Вот только проблема всех гибкостей — найти применение.
А чего там можно еще прикрутить?
L>Ха! Задействовав вместо map аналог multimap можно организовать те же цепочки. Хе-хе, причём как!
Ха! С неявной картой не выдет! L>Просто учитывая порядок декларации обработчиков. Интересно, С++ гарантирует конструирование шаблонов возвращяемых значений в порядке декларации методов класса или нет?
Не знаю. L>
L>MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);
L>MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);//в каком порядке будут сконструированы MsgOut?
L>
Даже есль забыть что __COUNTER__ это не стандарт то это всеравно не скомпилится ибо функции различаются только возвращаемым значением.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
L>>Кстати, ещё можно и со специализацией MsgOut (MsgResult в оригинале) поизвращаться. L>>Очень гибкая схема получается. Вот только проблема всех гибкостей — найти применение. WH>А чего там можно еще прикрутить?
Хз. Если б знал — не гадал бы.
WH>Даже есль забыть что __COUNTER__ это не стандарт то это всеравно не скомпилится ибо функции различаются только возвращаемым значением.
Тьфу, забыл. Сморозил.
А так?
Здравствуйте, WolfHound, Вы писали:
WH>Поддержка нескольких обработчиков для обного сообщения. Они будут вызваны в порядке перечисления в списке обработчиков. Причем если один из обработчиков сказал что он обработал это сообщение то другие его(сообщение)не получат. Можно еще исключения глушить... но я подумал что это не нужно.
А зачем несколько обработчиков?! Интересно а как ты BeginPaint откатишь? Не проще ли одну функцию на класс заводить и вызывать из неё несколько своих?
Здравствуйте, limax, Вы писали:
L>Здравствуйте, lboss, Вы писали: L>>>Для OnCommand ещё одну карту создаёшь?
L>>Нет — поюзал виртуальность... L>Каким образом? Поподробнее, если можно.
Просто создал базовый класс с виртуальной функцией — она проосто процессит сообщение и говорит смогла или нет. А в карте сообщений заменил map на std::hash_map<UINT, std::list<VMessageProcessorBase *> >. Ну ещё приоритеты добавил...
Здравствуйте, lboss, Вы писали:
L>Просто создал базовый класс с виртуальной функцией — она проосто процессит сообщение и говорит смогла или нет. А в карте сообщений заменил map на std::hash_map<UINT, std::list<VMessageProcessorBase *> >. Ну ещё приоритеты добавил...
Может выложишь что у тебя получилось, а мы посмотрим.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, lboss, Вы писали:
L>Нет — там целая библиотека...
В смысле? Не имеешь права или боишся что не влезет? Если второе то заархивируй и загрузи на сервер.
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, WolfHound, Вы писали:
WH>Здравствуйте, lboss, Вы писали:
L>>Нет — там целая библиотека... WH>В смысле? Не имеешь права или боишся что не влезет? Если второе то заархивируй и загрузи на сервер.
А зачем это? Просто у меня есть своя библиотека (vlib) там много кода (в том числе и эксперементального) — оформлять и вычищать это нет времени — а вам эта куча мало что даст... Основные принципы работы с сообщениями я уже в флейме указал — так, что зачем выгружать всё библиотеку?!
L>А зачем это? Просто у меня есть своя библиотека (vlib) там много кода (в том числе и эксперементального) — оформлять и вычищать это нет времени — а вам эта куча мало что даст... Основные принципы работы с сообщениями я уже в флейме указал — так, что зачем выгружать всё библиотеку?!
Достаточно пары файлов касающихся окон — не беда если это прямо не будет компилиться,
просто чем больше кода — тем понятнее, конечно у каждого накоплено много своего
кода и обычно в виде *.lib — файла, если ты не хочешь все публиковать — будет интересно
посмотреть часть, касаюэуюся SWL-modified.
Итак в ветке были рассмотрены три основных архитектуры обработки сообщений
1 — явные карты сообщений
2 — неявные карты сообщений
3 — базовый класс с виртуальными функциями-обработчиками
Третья была сразу отброшена как устаревщая и неэффективная.
Есть еще (как минимум) один пропущенный вариант: использования псевдо-виртуальныех функций
template <class T>
// базовый классstruct WndBase {
void msg_handle( UINT id, WPARAM w, LPARAM l ) {
T *p = static_cast<T*>( this );
switch ( id ) {
case WM_PAINT: p->wm_paint(); break;
case WM_MOVE: p->wm_move(LOWORD(l),HIWORD(l)); break;
case WM_CREATE: p->wm_create(*(CREATESTRUCT*)l); break;
// .... все виндовые сообщения
}
}
void wm_paint() {}
void wm_create( CREATESTRUCT&) {}
void wm_move(int x, int y) {}
// .... все виндовые сообщения
};
// производныйstruct MyWnd : public WndBase<MyWnd> {
void wm_move( int x, int y ) {
// ....
}
};
В этой реализации нет таких накладных расходов как при использовании виртуальных функций. Недостатки: необходимость обьявления всех обработчиков в базовом классе и switch в функции msg_handle (слишком длинный получится) Плюсом является то, что отпадает геморой связаный с использованием макросов и написанием завернутых сигнатур сообщений типа MsgResult<MyClass,WM_PAINT> onMsg( Msg<WM_PAINT>& ) и открывается путь к использованию нормальных сигнатур функций обработчиков wm_move(int x, int y).
В этой реализации всю картину портит свитч по всем виндовым сообщениям. От него можно отказатся так как помещение всех обработчиков в базовый класс открывает новые возможности к созданию карты сообщений. В частности можно путем сравнения указателей (&WndBase::wm_paint == &T::wm_paint) определить перегрузил ли пользователь функцию в дочернем классе. Это позволит неявно создавать карты сообщений и вкусить удобство от нормальных сигнатур обработчиков
Честно говоря это первая из реализаций, которая мне пришла в голову, но подумав я её отбросил по причинам:
1. Сообщений много — инстанциирование шаблона будет происходить каждый раз — это сделает процесс компиляции невыносимым...
2. Проблемма наследника от наследника: например диалог наследник окна. Но ведь от диалого тоже кто-то отнаследуется... В общем получается, что весь код — это шаблон (а-ля WTL) — это терпимо на не больших проектах, но становится невыносимым при укреплении проекта... А учитывая п.1 вообще нежизненно... Плюс отсутсвие нормального полиморфизма — иногда очень бесит. В общем стиль на любителя...
С уважением Вадим.
Re: По мотивам SWL
От:
Аноним
Дата:
05.06.03 12:25
Оценка:
Здравствуйте, WolfHound, Вы писали:
А как в шаблон сообщения добавить char* параметр?, почему-то на этапе линковки проблемы возникают
Здравствуйте, yaroslav_v, Вы писали:
L>>А зачем это? Просто у меня есть своя библиотека (vlib) там много кода (в том числе и эксперементального) — оформлять и вычищать это нет времени — а вам эта куча мало что даст... Основные принципы работы с сообщениями я уже в флейме указал — так, что зачем выгружать всё библиотеку?!
_>Достаточно пары файлов касающихся окон — не беда если это прямо не будет компилиться, _>просто чем больше кода — тем понятнее, конечно у каждого накоплено много своего _>кода и обычно в виде *.lib — файла, если ты не хочешь все публиковать — будет интересно _>посмотреть часть, касаюэуюся SWL-modified.
Здравствуйте, lboss, Вы писали:
L>Но я не могу придумать как форсировать вызов конструктора Initializer'а... Пришлось придумывать MsgResult...
Я чегой-от никак не пойму: а какого лешего инстанциируются статические члены MsgResult, а конкретно m_Initializer?
По идее пока соответсвующая OnMsg не вызвана, он не нужен, а вызвана она будет только если ее предварительно зарегистрировать, что делает именно Initializer. Замкнутый круг.
Ничего не понимаю.
Копаясь с этим делом я пришел к такому коду ( этот работает ) :
#include <map>
template < class Param, class Id, Id id >
struct Message
{
Param ¶m_;
Message(Param ¶m) : param_(param) { }
};
template < class Target, class Param, class Id >
struct MessageMap
{
typedef bool (*ProxyFun)(Target&, Param&);
template < Id id >
static bool ConcreteProxyFun(Target &target, Param ¶m)
{
return target.Handle(Message<Param, Id, id>(param));
}
typedef std::map<Id, ProxyFun> Map;
static Map &GetMap()
{
static Map map_;
return map_;
}
template < Id id >
struct Registrar
{
Registrar()
{
static struct ActualRegistrar
{
ActualRegistrar()
{
GetMap()[id] = ConcreteProxyFun<id>;
}
} registrar;
}
};
static bool Handle(Target& target, Id id, Param ¶m)
{
Map::iterator iter = GetMap().find(id);
if(iter == GetMap().end())
return false;
else
return (*iter->second)(target, param);
}
};
Есть ту фсякие косяки, ну да бог с ними, хочется автоматики. А потому пытаемся повторить подвиг lboss'а и... ничего не получается.
Registrar переименовываем в Result, добавляем ему чуть-чуть и первозим его в глобальное пространство имен:
template < class Target, class Param, class Id, Id id >
struct Result
{
bool result_;
Result(bool result) : result_(result)
{
static struct ActualRegistrar
{
ActualRegistrar()
{
MessageMap< Target, Param, Id >::GetMap()[id] = MessageMap< Target, Param, Id >::ConcreteProxyFun<id>;
}
} registrar;
}
operator bool() { return result_; }
};
Только вот Result::ActualRegistrar не инстанциируется. Да и не должен. Это просто я типа у lboss'а подсмотрел, только чего-то недостмотрел Объясните пожалуйста чего я не догоняю?
Здравствуйте, Рома Мик, Вы писали:
РМ>Я чегой-от никак не пойму: а какого лешего инстанциируются статические члены MsgResult, а конкретно m_Initializer?
Мой вариант
А фокус тут в том что когда обработчик возвращает значение компилятор вынужден инстационировать конструктор который тянет за собой registrator. Как и все гениальное просто.
... << RSDN@Home 1.1 alpha 1 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Простите непонятливого.
WH>А фокус тут в том что когда обработчик возвращает значение компилятор вынужден инстационировать конструктор который тянет за собой registrator.
Только если обработчик не зарегистрировать, он не будет вызван, а следовательно не станет возвращать значение, и как следствие registartor может и не быть инстанциирован. И у меня именно так и происходит. Вместо отладки в таком случае неплохо бы, что компилятор генерил отчет о проделанной работе: что он сделал и почему...
Если не сложно, гляньте на мой код: 3.5 кб
Там закомментарены конструкторы Window и Wnd ( файлы main.cpp и window.h ). Если их раскомментарить, то все работает как и должно, а в противном случае — нет. Мне же кажется, что трюк я повторил почти дословно...
Здравствуйте, Рома Мик, Вы писали:
РМ>Если не сложно, гляньте на мой код: 3.5 кб РМ>Там закомментарены конструкторы Window и Wnd ( файлы main.cpp и window.h ). Если их раскомментарить, то все работает как и должно, а в противном случае — нет. Мне же кажется, что трюк я повторил почти дословно...
Частично заработало.
template < class Target, class Param, typename Param::Id id >
struct MsgResult
{
struct Registrar
{
Registrar()
{
MessageMap< Target, Param >::Register<id>();
}
static bool ProxyFun(Target &target, Param ¶m)
{
return target.Handle(Message<Param, id>(param));
}
};
static Registrar registrar_;
bool result_;
MsgResult(bool result) : result_(result)
{
(void)registrar_;
}
operator bool()
{
return result_;
}
};
template < class Target, class Param, typename Param::Id id >
typename MsgResult< Target, Param, id >::Registrar MsgResult< Target, Param, id >::registrar_;
А без выделенного все компилировалось, но regisrar_ не инстанциировался.
Здравствуйте, c-smile, Вы писали:
CS>Я уже лет шесть по разными GUI подходами занимаюсь... CS>И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile CS>Получил массу удовольствия имплементируя.
И где ЭТО сейчас? Напишите пожалуйста.
Re: По мотивам SWL
От:
Аноним
Дата:
25.08.04 11:24
Оценка:
Здравствуйте, WolfHound, Вы писали:
WH>Сейчас наткнулся на SWL
идея там на 5+ но реализация на 3-
Я, кстати, ее малость развил — добавил контролов и немного переделал их
иерархию. Сейчас есть
Здравствуйте, limax, Вы писали:
CS>> Хотя в твоём варианте с on(mouse_event&) эта проблема вроде исчезает, однако, по-моему, появляется другая: при добавлении в дочернем классе одной новой функции on(death_for_all&) компилятор прячет все старые on(...).
Виртуальные ф-ии не спрячешь. Ничто не помешает их переопределять в последующих потомках,
или вызывать внутри либы из базового класса. А учитывая, что, по-хорошему, все эти ф-ии являются protected, так о чем, вообще, речь???
L>Любой монолитный код, каким бы удобным и хорошо спроектированным он не был по-началу, будет когда-нибудь нуждаться в переписывании. Монолитность — bad, это у некоторых уже в определениях, в том числе и у меня.
Кстати, твой подход абсолютно стольк же монолитен, только требует в 10 раз больше монолитного кода
При смене имени классов или сообщений тебе точно так же придется везде все поменять...
L>Давай закроем тему? Это бесполезный спор. Каждый останется при своём мнении и будет пользовать тем, что ему удобнее. Тем более, что я не хочу яростно защищать ни один из существующих методов обработки сообщений — у каждого своих недостатков предостаточно.
В принципе, области применения очевидны.
Если тебуется перехватывать маленькое число сообщений в миниатюрном приложении, то подход с шаблонами рулит,
если же идет "тяжелая артиллерия", с разработкой насыщенных контролов или множества форм прикладного уровня, то вариант с виртуальными ф-иями явно предпочтительней.