Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Системное программирование и WinAPI > Сделать калбэк функцию окна объектной |
Автор: Alexeis 4.4.2013, 11:19 |
Собственно одно решение нашел в исходниках билдера/делфи. Для каждого объекта выделяется блок памяти под функцию и указатель на объект . В область функции зафигачивают машинные коды для перехода по адресу объектного обработчика ну и дополнительно инициализируют this . Может кто-то встречал более переносимое решение чем вставка машинных кодов и правка адресов для перехода? Исходников класса CWnd, так можно было еще там подсмотреть решение. |
Автор: Dem_max 4.4.2013, 11:45 |
Смотря что за велосипед |
Автор: borisbn 4.4.2013, 12:52 |
а чем std:: или boost:: или на худой конец my_bicycle::bind не угодил ? |
Автор: Alexeis 4.4.2013, 14:50 | ||||
Может не совсем точно объяснил. Задача сделать функцию типа
Не статическим членом класса. К примеру взываем функцию
Для создания диалога, а диалог у нас обернут в класс С++ . Очевидно boost::function никак не станет вместо WndProc или DialogProc . Подсмотрел также решение MFC Они решили вопрос так. Создали глобальрный ассоциативный массив СHandleMap, который с каждым хэндлом ассоциирует указатель на класс CWnd* . Функция окна одна на всех, при приходе очередного сообщения функция лезет в СHandleMap, находит правильный экземпляр наследник CWnd* и вызывает его виртуальную функцию обработки оконных сообщений. Это решение не такое быстрое, но универсальное. Мне хотелось как раз найти вот такое решение как привел borisbn. Cразу вспомнилось boost::function, но видимо не провернуть такой трюк никак. Пока что есть только решение от Microsoft. |
Автор: borisbn 4.4.2013, 14:59 | ||
Alexeis, я это делал следующим (да... знаю, что извращённым) способом:
|
Автор: GremlinProg 4.4.2013, 15:01 | ||
Скорее всего, это реализация событий у борланда, с процедурой окна она не имеет ничего общего, просто борланд унифицирует таким образом делегат (этот подход у него как минимум с Delphi 3-4) Это когда указатель на метод склеивается с указателем на объект и представляется универсальным функтором, который можно вызвать как простую функцию в любом контексте. Насколько я помню, у Borland'а существует глобальный указатель, типа pCreateObject, в который записывается указатель на объект, перед вызовом CreateWindow. Первый вызов процедуры окна просто берет этот указатель и пишет в недра свойств окна или WindowLong (не помню точно), при этом, меняет процедуру окна на штатную, которая извлекает указатель из недр и вызывает для него метод WndProc. |
Автор: Alexeis 4.4.2013, 16:05 | ||||||||
А чо, четенькое решенице. Быстрое и довольно честное.
Делегаты у борлонда существуют ввиде структуры из 2х полей. Делегат имеет размер 8 байт и не может быть записан вместо адреса функции и тем более винда не будет вызывать его. Вот собственно привожу весь код создания объектного калбека. Тут правда паскаль, но код такой сиподобный ^. тоже что и -> , знак $ заменяет 0x , Move тоже что и memcpy только аргументы поменяны местами. @ тоже что &
|
Автор: GremlinProg 5.4.2013, 08:17 | ||||||
Alexeis, пытается троллить, забавно. В цитате я об этом и написал ![]() только размер делегата равен не 8 байтам, а равен размеру двух указателей (в x64 это 16 байт)
Тут большинство программисты и в общем случае понимают разницу между Object Pascal и Си ![]() Не советую этим пользоваться! Это бессмысленная "кучамала", первым комментарием уже сказал почему. |
Автор: xvr 5.4.2013, 09:27 |
Не корысти ради, а токмо точности для - У Борланда существуют оба варианта, и делегаты из 2х указателей (которые в С++ у них называются __closure), и эти самые thunk'и, которые позволяют склеить this и метод в один указатель и приделать еще что нибудь. Второе, в частности, когда то использовалось для реализации таблиц виртуальных методов в множественном наследовании. У них thunk'и двигали this Но все это генерил компилятор в compile time, а то, что показал Alexis делает библиотека VCL в run time Пользоваться можно, но только если других методов уже не осталось ![]() |
Автор: Dem_max 5.4.2013, 10:07 |
Я собственно использую __closure в С++ Builder, для того чтобы не делать функцию статической. Для мелкософтовского компилятора это будет __thiscall Но это чисто компиляторозависимый велосипед. |
Автор: GremlinProg 5.4.2013, 10:11 |
а я об этом как раз и писал, для передачи объекта в процедуру окна городить такой огород бессмысленно и неразумно |
Автор: xvr 5.4.2013, 10:33 | ||||
Неа. __closure в Builder'е - это модификатор типа указателя, а __thiscall - это calling conversion для функции. На уровне сорцов это сразу видно - __closure указатели вызываются как обычные функции (я имею в виду синтаксис вызова), а __thiscall как члены класса, т.е. для них явно нужно указать this (или вызывать в контексте метода класса, тогда this компилятор подставит сам)
С этим согласен. Если только GWL_USERDATA не занят уже под что то другое. В VCL Borland не мог воспользоваться GWL_USERDATA для передачи this, т.к. GWL_USERDATA находится в распоряжении пользователя библиотеки VCL, а не её самой. |
Автор: GremlinProg 5.4.2013, 10:43 | ||
Для таких случаев существует как минимум 2 пути: 1. использовать http://msdn.microsoft.com/ru-RU/library/windows/desktop/ms632594(v=vs.85).aspx 2. расширить набор WindowLong (WNDCLASS::cbWndExtra) при регистрации класса (обычный подход)
Для расширения класса (например, для второго варианта) у Borland'а, насколько помню, всегда имелся метод CreateParams, который можно было переписать на свой вкус. |
Автор: borisbn 5.4.2013, 13:05 | ||
дык для этого ж они свойство Tag предоставляют |
Автор: Alexeis 5.4.2013, 23:20 | ||||
Неа, ты писал
Что есть описание __closure . Поэтому я и уточнил, что оконная функция реализуется через генерацию машинного кода перехода. В свое время делая сабклассинг я был удивлен, что у разных экземпляров одного класса отличаются адреса функций окна. То, что указатель на функцию член сохраняется внутри через __closure фактор не существенный, основной замес именно в подмене функции окна. |
Автор: GremlinProg 6.4.2013, 08:56 |
Alexeis, ты можешь, конечно называть апельсин помидором, но суть от этого не поменяется, цитируя ты повторил то же самое, о чем я писал. О том, как делал процедуру окна борланд в Delphi я тоже http://forum.vingrad.ru/index.php?showtopic=365615&view=findpost&p=2557430 (последний абзац). Поясняю: он это делал аналогично тому, как предложил borisbn, только борланд вместо installWindowProc просто регистрировал начальную процедуру окна(InitWindowProc), которая делала инициализацию окна и подставляла на свое место штатную StdWindowProc. Я просто это все пишу по памяти, т.к. уже много лет не работаю с продуктами от борланд. |
Автор: mes 6.4.2013, 16:24 |
вариантов прикрутить не много.. пару уже назвали, это хранение this в отведенной области, и генерация машиного перехода... остался маппинг hwnd на this и заготовка достаточного объема статических функций со связанным аргументом.. У каждого из подходов есть свои недостатки.. Иногда бывает полезным смешать разные подходы.. |
Автор: Alexeis 6.4.2013, 17:15 | ||
mes, мне кажется самым экономным будет использование
Ммм...? А зачем иметь много статических функций? Разве недостаточно одной функции у общего базового класса? Функцию-член обработки сообщений окна делаем виртуальной и соответственно для каждого производного класса можно перекрывать обработку сообщений. |
Автор: volatile 7.4.2013, 00:42 |
borisbn, привел хороший способ. Благо в WinApi, во многих местах присутствует что-то типа void *userdata имхо самое место для указателя на класс. Способ борланда, конечно некрасив, но им же не надо было думать о кроссплатформенности, и прочих табу ... наложенных на С++ прикладников. Зато их способ, чуток должен быть эффективней. А вызов функций, это именно то место, где не помешает подумать об эффективности. Так что, их способ, тоже имеет право на существование. и их можно понять. С маппингом, думаю, будет совсем не эффективно. короче, голосую за способ borisbn, |
Автор: mes 7.4.2013, 00:50 | ||
это два разных варианта ) маппинг hwnd и "маппинг" оконной функции )) Добавлено через 4 минуты и 43 секунды прям уж совсем ? на 1000 окон около 10 "лишних сравнений"... |
Автор: volatile 7.4.2013, 01:14 |
10 сравнений чтобы вызвать одну функцию, думаю что да, совсем не эффективно. ![]() |