![]() |
|
Модераторы: Daevaorn |
![]()
|
|
| Jcs |
|
|||
|
Unregistered |
Подскажите, пожалуйста, если кто знает, как связать конкретное сообщение (к примеру WM_CREATE) c нестатической функцией-членом класса. Можно, конечно, использовать и статические, выдирая при этом указатель на конкретный экземпляр класса из описателя окна, и вызывая из него нестатическую функцию, но как-то не охота забивать класс парами функций на каждое сообщение. Заранее спасибо.
|
|||
|
||||
| mr.DUDA |
|
|||
|
3D-маньяк ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 8244 Регистрация: 27.7.2003 Где: город-герой Минск Репутация: 25 Всего: 232 |
Если програмить на MFC или ATL/WTL, то всё просто - там юзается простой макрос типа BEGIN_MESSAGE_MAP/END_MESSAGE_MAP (или DECLARE_MESSAGE_MAP), внутри него связывается любое сообщение с конкретной нестатической функцией. Или ты про чистый WinAPI ?
-------------------- ![]() |
|||
|
||||
| Jcs |
|
|||
|
Unregistered |
Спасибо за ответ, я про WIN API в чистом виде, в самих MFC к месссаджам привязаны нестатические функции, а я сколько ни пытался, даже указатель на ф-цию-член класса (нестатическую) выдрать не могу... Как они так сделали..
|
|||
|
||||
| RAN |
|
||||||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Это вопрос ко мне. Я уже где-то отвечал. Я делаю так, как ты написал:
Но только я не понял, почему две функиции. Функция одна, и она не статическая. Вот пример класса:
Этот код получился "вырезанием" из реального проекта и исправлением кое-чего. Поэтому возможно что-то не так, но принцип такой. Особенности использования: приходиться вызывать для каждого класса функции RegisterClass и UnregisterClass. Это возможно обойти. Но мне показалось, что так лучше. А так можно в конструкторе класса проверять зарегистрирован класс и если нет, то вызывать RegisterClass. А unregisterclass вообще не вызывать. P.S. Кстати, это класс child-окна. |
||||||
|
|||||||
| Jcs |
|
|||
|
Unregistered |
Это действительно работает, только я пытался избежать использования switch в функции обработки окна. Для этого использовал свой класс CMsgMap, в котором каждому конкретному сообщению сопоствляется своя функция обработки.. Может это, конечно, требуется не всем, но мне в проекте надо функцию обработки генерить на лету, в зависимости от состояния приложения. Но похоже все-таки придется использовать статические функции, передавая им указатель на экземпляр класса. Как выяснилось, нестатические тоже его получают, только при помощи компилятора, так что разницы особой не будет. Спасибо за отклик.
|
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Ну, так давай подумаем вместе. Может чего родится интересное. Напиши поподробнее задачу. |
|||
|
||||
| Unregistered |
|
|||
|
Unregistered |
Изначально задача была - каждому сообщению сопоставить функцию, адрес которой пишется в таблицу сообщений. Обработчик окна должен был искать сообщение в таблице и вызывать соответствующую функцию, причем хотелось, чтобы все функции принимали именно то, что им надо, т.е. OnCreate должна только принимать указатель на структуру LPCREATESTRUCT и больше ничего.
Но так, как придется использовать указатель на функцию, то для каждого такого типа функций нужно будет определять прототипы. Поэтому пока у меня все функции-обрабочики сообщений имеют один тип (принимают HWND,WPARAM и LPARAM), причем если они члены какого-либо класса, то все они статические, так как указатель для нестатических функций-членов получить нельзя. В этой реализации не все удобно, но вроде работает. |
|||
|
||||
| Fantasist |
|
||||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
Почему нельзя, все можно. Естесственно, этот указатель не совсем указатель - это скорее смещение относительно экземпляра класса и не имеет смысла без экземпляра класса. Однако ведь тебе не нужно вызывать метод без экземпляра класса, не так ли? Делаешь что-нибудь типа:
Это как идея. В реальности все схема будет слегка другая, но принцип тот же. Это сообщение отредактировал(а) Fantasist - 1.10.2003, 19:26 -------------------- Волны гасят ветер... |
||||
|
|||||
| mr.DUDA |
|
|||
|
3D-маньяк ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 8244 Регистрация: 27.7.2003 Где: город-герой Минск Репутация: 25 Всего: 232 |
[/QUOTE]message_map[WM_MSG1]=&CMsgHandler::HandleMsg;[QUOTE]
Уверен, что можно получить указатель на нестатический компонент-функцию класса ? имхо, закавыка у Jcs именно со статическими членами класса - окна (не хочет он их использовать), а более никак не получить указатель на ф-цию / указатель на this внутри функции оконного класса. Последнее достижимо только с пом. GetWindowLong (как предложил RAN). -------------------- ![]() |
|||
|
||||
| Fantasist |
|
|||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
Именно. Указатель на член класса. Таковой указатель, как я уже упоминал, не имеет смысла без указателя на экземпляр класса. Он сам по себе не является указателем, а является, так сказать "смещением" к члену класса. А вот экземпляр класса мы и храним в WindowLong. Как я понял, проблема была в том, чтобы не делать switch в процедуре обработки окна, а делать динамический мэппинг сообщений к процедурам обработки. Вот примерно таким способом его можно и сделать. -------------------- Волны гасят ветер... |
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Jcs, давай по порядку:
Из твоего топика я понял, что ты хочешь объять необъятное. Тебе нужен универсальные механизм для создания карты сообщений. Причём функции могут быть как членами класса, так и обячными глобальными. Конечно, нет ничего невозможного, но это сложно. Моё предложение тогда такое: в CreateWindow последним параметром передавать либо указатель на экземпляр класса, либо NULL. А в обработчике сообщений анализировать это значение. Если оно NULL, то вызывать _cdecl, иначе _thiscall. Проблема заключается в том, как узнать адрес функции и в том, что если тебе нужно в одном и том же окне совмещать и thiscall и cdecl, то придётся при регистрации функции передавать уазатель на экземпляр класса или NULL. Фуф, ну и намудрил я. Например:
Как можно получить адрес функции - члена класса? class A { public: void func(); }; A::func ? Витруальную можно найти в vtable, но это тоже не хорошо. Потому что в каждом компиляторе этот механизм может реализовываться по своему. (то что в VC и Builder есть различия знаю точно. Сам проверял) |
|||
|
||||
| Jcs |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 156 Регистрация: 1.10.2003 Репутация: нет Всего: нет |
Цена использования нестатических функций в карте сообщений выше, чем получаемые преимущества, поэтому обработчик конкретного мессаджа должен быть статическим. Карта сообщений сейчас являет собой динамически связанный список, хотя как мне кажется в том способе, который предложили fantasist и RAN поиск сообщений будет происходить бытсрее.
|
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Это почему? map - это тоже список. Если тебя интересует скорость, то надо выделить память typedef LRESULT (*MSG_PROC) (HWND , WPARAM ,LPARAM ); MSG_PROC* Map = new MSG_PROC[WM_USER]; Обнулить эту область памяти. И потом добавлять в массив функции обработки сообщений. Это будет самый быстрый map. LRESULT CALLBACK CWnd::Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { MSG_PROC msgproc = Map[uMsg]; return( msgproc ? *msgproc(hwnd, wParam, lParam) : DefWindowProc( hwnd, uMsg, wParam, lParam ) ); } |
|||
|
||||
| Fantasist |
|
||||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
map - это не список. Чаще всего это дерево, оптимизированное для быстрого поиска по ключу. Конечно, самый быстрый способ взятие значения по ключу целого типа - это хранить эти значения в массиве. Единственный недостаток - надо выделить память сразу под весь возможный диапазон ключей. Как алтернатива - hash_map почти то же самое, что массив, но ограниченный.
Я все выше уже написал. Внимательно посмотрите приведенный мною код. Это стардант С++. -------------------- Волны гасят ветер... |
||||
|
|||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Хорошо. А можно динамически получать адрес виртуальной функции? CMsgHandler obj; obj.HandleMsg - адрес функции? |
|||
|
||||
| Fantasist |
|
|||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
Точно так же. В моем примере функции как раз виртуальные. Адрес берется точно так же, просто указатель несет в себе метку о виртуальности, и при вычислении реального адреса функции (в момент использования) это учитывается. -------------------- Волны гасят ветер... |
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Нет, этот адрес получается на этапе компиляции. А я имею ввиду следующую ситуацию:
class base { public: virtual void method() {.....} }; class derived:public base { public: virtual void method() {......} }; ......................... base* obj = new derived; .......................... Вопрос: какой адрес функции obj->method() ? Это будет тоже самое, что и &derived::method, хотя obj - указатель на объект класса base. На этапе компиляции этот адрес не известен. А в runtime его, по ходу, не получить. |
|||
|
||||
| Fantasist |
|
|||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
Еще раз. Указатель на член класса не является указателем в прямом смысле слова. Он несет в себе только информацию, достадочную для нахождения адреса члена класса по предоставленному экземпляру объекта, то есть, условно говоря, смещение. Без этого объекта этот "указатель" не имеет никакого смысла. Адрес виртуальной функции так же вычисляется по предоставленному экземпляру объекта. Почитай у Страуструпа - хорошо описанно. -------------------- Волны гасят ветер... |
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Fantasist, я это всё прекрасно понимаю. Не первый год с ООП.
Страуструпа у меня нет. Искать не хочется, так как проблемы такой передо мной не стоит. Но было бы очень интесно. Может кто знает ответ. Вот и написал. |
|||
|
||||
| Fantasist |
|
||||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
А в чем тогда вопрос? В чем сомнения? И к чему тогда:
? -------------------- Волны гасят ветер... |
||||
|
|||||
| Jcs |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 156 Регистрация: 1.10.2003 Репутация: нет Всего: нет |
Все работает, как и описал Fantasist, причем и для дочерних классов, а смещене дло функции от начала экземпляра одинаковое и у родителя и у потомка. Есть ли у кого-нить электронный Страустрап?
Буду очень признателен, если кто-нибудь вышлет. [email protected] |
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Я получаю экземпляр класса. Знаю его базовый класс. Но он может быть экземпляром класса-наследника базового. Вопрос: как узнать адрес некоторой виртуальной функции? Например для выше написанного примера, хочу написать функции, возвращаюшую адрес виртуальной функции:
class base { public: virtual void method() {.....} }; class derived:public base { public: virtual void method() {......} }; ......................... base* obj = new derived; .......................... void* GetMethodAddr(base* x) { ......................Как тут получить адрес x->Method() ? ............................... } |
|||
|
||||
| Jcs |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 156 Регистрация: 1.10.2003 Репутация: нет Всего: нет |
При наследовании новые члены дочернего класса, если таковые имеются, добавляются в конец класса, поэтому все наследуемые функции, в том числе и виртуальные, имеют одно и то же смещение от начала, поэтому достаточно сделать приведение типов и будет работать. Вот рабочий пример (упрощен):
|
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Не виртуальные функции никуда не добавляются. Они связываются на этапе компиляции. Про смещение можно говорить только, когда имеешь дело с виртуальными функциями. У тебя,Jcs, не совсем верное представление о функционировании наследования. В объекте класса храниться указатель на vtable - виртуальную таблицу. Эта виртуальная таблица одна на все объекты данного класса. А не виртуальные функции туда вовсе не помещаются. И вообще, если это в ответ на мой вопрос, то меня похоже никто не понимает. Жаль. Ну, да ладно. Это так. От любознательности и для общего развития |
|||
|
||||
| Fantasist |
|
|||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
Это точно кто-то кого-то не понимает. -------------------- Волны гасят ветер... |
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
а говоришь не понимаешь. Вот именно это я и хотел услышать. Невозможно, так невозможно. Лезть в vtable конечно можно. Но, во-первых, место vtable в классе не регламентировано (хотя это обычно первое слово), во-вторых, язык,как я понимаю, также не позволяет узнать позицию в vtable адреса виртуальной функции. Короче, спасибо за интересную дискуссию. |
|||
|
||||
| DENNN |
|
|||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 3878 Регистрация: 27.3.2002 Где: Москва Репутация: 1 Всего: 43 |
Я присоединяюсь и предлагаю добавить эту тему в FAQ- вещь вобщем то нужная. |
|||
|
||||
| mr.DUDA |
|
|||
|
3D-маньяк ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 8244 Регистрация: 27.7.2003 Где: город-герой Минск Репутация: 25 Всего: 232 |
ЗЫ, а ещё ведь есть виртуальные базовые классы
Вот вам задачка. Если класс порожден вот так:
то как определить, что лежит в vtable класса D ? -------------------- ![]() |
|||
|
||||
| Fantasist |
|
|||
|
Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: 4 Всего: 41 |
Что значит как определить? Вестимо, там лежит D::M. Или ты ожидаешь что конструкция: A* a=new D(); a->M(); вызовет A::M()? -------------------- Волны гасят ветер... |
|||
|
||||
| mr.DUDA |
|
|||
|
3D-маньяк ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 8244 Регистрация: 27.7.2003 Где: город-герой Минск Репутация: 25 Всего: 232 |
Это я про внутренние потроха vtable, в ответ на:
-------------------- ![]() |
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Хочу ещё поблагодарить за интересную тему. Она мне подсказала интересное решение в моём приложении (по крайней мере, мне нравиться). Суть в следующем:
Есть оконный класс (свой собственный, естественно). Сделан он так, как я описывал выше. Т.е. в классе есть статическая функция, выполняющая роль оконной процедуры. Эта функция получает указатель на объект класса по дескриптору окна (GetWindowLong). Потом вызывается соответствующая сообщению функция. Так вот в окне можно кое-что "перетаскивать" мышкой. Возникла идея написать две разные функции для обработки события WM_MOUSEMOVE. Скажем, OnMouseMove1() и OnMouseMove2(). Ввожу переменную для хранения указателя на текущюю функцию обработки. Меняю значение этой переменной в зависимости от обстоятельств.
Всё работает. Но если не вводить дополнительную переменную MouseProc, то компилятор VC ругается (p->*OnMouseMove)(LOWORD(lParam), HIWORD(lParam)); error C2597: illegal reference to data member 'CWnd::OnMouseMove' in a static member function error C2568: '->*' : unable to resolve function overload В другом месте в не статической функции другая проблема: (*OnMouseMove)(xPos, yPos); error C2171: '*' : illegal on operands of type 'void (__thiscall CWnd::*)(int,int)' error C2064: term does not evaluate to a function Написал (this->*OnMouseMove)(xPos, yPos); и всё OK. Может кто объяснит логику происходящего |
|||
|
||||
| Jcs |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 156 Регистрация: 1.10.2003 Репутация: нет Всего: нет |
В первом случае ты пытаешься из статической фунуции обратиться к нестатическому члену класса, а во втором случае я не уверен, но, как мне кажется, функция должна знать адрес конкретного экземпляра класса, для корректного вызова.
|
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Ну да. И в чём проблема. Я постоянно обращаюсь и нет проблем. А вызвать функцию по указателю - нелегальная ссылка, видите ли. |
|||
|
||||
| RAN |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 709 Регистрация: 14.3.2003 Где: Щёлково Моск.обл. Репутация: 5 Всего: 6 |
Но я и сам понял.
Так и есть Начну со второго примера. Пишу (*OnMouseMove)(xPos, yPos);. VC думает, что я пытаюсь разыменовать переменную типа 'void (__thiscall CWnd::*)(int,int)'. "Нет, - говорит, - нельзя. Без this не можно так делать." Исправляю на (this->*OnMouseMove). "Ага, - говорит VC, - это другое дело. Вызвать можно. this - указатель на CWnd, две переменные типа int в качестве параметров. Всё OK." А в первом случае из-за этого же другая проблема. Надо было писать так: (p->*(p->OnMouseMove))(LOWORD(lParam), HIWORD(lParam)); Так и написал. Всё понял. P.S. Это однозначно надо в FAQ. ВЕЛИК И МОГУЧ ЯЗЫК C++ |
|||
|
||||
![]()
|
| Правила форума "С++:Общие вопросы" | |
|
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |