Re[9]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 19:35
Оценка: 10 (1)
Здравствуйте Yarik, вы писали:

Y>НО: Если не затруднит намылить, как же всё-таки брать такой указатель на член класса?


class CA
{
public:
int A()
{ return 1; }
protected:
int m_a;

};

class CB
{
public:
int B()
{ return 2; }
protected:
int m_b;

};

class CC : public CA, public CB
{
public:
int C()
{ return 3; }
protected:
int m_c;
};

// так объявляется указатель на член класса
typedef int (CC::*CPtr)();


int
_tmain(
int argc,
_TCHAR * argv[]
)
{
// это к вопросу о размере указателя
printf("sizeof(CPtr) = %d\n", sizeof(CPtr));

// так инициализируется указатель на член класса
CPtr p = CC::A;

CC c;

// a так вызывается функция по указателю
return (c.*p)();
}
-- Alex Fedotov
Адрес метода
От: Vovchik  
Дата: 31.05.01 07:10
Оценка:
К куче существующих окон необходимо заменить WindowProc.
Первое, что пришло в голову, создать класс CMyWnd,
содержащий метод MyWndowProc и атрибут OldWindwProc.
Одна универсальная функция неэффективна, т.к. единственное
различие для вызовов из разных окон это значение hWnd?
а по нему искать каждый раз OldWindwProc не хочется.

Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?
Вопрос 2: Как иначе организовать множество функций различающихся только
значением атрибута OldWindwProc.
Re: Адрес метода
От: Willi  
Дата: 31.05.01 08:19
Оценка:
Здравствуйте Vovchik, вы писали:

V>К куче существующих окон необходимо заменить WindowProc.

V>Первое, что пришло в голову, создать класс CMyWnd,
V>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>Одна универсальная функция неэффективна, т.к. единственное
V>различие для вызовов из разных окон это значение hWnd?
V>а по нему искать каждый раз OldWindwProc не хочется.

V>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?

V>Вопрос 2: Как иначе организовать множество функций различающихся только
V> значением атрибута OldWindwProc.

а если попробовать так

oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC);
::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
\/\/i||i
Re[2]: Адрес метода
От: Willi  
Дата: 31.05.01 08:34
Оценка:
Здравствуйте Willi, вы писали:

W>Здравствуйте Vovchik, вы писали:


V>>К куче существующих окон необходимо заменить WindowProc.

V>>Первое, что пришло в голову, создать класс CMyWnd,
V>>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>>Одна универсальная функция неэффективна, т.к. единственное
V>>различие для вызовов из разных окон это значение hWnd?
V>>а по нему искать каждый раз OldWindwProc не хочется.

V>>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?

V>>Вопрос 2: Как иначе организовать множество функций различающихся только
V>> значением атрибута OldWindwProc.

W>а если попробовать так


W>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC);

W>::SetWindowLong(hWnd, GWL_WNDPROC, newProc);

Пардон не дочитал вопрос до конца.

Функция для SetWindowLong не может быть обычным методом класса.
Она должна быть либо глобыльной, либо static member,
проскольку обычные методы кроме указанных параметров
принимают неявный this

Дело в том что процедура может обслуживать не одно окно
а несколько, соответственно у нескольких hWnd может быть
один и тотже GWL_WNDPROC. Если не заморачиваться на этом,
то тебе достаточно иметь соответствие экземпяр класса <-> hWnd
И когда в твою новую процедуру свалится сообщение, отыскивать
нужный клас по пришедшему hWnd
\/\/i||i
Re[2]: Адрес метода
От: Vovchik  
Дата: 31.05.01 08:34
Оценка:
Здравствуйте Willi, вы писали:

W>а если попробовать так


W>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC);

W>::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
В том-то весь и прикол, что newProc — если это
глобальная функция то все ОКей, а если это метод
какого-либо класса то C++ не возвращает указатель
на функцию.
Говорит:
cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'
Re[3]: Адрес метода
От: Willi  
Дата: 31.05.01 08:37
Оценка:
Здравствуйте Vovchik, вы писали:

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


W>>а если попробовать так


W>>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC);

W>>::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
V>В том-то весь и прикол, что newProc — если это
V>глобальная функция то все ОКей, а если это метод
V>какого-либо класса то C++ не возвращает указатель
V>на функцию.
V>Говорит:
V>cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'

см. выше
\/\/i||i
Re[3]: Адрес метода
От: Vovchik  
Дата: 31.05.01 08:40
Оценка:
Здравствуйте Willi, вы писали:

W>то тебе достаточно иметь соответствие экземпяр класса <-> hWnd

W>И когда в твою новую процедуру свалится сообщение, отыскивать
W>нужный клас по пришедшему hWnd

Я не совсем тебя понял, если ты имел в виду static функцию, то
она не имеет точно также доступа к не-static атрибутам :(
Или ты имел в виду что-то типа создания функции FindOldWindowProc (HWND hWnd) ?
Re[4]: Адрес метода
От: Willi  
Дата: 31.05.01 08:55
Оценка:
Здравствуйте Vovchik, вы писали:

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


W>>то тебе достаточно иметь соответствие экземпяр класса <-> hWnd

W>>И когда в твою новую процедуру свалится сообщение, отыскивать
W>>нужный клас по пришедшему hWnd

V>Я не совсем тебя понял, если ты имел в виду static функцию, то

V>она не имеет точно также доступа к не-static атрибутам :(
совершенно верно, зато не принимает this :)

V>Или ты имел в виду что-то типа создания функции FindOldWindowProc (HWND hWnd) ?

что-то вроде того

Заводишь глобальную коллекцию
После подмены процедуры сохраняешь туда либибо старый адрес,
тогда твой класс тебе вообще не нужен, либо, если класс все-таки нужен
сохраняешь туда указатель на экземпляр класса.

А в своей оконной процедуре достаешь из коллекцию нужную тебе информацию
\/\/i||i
Re[3]: Адрес метода
От: Vitek  
Дата: 31.05.01 09:00
Оценка:
Здравствуйте Vovchik, вы писали:

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


W>>а если попробовать так


W>>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC);

W>>::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
V>В том-то весь и прикол, что newProc — если это
V>глобальная функция то все ОКей, а если это метод
V>какого-либо класса то C++ не возвращает указатель
V>на функцию.
V>Говорит:
V>cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'

Ясен пень.
Указатель на метод класса реально состоит из 8-ми байт.
Не буду объяснять почему! Тут всё логично!

И вообще как бы ты хотел, чтоб вызвалась твоя функция-член класса?
Кто ей this-указатель будет передавать?

Реально запускается общая для всех классов ф-я, в которой и происходит
нечто похожее на FindInstanceByHandle(HWND);

А далее запускается нужная функция.
Так что прийдётся тебе реализовать ф-ю подобную описанной
или ...

Коварный метод — засунуть указатель на объект класса куды-нить
вместо например ID (если он конэшна не используется).
Работает быстро, но если объект задействут его — считай
"Программа выполнила..."

Ну и, естественно, чтобы вызвать старый обработчик — метод класса
ты должен знать объект какого класса вызвал твой обработчик.

Либо, как в билдере, все они должны наследоваться от одного класса
и обработчик должен быть виртуальной функцией.
Тогда просто вызываешь обработчик самого верхнего класса.
Реально запустится тот, что тебе нужен (объяснил как умел — а
вообще читай про наследование и виртуальность).
В билдере ((TControl*)unknown_control)->WindowProc;

Что не понял — спрашивай.
Re: Адрес метода
От: nickp  
Дата: 31.05.01 11:24
Оценка:
Здравствуйте Vovchik, вы писали:

V>К куче существующих окон необходимо заменить WindowProc.

V>Первое, что пришло в голову, создать класс CMyWnd,
V>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>Одна универсальная функция неэффективна, т.к. единственное
V>различие для вызовов из разных окон это значение hWnd?
V>а по нему искать каждый раз OldWindwProc не хочется.

Если тебе надо только узнать значение OldWindowProc,
то почему-бы не сохранить его используя
SetWindowLong(...,GWL_USERDATA,...)
Re[4]: Адрес метода
От: Vovchik  
Дата: 31.05.01 11:30
Оценка:
Здравствуйте Vitek, вы писали:

V>Либо, как в билдере, все они должны наследоваться от одного класса

IMHO, это не возможно, т.к. окно уже существует и это не мое окно.

Но проблема решена:
CWnd::SubclassWindow( HWND hWnd) — захватывает существующее окно
и обрабатывает сообщения переписанной WindowProc

Всем кто откликнулся большое спасибо.
Re[4]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 06:08
Оценка:
Здравствуйте Vitek, вы писали:

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


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


W>>>а если попробовать так


W>>>oldProc = ::GetWindowLong(hWnd, GWL_WNDPROC);

W>>>::SetWindowLong(hWnd, GWL_WNDPROC, newProc);
V>>В том-то весь и прикол, что newProc — если это
V>>глобальная функция то все ОКей, а если это метод
V>>какого-либо класса то C++ не возвращает указатель
V>>на функцию.
V>>Говорит:
V>>cannot convert from 'void *(__thiscall CMyWnd::*)(void)' to 'void *'

V>Ясен пень.

V>Указатель на метод класса реально состоит из 8-ми байт.
V>Не буду объяснять почему! Тут всё логично!

Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!
Re: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 06:30
Оценка:
Здравствуйте Vovchik, вы писали:

V>К куче существующих окон необходимо заменить WindowProc.

V>Первое, что пришло в голову, создать класс CMyWnd,
V>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>Одна универсальная функция неэффективна, т.к. единственное
V>различие для вызовов из разных окон это значение hWnd?
V>а по нему искать каждый раз OldWindwProc не хочется.

V>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?

V>Вопрос 2: Как иначе организовать множество функций различающихся только
V> значением атрибута OldWindwProc.

Я делал так:

//////////////////////////////////////////////////////////////////////

class CButton
{
friend LRESULT CALLBACK __ButtonProc__ (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

public:
// ... всякие там методы
public:
// Windowing
HWND hWnd; // для удобства (или для проверки правильности соответсвия экземпляра окну)
HWND Create (HINSTANCE hInstance, HWND hWndParent);

//Message handling
WNDPROC fnDefButtonProc;
LRESULT OnWndMessage (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
}

//...............................................

//////////////////////////////////////////////////////////////////////

HWND CButton::Create (HINSTANCE hInstance, HWND hWndParent)
{
hWnd = CreateWindowEx (0,"BUTTON", NULL,
WS_CHILD /*| WS_VISIBLE*/ | BS_FLAT | BS_NOTIFY,
4,24,48,18,
hWndParent, NULL, hInstance, NULL);
assert (hWnd != NULL);

fnDefButtonProc = (WNDPROC) SetWindowLongA (hWnd, GWL_WNDPROC, (LONG) __ButtonProc__);
SetWindowLong (hWnd, GWL_USERDATA, (LONG) this);

return hWnd;
}

//////////////////////////////////////////////////////////////////////

LRESULT CButton::OnWndMessage (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// ............
}
return CallWindowProc (fnDefButtonProc, hWnd, uMsg, wParam, lParam);
}


//////////////////////////////////////////////////////////////////////

LRESULT CALLBACK __ButtonProc__ (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CButton *btn = (CButton*) GetWindowLong (hWnd, GWL_USERDATA);
assert (btn != NULL);
return btn->OnWndMessage (hWnd, uMsg, wParam, lParam);
}

//////////////////////////////////////////////////////////////////////

В случае с собственным классом окна можно пойти двуме путями:
1. Зарегистрировать класс с процедурой DefWindowProc, а потом см. выше
2. Пердавать this в последнем параметре CreateWindowEx:

hWnd = CreateWindowEx (WS_EX_TOOLWINDOW,
"MBSAppWClass",
"Multi Block Storage",
WS_POPUP|WS_DLGFRAME,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
65,
NULL,
NULL,
(HINSTANCE) hInstance,
this); // <---

В этом случае та глобальная процедура, которую ты указал при регистрации класса, должна иметь вид:

LRESULT CALLBACK
__MainWndProcCaller__ (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static CYourClass* YourClass;
if (uMsg == WM_CREATE)
{
CREATESTRUCT *cs = (CREATESTRUCT*) lParam;
YourClass = (CYourClass*) cs->lpCreateParams;
assert (Engine != NULL);
return 1;
}
return YourClass->OnWndMessage (hWnd, uMsg, wParam, lParam);
}

Для диалога это будет выглядеть немного по-другому:


void COptions::BringDialog(HWND hWndParent, PBYTE pbLocalSpacingType)
{
m_pbLocalSpacingType = pbLocalSpacingType;
DialogBoxParam (
(HINSTANCE) GetWindowLong (hWndParent, GWL_HINSTANCE),
MAKEINTRESOURCE (IDD_OPTIONS),
hWndParent,
__OptionsDlgProcCaller__,
(LPARAM) this);
}

//////////////////////////////////////////////////////////////////////

BOOL CALLBACK
__OptionsDlgProcCaller__ (
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static COptions* This = false;
if (uMsg == WM_INITDIALOG)
This = (COptions*) lParam;
if (This)
return This->OnDlgMessage (hDlg, uMsg, wParam, lParam);
else
return FALSE;
}
Re[5]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 07:13
Оценка:
Здравствуйте Yarik, вы писали:

V>>Ясен пень.

V>>Указатель на метод класса реально состоит из 8-ми байт.
V>>Не буду объяснять почему! Тут всё логично!

Y>Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!


А sizeof слабо сделать? (А винда уже есть, правда не у всех).
-- Alex Fedotov
Re[6]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 12:35
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Здравствуйте Yarik, вы писали:


V>>>Ясен пень.

V>>>Указатель на метод класса реально состоит из 8-ми байт.
V>>>Не буду объяснять почему! Тут всё логично!

Y>>Не ври! С каких это пор указатели стали 64-битными? Еще даже винды таковой нету!


AF>А sizeof слабо сделать? (А винда уже есть, правда не у всех).


А ты влепи #pragma pack (1) и посмотри, что получится!!!
Поинтеры на наших PC были, есть и будут 32-битными. Потому что процы у нас такие.
Вот войдет в жизнь (нашу) Пень4 (tm), тогда и говори, что this размером 8 байт.
Re[7]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 14:37
Оценка:
Здравствуйте Yarik, вы писали:

AF>>А sizeof слабо сделать? (А винда уже есть, правда не у всех).


Y>А ты влепи #pragma pack (1) и посмотри, что получится!!!

Y>Поинтеры на наших PC были, есть и будут 32-битными. Потому что процы у нас такие.
Y>Вот войдет в жизнь (нашу) Пень4 (tm), тогда и говори, что this размером 8 байт.

Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита.
Но указатель на функцию-член класса действительно 8 байт (собственно
указатель + смещение от this), что проверяется простым sizeof и от
#pragma pack это не зависит.

BTW, P4 — 32-разрядный, это ты видно с Itanium путаешь.
-- Alex Fedotov
Re[8]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 16.07.01 15:17
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Здравствуйте Yarik, вы писали:


AF>Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита.

AF>Но указатель на функцию-член класса действительно 8 байт (собственно
AF>указатель + смещение от this), что проверяется простым sizeof и от
AF>#pragma pack это не зависит.

Вот это уже на правду похоже. А ещё похоже, что мы с вами спорим каждый о своём.
О чем глубоко сожалею. :-(
Зато как интересно было :-)

AF>BTW, P4 — 32-разрядный, это ты видно с Itanium путаешь.

Да, ты прав. Но это уже не имеет значения. :-)

НО: Если не затруднит намылить, как же всё-таки брать такой указатель на член класса?
Re[8]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.07.01 20:03
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита.

AF>Но указатель на функцию-член класса действительно 8 байт (собственно
AF>указатель + смещение от this), что проверяется простым sizeof и от
AF>#pragma pack это не зависит.

Все почти верно, но не "указатель + смещение от this", а указатель + this. Указатель на функцию это простой указатель, а this нужен для пердачи в качестве скрытого параметра.

Кстати, а ни кто не пробывал создать унивирсальный указатель на функцию любого класса. На asm-е я такие штуки прокатывал, а как на Плюсах?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.07.01 20:06
Оценка:
Здравствуйте Yarik, вы писали:

Y> hWnd = CreateWindowEx (0,"BUTTON", NULL,

Y> WS_CHILD /*| WS_VISIBLE*/ | BS_FLAT | BS_NOTIFY,
Y> 4,24,48,18,
Y> hWndParent, NULL, hInstance, NULL);
Y> assert (hWnd != NULL);
Y>
Y> fnDefButtonProc = (WNDPROC) SetWindowLongA (hWnd, GWL_WNDPROC, (LONG) __ButtonProc__);
Y> SetWindowLong (hWnd, GWL_USERDATA, (LONG) this);

Интересно! А что произойдет если в GWL_USERDATA окна к которому Вы подключаетесь будет лежать какая то информация?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Адрес метода
От: Alex Fedotov США  
Дата: 16.07.01 21:12
Оценка:
Здравствуйте VladD2, вы писали:

VD>Все почти верно, но не "указатель + смещение от this", а указатель + this. Указатель на функцию это простой указатель, а this нужен для пердачи в качестве скрытого параметра.


Нет, именно смещение, которое надо прибавить к this во время вызова. This ведь не известен
до момента вызова и к собственно указателю отношения не имеет:

CPtr p = CC::A; // при чем тут this?

Тем не менее размер CPtr — 8 байт, поскольку включает адрес функции CA::A и то самое
смещение (которое в данном случае равно нулю). А вот если бы было

CPtr p = CC::B;

то смещение было бы уже не ноль, а 4 (при выравнивании структур на 4 байта).

Продолжение моего предыдущего примера:

int callme(CPtr p)
{
CC c;
return (c.*p)();
}

CPtr p = CC::B;
return callme(p);

; вот какой код компилятор генерирует для вызова метода
push 4
mov eax, offset CB::B
pop ecx
push ecx ; обрати внимание, в стек идут 8 байт — адрес метода
push eax ; и пресловутое смещение
call callme

; а вот так выглядит реализация функции callme в его исполнении
callme:
push ebp
mov ebp, esp
sub esp, 0Ch
mov eax, dword ptr [ebp + 0Ch] ; это смещение
lea ecx, [ebp + eax — 0Ch] ; вот оно прибавляется к адресу объекта и передается в
; качестве скрытого параметра
call dword ptr [ebp + 08h]
leave
ret
-- Alex Fedotov
Re[8]: Адрес метода
От: Alexander Shargin Россия RSDN.ru
Дата: 17.07.01 15:46
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Еще раз, медленно. Никто не говорит, что любой указатель на x86 64 бита.

AF>Но указатель на функцию-член класса действительно 8 байт (собственно
AF>указатель + смещение от this), что проверяется простым sizeof и от
AF>#pragma pack это не зависит.

Мне кажется, нужно всё-таки внести окончательную ясность по данному вопросу. Иначе эта дискуссия с цитированием ассемблерных листингов собьёт с толку кого угодно. :)

В большинстве случаев указатель на невиртуальную функцию-член всё-таки занимает 4 байта — только адрес функции. Дополнительные 4 байта — смещение — возникают только при множественном наследовании. При обычном наследовании смещение не нужно, так оно всегда равнялось бы нулю, и мы просто без нужды тратили бы память.

Поясню, зачем вообще понадобилось это смещение. Дело в том, что переменные-члены классов, образующих цепочку наследования, располагаются в памяти последовательно: сначала — самого базового класса, затем его непосредственного потомка и т. д. Например:

class A
{
int m_a;
...
};

class B : A;
{
int m_b;
...
};

class C : B;
{
int m_c;
...
};

Объект класса C:
*-----------------* <-this
* Данные класса A *
*-----------------*
* Данные класса B *
*-----------------*
* Данные класса C *
*-----------------*

Любая функция в классах A, B или C точно "знает" адреса членов класса, с которыми работает:
m_a: this+0
m_b: this+4
m_c: this+8

Поэтому любой из этих функций можно смело передавать this, не прибавляя к нему смещение.


Теперь рассмотрим случай из примера Алекса Федотова.

class A
{
int m_a;
...
};

class B;
{
int m_b;
...
};

class C : A, B;
{
int m_c;
...
};

Объект класса C выглядит так же, как и раньше:
*-----------------* <-this
* Данные класса A *
*-----------------*
* Данные класса B *
*-----------------*
* Данные класса C *
*-----------------*

Но теперь функции класса B, которые понятия не имеют о существовании класса C, используют для доступа к члену m_b адрес this+0. Если вызвать функцию класса B для класса C, он будет работать с членом A::m_a по адресу this+0, как будто это B::m_b. Вот почему для обеспечения корректности программы приходится прибавлять смещение к указателю this при использовании функций класса B для объекта C. Поэтому в указателе на функцию для класса C появляется дополнительное поле — смещение. Для любых функций из классов A и C это смещение будет равно нулю, для функций класса B — четырём.



С уважением,
Александр
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[9]: Адрес метода
От: Alex Fedotov США  
Дата: 17.07.01 16:54
Оценка:
Здравствуйте Alexander Shargin, вы писали:

AS>Мне кажется, нужно всё-таки внести окончательную ясность по данному вопросу. Иначе эта дискуссия с цитированием ассемблерных листингов собьёт с толку кого угодно. :)


Какой же вы политкорректный все-таки, даже не обвинили меня в подтасовке фактов, хотя на самом деле до этого было недалеко, поскольку пример я подбирал специально :)

AS>В большинстве случаев указатель на невиртуальную функцию-член всё-таки занимает 4 байта — только адрес функции. Дополнительные 4 байта — смещение — возникают только при множественном наследовании. При обычном наследовании смещение не нужно, так оно всегда равнялось бы нулю, и мы просто без нужды тратили бы память.


С другой стороны, можно сказать, что в общем случае множественного наследования указатель занимает 8 байт, а в частном случае одиночного наследования достаточно четырех. Это я не согласен с фразой "в большинстве случаев".

AS>Поясню, зачем вообще понадобилось это смещение.


Это разъяснение достойно помещения в FAQ. Можно я его сохраню для цитирования?
-- Alex Fedotov
Re[9]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.07.01 20:41
Оценка:
Здравствуйте Alexander Shargin:

У меня закралось подозрение, что что-то здесь не так. А нельзя ли гипотетический пример привести в практический, да так чтобы в консольку выводился sizeof(...).

Вот я тут кинул примерчик (http://www.rsdn.ru/forum/?action=message&amp;gid=9&amp;mid=3166
Автор: VladD2
Дата: 18.07.01
). Что нужно сделать, чтобы он сработал не верно?

Вот после перевода данных дебатов в практическую плоскость из них не то, что FAQ, а даже статью сделать можно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Адрес метода
От: Alex Fedotov США  
Дата: 17.07.01 21:04
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте Alexander Shargin:


VD>У меня закралось подозрение, что что-то здесь не так. А нельзя ли гипотетический пример привести в практический, да так чтобы в консольку выводился sizeof(...).


http://rsdn.ru/forum/?action=message&amp;gid=9&amp;mid=3097
Автор: Alex Fedotov
Дата: 16.07.01

(я его компилировал и запускал, так что пример вполне практический)

VD>Вот я тут кинул примерчик (http://www.rsdn.ru/forum/?action=message&amp;gid=9&amp;mid=3166
Автор: VladD2
Дата: 18.07.01
).


Ну вы и извращенец, батенька :). Такими примерами можно запросто расшатать неокрепшую психику начинающих программистов.

VD> Что нужно сделать, чтобы он сработал не верно?


Это слегка модифицированный мой пример.

class CA
{
public:
CA() { m_a = 1; }
int A() { return т_a; }
protected:
int m_a;
};

class CB
{
public:
CB { m_b = 2;
int B() { return m_b; }
protected:
int m_b;

};

class CC : public CA, public CB
{
public:
CC { m_c = 3;
int C() { return 3; }
protected:
int m_c;
};

typedef int (CC::*CPtr)();

int
_tmain(
int argc,
_TCHAR * argv[]
)
{
printf("sizeof(CPtr) = %d\n", sizeof(CPtr));

CPtr p = CC::B;
CC c;

printf("c.B() returns %d", (c.*p)());
return 0;
}

Пожалуйста, замени (c.*p)() вызовом через свой универсальный указатель.

По-моему, Алескандр все очень наглядно объяснил и должно быть понятно, почему это не будет работать.
-- Alex Fedotov
Re[3]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 18.07.01 08:10
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте Yarik, вы писали:


Y>> hWnd = CreateWindowEx (0,"BUTTON", NULL,

Y>> WS_CHILD /*| WS_VISIBLE*/ | BS_FLAT | BS_NOTIFY,
Y>> 4,24,48,18,
Y>> hWndParent, NULL, hInstance, NULL);
Y>> assert (hWnd != NULL);
Y>>
Y>> fnDefButtonProc = (WNDPROC) SetWindowLongA (hWnd, GWL_WNDPROC, (LONG) __ButtonProc__);
Y>> SetWindowLong (hWnd, GWL_USERDATA, (LONG) this);

VD>Интересно! А что произойдет если в GWL_USERDATA окна к которому Вы подключаетесь будет лежать какая то информация?


А что там может лежать, если окно создаю я ?! На то оно и USERDADA, чтобы USER мог ложить туда свои DATA.
Re[9]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 18.07.01 08:54
Оценка:
Здравствуйте Alexander Shargin, вы писали:

AS>В большинстве случаев указатель на невиртуальную функцию-член всё-таки занимает 4 байта — только адрес функции. Дополнительные 4 байта — смещение — возникают только при множественном наследовании. При обычном наследовании смещение не нужно, так оно всегда равнялось бы нулю, и мы просто без нужды тратили бы память.


AS>Поясню, зачем вообще понадобилось это смещение. Дело в том, что переменные-члены классов, образующих цепочку наследования, располагаются в памяти последовательно: сначала — самого базового класса, затем его непосредственного потомка и т. д. Например:


AS>Но теперь функции класса B, которые понятия не имеют о существовании класса C, используют для доступа к члену m_b адрес this+0. Если вызвать функцию класса B для класса C, он будет работать с членом A::m_a по адресу this+0, как будто это B::m_b. Вот почему для обеспечения корректности программы приходится прибавлять смещение к указателю this при использовании функций класса B для объекта C. Поэтому в указателе на функцию для класса C появляется дополнительное поле — смещение. Для любых функций из классов A и C это смещение будет равно нулю, для функций класса B — четырём.


Я тут покопался в MSDN'е и нашел интересную штуку к теме. Хочу поделиться размышлениями. Поправьте меня, если ошибся где-то.

Если говоришь
#pragma pointers_to_members(full_generality,single_inheritance)
то поинтер будет 4-хбайтовым, но сгернерится ошибка, если схема наследования множественная или виртуальная.

#pragma pointers_to_members(full_generality,multiple_inheritance)
то поинтер будет 8-мибайтовым, но всё-таки сгернерится ошибка, если схема наследования виртуальная.

#pragma pointers_to_members(full_generality,virtual_inheritance)
то поинтер будет 8-мибайтовым, ошибок не будет никогда.

Всё вроде как понятно — первые два типа наследования Вы подробно разъяснили, но как быть с виртуальным наследованием? Не будете ли Вы любезным просветить неосведомлённых вроде меня?

Всего хорошего,
Ярослав.
Re[4]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.07.01 09:46
Оценка:
Здравствуйте Yarik, вы писали:

VD>>Интересно! А что произойдет если в GWL_USERDATA окна к которому Вы подключаетесь будет лежать какая то информация?


Y>А что там может лежать, если окно создаю я ?! На то оно и USERDADA, чтобы USER мог ложить туда свои DATA.


А какой смысл сабкласить окно если оно Ваше? Вы же имеете полный доступ к процедуре окна! Не проще ли вставить в неё вызов нузной функции.

Сабкласинг окон нужен в первую очередь для перехвата сообщений чужого окна и тут Ваш способ может привести к ошибке. Лучше закладывайте информацию в доп. свойства окна, а еще проще, эфективнее и надежнее использовать CWindowImpl<> из ATL. В ATL сабкласинг делаест очень красиво.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.07.01 10:01
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Здравствуйте VladD2, вы писали:


VD>>Здравствуйте Alexander Shargin:


VD>>У меня закралось подозрение, что что-то здесь не так. А нельзя ли гипотетический пример привести в практический, да так чтобы в консольку выводился sizeof(...).


AF>http://rsdn.ru/forum/?action=message&amp;gid=9&amp;mid=3097
Автор: Alex Fedotov
Дата: 16.07.01

AF>(я его компилировал и запускал, так что пример вполне практический)

VD>>Вот я тут кинул примерчик (http://www.rsdn.ru/forum/?action=message&amp;gid=9&amp;mid=3166
Автор: VladD2
Дата: 18.07.01
).


AF>Ну вы и извращенец, батенька :). Такими примерами можно запросто расшатать неокрепшую психику начинающих программистов.


Ну, я что ли придумал, что указатель на функцию нельзя привести к void*? Я извращался по совершенно конкретному поводу. Одному из наших программистов было поручено написать инициализацию нашего поп-ап-меню с картинками (из ascLib http://www.optim.ru/Software/rus/ascLib/ascLib.asp). Хотелось сделать её на подобии мапов (BEGIN_MSG_MAP ...) в ATL, но класс работы с меню помещался как локальная переменная основного класса (все усугублялось тем, что такой класс должен иметь возможность содержать несколько экземпляров меню). Основной класс должен обрабатывать сообщения о нажатии пунктов. Т.е. на лицо необходимость вызова функции по указателю из другого класса... Меня попросили помочь. Вот и пришлось выпендриваться. Мне проще пору строк на inline-asm-е написать чем бороться с недоработками языков и/или компиляторов.

AF>Пожалуйста, замени (c.*p)() вызовом через свой универсальный указатель.


На досуге попробую.

AF>По-моему, Алескандр все очень наглядно объяснил и должно быть понятно, почему это не будет работать.


Да не очень. Мне осталось непонятно зачем запоминать смещение в указателе, если оно и так известно. Приводи указатель перед вызовом и все.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Адрес метода
От: Yarik http://www.vershynin.com
Дата: 18.07.01 13:12
Оценка:
Здравствуйте VladD2, вы писали:


VD>>>Интересно! А что произойдет если в GWL_USERDATA окна к которому Вы подключаетесь будет лежать какая то информация?


Y>>А что там может лежать, если окно создаю я ?! На то оно и USERDADA, чтобы USER мог ложить туда свои DATA.


VD>А какой смысл сабкласить окно если оно Ваше? Вы же имеете полный доступ к процедуре окна! Не проще ли вставить в неё вызов нузной функции.


VD>Сабкласинг окон нужен в первую очередь для перехвата сообщений чужого окна и тут Ваш способ может привести к ошибке. Лучше закладывайте информацию в доп. свойства окна, а еще проще, эфективнее и надежнее использовать CWindowImpl<> из ATL. В ATL сабкласинг делаест очень красиво.


Я знаю, для чего нужен сабклассинг. Но ведь установка USERDATA не явсяется сабклассингом — просто с HWND связывается какой-л. LONG — и не более. А работа с Window Properties сдаётся мне грубой и утомительной. Если надо ассоциировать с окном кучу разных данных, проще объявить структуру и положить указатель на неё в USERDATA. Это проще и быстрее, чем играться со свойствами окна.

К вопросу о содержимом той самой USERDATA: (цитата из MSDN)
GWL_USERDATA — Sets the 32-bit value associated with the window. Each window has a corresponding 32-bit value intended for use by the application that created the window. This value is initially zero.

Т.е. для своих окон (в смысле создания, а не определения) можно смело юзать данную штуку. И не в коем случае ничего туда не пихать для окон, созданных другими процессами.

Для окон с определенным програмистом классом можно сделать ещё проще: при регистрации класса указать кол-во дополнительно выделяемых байт на окно/на класс и получать доступ к каждому LONG'у, из них составленному, с помощью Set/GetWindowLong и Set/GetClassLong. (так написано в MSDN)
Re[12]: Адрес метода
От: Alex Fedotov США  
Дата: 18.07.01 16:09
Оценка:
Здравствуйте VladD2, вы писали:

AF>>По-моему, Алескандр все очень наглядно объяснил и должно быть понятно, почему это не будет работать.


VD>Да не очень. Мне осталось непонятно зачем запоминать смещение в указателе, если оно и так известно. Приводи указатель перед вызовом и все.


А оно как раз не известно. Есть определение:

typedef int (CC::*CPtr)();

Есть указатель на член класса

CPtr p;

Eсть объект класса

CC c;

Вызываем

(c.*p)()

Как компилятор узнает, что p указывает на функцию из класса B, а не из класса A?
-- Alex Fedotov
Re[13]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.07.01 18:09
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Здравствуйте VladD2, вы писали:


VD>>Да не очень. Мне осталось непонятно зачем запоминать смещение в указателе, если оно и так известно. Приводи указатель перед вызовом и все.


AF>А оно как раз не известно. Есть определение:

AF>typedef int (CC::*CPtr)();
AF>Есть указатель на член класса
AF>CPtr p;
AF>Eсть объект класса
AF>CC c;
AF>Вызываем
AF>(c.*p)()

AF>Как компилятор узнает, что p указывает на функцию из класса B, а не из класса A?


Ну, если он не дурной, то глянет реализацию класса CC, вычислит смещение класса B и ровно на нее изменит указатель c.

В моем случае надо было вызывать методы относящиеся к самому верхнему классу, т.е. к CC в данной терминалогии, так и к нему нельзя. По-моему это или недоработка (Страуса, MS ...), или моё неумение решать проблемы данного класса средствами C++. Если верено последнее предположение, то может кно просветил бы, а?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.07.01 18:21
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Как компилятор узнает, что p указывает на функцию из класса B, а не из класса A?


Если функция не виртуальная, то ее адрес всегда фиксирован. Адресс функции — это всеголишь адресс ее первой инструкции.

А если так, то смешение может относиться только к сдвизке this (о чем собственно и коворил Шаргин). Структура всех классов известна компилятору и он может добавлять или вычитать смещение сам без доп информации. Так вот, мне и непонятно почему такие плевые операции компилятор не может расчитать сам?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[14]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.07.01 19:51
Оценка:
Здравствуйте VladD2, вы писали:

Кажись, дошло и до меня. :)

Они же запоминают не то! У них получается универсальный указатель на один из членов класса. В дельфях (если мне не изменяет память) указатель выглядел так: первое двойное слово было this-ом (self) конкретного экземпляра, а второе уже указателем на функцию.

Похоже подход дельфей более жизнеспособный. Честно говоря, я даже задачи не могу себе, представить, которая нуждалась бы в C++ указателях на одну из функций конкретного класса.

Ну, да ладно... Я переработал свой код так, чтобы он использовал ваш пример. Мой код использует Delphi-йскую идеологию, поэтому мне нужно было только скорректировать адрес this-а. Просто при присвоении указателя на this я учел, что функция чей адрес запоминается относится к дочернему классу. Вот код:

class CA
{
public:
CA(){ m_a = 1; }
int A(){ return m_a; }
protected:
int m_a;
};

class CB
{
public:
CB(){ m_b = 7777; }
int B(){ return m_b; }
protected:
int m_b;

};

class CC : public CA, public CB
{
public:
CC(){ m_c = 3; }
int C(){ return 3; }
protected:
int m_c;
};

typedef int (CC::*CPtr)();

// >>> --- CUniversalFuncPtr ---------------------------------------
struct CUniversalFuncPtr
{
void * pF; // Указатель на функцию класса
void * pThis; // Указатель на this экземпляр класса
};

int SomeCaller2(CUniversalFuncPtr UniversalFuncPtr)
{
int iRetVal;
__asm
{
// Эмулируем thiscall
mov ecx, UniversalFuncPtr.pThis;
mov eax, UniversalFuncPtr.pF;

//push i ; // закладываем значение параметра
call eax // ecx.f1(i);
mov iRetVal, eax
}
return iRetVal;
}
// <<< --- CUniversalFuncPtr ---------------------------------------

int main(int argc, char* argv[])
{
// Создаем переменную — подопытного кролика...
CC c;

// Поулучаем переменную на функцию некоторого класса
// совершенно неважно какой это класс. Главное что функция
// должна быть описанна как:
// void FuncName(int);
typedef int ( CC::*pmfnP)(void);
pmfnP pFunction = &CC::B;
printf("sizeof(pmfnP) = %d\n", sizeof(pmfnP));

CUniversalFuncPtr UniversalFuncPtr;

// >>> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

// Закладываем указатель на объекта (this)
// нужное смещение вычислит компилятор...
UniversalFuncPtr.pThis = (void*)(CB*)&c;

// а можно и так:
//UniversalFuncPtr.pThis = (void*)((char*)&c + *((int*)&pFunction+1));
// в этом случае смещение берется из тех самых лишних четырех байт,
// (о которых говорил Шаргин) добавляемых компилятором к указателю.

// <<< !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


// Закладываем указатель на функцию
__asm { mov eax, dword ptr pFunction }
__asm { mov UniversalFuncPtr.pF, eax }
// Приведенные выше операторы аналогичны конструкции:
// UniversalFuncPtr.pF = (void*)pFunction;
// но к сожалению такая конструкция вызывает сообщение об ошибке:
// error C2440: 'type cast' : cannot convert from
// 'void (__thiscall CC::*)(int)' to 'void *'

// ...

printf("UniversalFuncPtr.B() returns %d\n", SomeCaller2(UniversalFuncPtr));

// -------------------------------------------

printf("c.B() returns %d", (c.*pFunction)());

getchar();

return 0;
}

// ----------------------------------

Так всеже можно тоже самое сделать на сях без asm-а?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: Адрес метода
От: Alex Fedotov США  
Дата: 18.07.01 21:29
Оценка:
Здравствуйте VladD2, вы писали:

VD>Кажись, дошло и до меня. :)


VD> // Закладываем указатель на объекта (this)

VD> // нужное смещение вычислит компилятор...
VD> UniversalFuncPtr.pThis = (void*)(CB*)&c;

Проблема с этим в том, что не всегда заранее известно, на фукцию какого из базовых классов указывает указатель:

typedef int (CC::*CPtr)();

void Function(CPtr p)
{
CC c;
(c.*p)();
}

Имеющейся здесь информации недостаточно, чтобы кастировать this к нужному базовому классу во время компиляции, поэтому это происходит во время выполнения, а нужное смещение передается вместе с указателем.


VD> // а можно и так:

VD> //UniversalFuncPtr.pThis = (void*)((char*)&c + *((int*)&pFunction+1));
VD> // в этом случае смещение берется из тех самых лишних четырех байт,
VD> // (о которых говорил Шаргин) добавляемых компилятором к указателю.

Именно так и работает компилятор. Только он делает это в момент вызова, поскольку в момент создания указателя еще неизвестно, к какому экземпляру класса мы будем применять этот указатель.

Вроде разобрались?
-- Alex Fedotov
Re[15]: Адрес метода
От: Alexander Shargin Россия RSDN.ru
Дата: 19.07.01 02:58
Оценка:
Здравствуйте VladD2, вы писали:

VD> // Закладываем указатель на функцию

VD> __asm { mov eax, dword ptr pFunction }
VD> __asm { mov UniversalFuncPtr.pF, eax }
VD> // Приведенные выше операторы аналогичны конструкции:
VD> // UniversalFuncPtr.pF = (void*)pFunction;
VD> // но к сожалению такая конструкция вызывает сообщение об ошибке:
VD> // error C2440: 'type cast' : cannot convert from
VD> // 'void (__thiscall CC::*)(int)' to 'void *'
VD>
VD> // ...


VD>Так всеже можно тоже самое сделать на сях без asm-а?



Вместо

__asm { mov eax, dword ptr pFunction }
__asm { mov UniversalFuncPtr.pF, eax }


можно использовать такую конструкцию:

template<typename PTR> union PtrConvertor
{
PTR ptr;
struct
{
void *addr;
int offset;
} data;
};

...

PtrConvertor<pmfnP> pc;
pc.ptr = CC::B;
UniversalFuncPtr.pF = pc.data.addr;
// Можно использовать и pc.data.offset
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[9]: Адрес метода
От: Gambler  
Дата: 19.07.01 07:01
Оценка:
Здравствуйте Alexander Shargin, вы писали:


AS>Объект класса C выглядит так же, как и раньше:

AS>*-----------------* <-this
AS>* Данные класса A *
AS>*-----------------*
AS>* Данные класса B *
AS>*-----------------*
AS>* Данные класса C *
AS>*-----------------*

Хм.. тогда в чём здесь дело

=======
// test002.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "stdio.h"
#include "tchar.h"

class CA
{
public:
int A()
{ printf("\nThis is A();\t%d\n", this);return 1; }
protected:
int m_a;

};

class CB
{
public:
int B()
{ printf("\nThis is B();\t%d\n", this);return 2; }
protected:
int m_b;

};

class CC : public CA, public CB
{
public:
int C()
{ printf("\nThis is C();\t%d\n", this);return 3; }
protected:
int m_c;
};

// &ograve;&agrave;&ecirc; &icirc;&aacute;&uacute;&yuml;&acirc;&euml;&yuml;&aring;&ograve;&ntilde;&yuml; &oacute;&ecirc;&agrave;&ccedil;&agrave;&ograve;&aring;&euml;&uuml; &iacute;&agrave; &divide;&euml;&aring;&iacute; &ecirc;&euml;&agrave;&ntilde;&ntilde;&agrave;
typedef int (CC::*CPtr)();


int _tmain( int argc, _TCHAR * argv[] )
{
// &yacute;&ograve;&icirc; &ecirc; &acirc;&icirc;&iuml;&eth;&icirc;&ntilde;&oacute; &icirc; &eth;&agrave;&ccedil;&igrave;&aring;&eth;&aring; &oacute;&ecirc;&agrave;&ccedil;&agrave;&ograve;&aring;&euml;&yuml;
printf("sizeof(CPtr) = %d\n", sizeof(CPtr));

// &ograve;&agrave;&ecirc; &egrave;&iacute;&egrave;&ouml;&egrave;&agrave;&euml;&egrave;&ccedil;&egrave;&eth;&oacute;&aring;&ograve;&ntilde;&yuml; &oacute;&ecirc;&agrave;&ccedil;&agrave;&ograve;&aring;&euml;&uuml; &iacute;&agrave; &divide;&euml;&aring;&iacute; &ecirc;&euml;&agrave;&ntilde;&ntilde;&agrave;
CPtr p = CC::A;

CC c;

(c.*p)();

p = CC::B;

(c.*p)();

p = CC::C;

(c.*p)();


return 0;
}

==============

результат выполнения

sizeof(CPtr) = 8

This is A(); 1245036

This is B(); 1245040

This is C(); 1245036

То есть this другой у B

почему?
-------------------------------------------------------------------

Вызывает презедент к себе коров и говорит:
— Ну, что будем сдавать, молоко или мясо?
(с) Г. Явлинский TV6 — Герой дня (18.04.2002)
Re: Адрес метода
От: Аноним  
Дата: 19.07.01 07:46
Оценка:
Здравствуйте Vovchik, вы писали:

V>К куче существующих окон необходимо заменить WindowProc.

V>Первое, что пришло в голову, создать класс CMyWnd,
V>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>Одна универсальная функция неэффективна, т.к. единственное
V>различие для вызовов из разных окон это значение hWnd?
V>а по нему искать каждый раз OldWindwProc не хочется.

V>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?

V>Вопрос 2: Как иначе организовать множество функций различающихся только
V> значением атрибута OldWindwProc.

1. В MFC эта задача решена через глобальную таблицу экземпляров классов окон.
2. В ATL более красиво, без всяких-там SetWindowLong, — через thunk:
поищи в сорцах ATL (atlwin.h) все, что связанно с
struct _WndProcThunk
{
DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
DWORD m_this; //
BYTE m_jmp; // jmp WndProc
DWORD m_relproc; // relative jmp
};
Re[16]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.07.01 16:19
Оценка:
Здравствуйте Alexander Shargin, вы писали:

AS>template<typename PTR> union PtrConvertor


Да. template ... union это круто. :) Я чесно говоря даже незнал что так можно. Век живи, век... все равно... :)

Ну, а как на счет замены для вызова функции на asm-е? Я как то пробовал заменить его на вызов в стиле C-декл. (в смысле, глобальной функцией описанной как стандартная сишная ф-я), но что-то у меня не вышло. :( На asm-е получилось проще. :)
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[16]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.07.01 16:31
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Проблема с этим в том, что не всегда заранее известно, на фукцию какого из базовых классов указывает указатель:



AF>typedef int (CC::*CPtr)();


AF>void Function(CPtr p)

AF>{
AF> CC c;
AF> (c.*p)();
AF>}

AF>Имеющейся здесь информации недостаточно, чтобы кастировать this к нужному базовому классу во время компиляции, поэтому это происходит во время выполнения, а нужное смещение передается вместе с указателем.



Ну, во время присвоения информации хоть отбавляй. :) К тому же мне (и остальным программистам) надо решать конкретные задачи, а не гипотетические.
Вот Александр Шаргин дал красивую идею как убрать asm пир копировании указателя на функцию. Может поможете убрать оставшийся asm?

VD>> // а можно и так:

VD>> //UniversalFuncPtr.pThis = (void*)((char*)&c + *((int*)&pFunction+1));
VD>> // в этом случае смещение берется из тех самых лишних четырех байт,
VD>> // (о которых говорил Шаргин) добавляемых компилятором к указателю.

AF>Именно так и работает компилятор.

Гы-гы. Так я у него и подсмотрел. :)

AF>Только он делает это в момент вызова, поскольку в момент создания указателя еще неизвестно, к какому экземпляру класса мы будем применять этот указатель.


В момент создания указателя не известно, а в момент его инициализации (присвоения ему реального значения, см. пример), известно!

AF>Вроде разобрались?


А что толку? Вы вон сами мой код назвали "расшатывающим неокрепшую психику начинающих программистов". :) Если уж в языке есть недоработки (или кривые места), то конструктивней попробовать сообща решить возникающие, из-за этого, проблемы средствами того же языка!
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[17]: Адрес метода
От: Alex Fedotov США  
Дата: 19.07.01 17:32
Оценка:
Здравствуйте VladD2, вы писали:

VD>Вот Александр Шаргин дал красивую идею как убрать asm пир копировании указателя на функцию. Может поможете убрать оставшийся asm?


А зачем? Все равно это как было шаманством, так шаманством и останется, полагающимся на реализацию конкретного компилятора.

VD>>> // а можно и так:

VD>>> //UniversalFuncPtr.pThis = (void*)((char*)&c + *((int*)&pFunction+1));
VD>>> // в этом случае смещение берется из тех самых лишних четырех байт,
VD>>> // (о которых говорил Шаргин) добавляемых компилятором к указателю.

AF>>Именно так и работает компилятор.

VD>Гы-гы. Так я у него и подсмотрел. :)

AF>>Только он делает это в момент вызова, поскольку в момент создания указателя еще неизвестно, к какому экземпляру класса мы будем применять этот указатель.


VD>В момент создания указателя не известно, а в момент его инициализации (присвоения ему реального значения, см. пример), известно!


Нет, нет и еще раз нет, если мы говорим о языке C++. В момени инизиализации указателя

CPtr ptr = CC::B;

известен только класс, который будет вызываться, а экземпляр класса, может появиться много позже. Хорошо известный пример — message maps в MFC, которые хранят указатели на члены класса. А объект появляется гораздо позже, более того, их может быть несколько.

Твой же UniversalFuncPtr -- скорее аналог интерфейса, чем указателя на метод класса. Кстати, в реальной жизни, я пользуюсь именно интерфейсами, а не указателями на члены класса.

AF>>Вроде разобрались?


VD>А что толку? Вы вон сами мой код назвали "расшатывающим неокрепшую психику начинающих программистов". :) Если уж в языке есть недоработки (или кривые места), то конструктивней попробовать сообща решить возникающие, из-за этого, проблемы средствами того же языка!


Ну, программисты на некоторых других языках (не будем показывать пальцем), считают, что множественное наследование является кривым местом, давайте теперь его исправлять. Я не считаю вызовы функций-членов класса кривыми и я понимаю, почему они реализованы именно так, а не иначе.

За сим откланиваюсь (подустал я от этой дискуссии, лучше пойду что-нибудь полезное сделаю).
-- Alex Fedotov
Re[18]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.07.01 20:51
Оценка:
Здравствуйте Alex Fedotov, вы писали:

AF>Здравствуйте VladD2, вы писали:


VD>>Вот Александр Шаргин дал красивую идею как убрать asm пир копировании указателя на функцию. Может поможете убрать оставшийся asm?


AF>А зачем? Все равно это как было шаманством, так шаманством и останется, полагающимся на реализацию конкретного компилятора.


Думаю, другие компиляторы устроены также. К тому же они мне по барабану. Взяв за основную библиотеку ATL вряд ли будишь задумываться о переносимости. Да, и проблем особых нет! Надо будет, сделаем #if... для нужного компилятора.

AF>Нет, нет и еще раз нет, если мы говорим о языке C++.


Я скорее всего говорю о диалекте этого языка от MS и его реализации в VC. Она пока от стандарта отличается.

AF>В момени инизиализации указателя

AF>CPtr ptr = CC::B;

AF>известен только класс, который будет вызываться, а экземпляр класса, может появиться много позже.


Эта и есть никчемная реализация которая меня не устраивает. Да, и плевать мне на то, что Страуструп и орлы из команды VC не додумались до такой простой идеи.
Мне хочется работать легко и писать красивый код! Я уже продемонстрировал, работоспособность такого указателя.


AF>Хорошо известный пример — message maps в MFC, которые хранят указатели на члены класса. А объект появляется гораздо позже, более того, их может быть несколько.


Но мне-то это извращение НЕ НУЖНО! Мне больше нравится реализация в ATL. Она значительно более гибка! Кстати, в ATL есть несколько мест, которые являются извращениями ни чуть не меньшими чем мое... ну и почему все используют ATL и не говорят, что это "не переносимо, шаманство, ...", а? А в Delphi половина базового класса TObject на asm-е написано... Попробуйте сказать Дельфинарию слова про шаманство! :)

AF>Твой же UniversalFuncPtr -- скорее аналог интерфейса, чем указателя на метод класса. Кстати, в реальной жизни, я пользуюсь именно интерфейсами, а не указателями на члены класса.


Он не скорее, а совершенно точно аналогичен указателю на функцию экземпляра в Delphi, Делегату в .Net и т.п. А еще он аналогичен call-back-функции получающей хэндлер, а на интерфейс он совсем не похож (Интерфейс – это указатель на vtbl фиксированного вида). Интерфейсы и я использую, но использовать их для реализации событий (внутри своего кода) — это большой геморрой (Уж с ними мы натрахались вдоволь, в Ax-ах и COM я то точно кое-что понимаю). К тому же у них есть один недостаток – в одном классе, интерфейс нельзя реализовать боле одного раза. Тат же ATL для обхода этого использует примочки в виде базовых классов XxxImpl. Конечно, можно сделать код на базе сообщений Windows, но хочется как то поэлегантней.

AF>Ну, программисты на некоторых других языках (не будем показывать пальцем), считают, что множественное наследование является кривым местом, давайте теперь его исправлять.


Множественное наследование мне нравится, и отказываться от него не хочется (очень жаль, что в .Net на нем поставлен крест). Особенно оно эффективно в сочетании с шаблонами (коих в .Net тоже не наблюдается, но в будущем обещают)...
Раньше я даже не мог понять, почему все новомодные языки стремятся его избежать, а теперь начинаю понимать... Хотя, похоже, всему виной непродуманность, а не реальные проблемы с множественным наследованием.

AF>Я не считаю вызовы функций-членов класса кривыми и я понимаю, почему они реализованы именно так, а не иначе.


А я считаю, что указатели на функции в старом добром C были мощным и удобным инструментом, а указатели на функции-члены в C++ стали совершенно бесполезным хламом. Любое использование этой фичи (которое может прийти мне на ум) можно заменить более изящными средствами (теми же интерфейсами, шаблонами...), а там где это действительно нужно они просто непригодны, из-за "ПРЯПОЙ" реализации. Мне нужно вызывать метод у любого объекта по некоторой ссылке. Именно это и недает мен данная реализация.
MS пытается продать делегаты как свое открытие, тогда как реально их использовали еще на C. И только по вине Страуструпа в C++ не появилось такого же удобного, гибкого и мощного средства как "указатели на функции экземпляра объекта".

PS

Любой язык, претендующий на звание "живого" должен развиваться. Многое, что мы сейчас видим C++ это плод обобщения использования ОО-методов в том самом C. На C чтобы с эмулировать объектную ориентацию приходилось изгаляться не хуже моего. Ну, и что? C++ вобрал в себя все, что мог дать C и привнес несколько фичь которые позволили (более менее) программировать на этом языке как на ОО. Так вот если C++ не хочет уступить место языкам без множественного наследования и других "излишеств" (таким как C# или гы-гы VB.Net) он должен вбирать правильные идеи, а начинаются такие идеи с вот таких извращенческих шаманств. :).

Короче, раньше бы я плюнул, а теперь обязательно это дело на чистых плюсах перепишу. :)
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 19.07.01 21:07
Оценка:
Здравствуйте Аноним, вы писали:

А>Здравствуйте Vovchik, вы писали:


V>>К куче существующих окон необходимо заменить WindowProc.

V>>Первое, что пришло в голову, создать класс CMyWnd,
V>>содержащий метод MyWndowProc и атрибут OldWindwProc.
V>>Одна универсальная функция неэффективна, т.к. единственное
V>>различие для вызовов из разных окон это значение hWnd?
V>>а по нему искать каждый раз OldWindwProc не хочется.

V>>Вопрос 1: Как заполучить адрес метода из экземпляра класса для SetWindowLong?

V>>Вопрос 2: Как иначе организовать множество функций различающихся только
V>> значением атрибута OldWindwProc.

А>1. В MFC эта задача решена через глобальную таблицу экземпляров классов окон.

А>2. В ATL более красиво, без всяких-там SetWindowLong, — через thunk:
А>поищи в сорцах ATL (atlwin.h) все, что связанно с
А>struct _WndProcThunk
А>{
А> DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
А> DWORD m_this; //
А> BYTE m_jmp; // jmp WndProc
А> DWORD m_relproc; // relative jmp
А>};


Господа! Хочется задать вопрос!

Так, такие /\/\/\/\ вещи это красИииво или извращЩщщение?! :)

И что лучше без извращений, но с глобальным Мапом, или с извращениями, но коротко и эффективно?


Не стесняйтесь. Высказывайтесь. Интересно любое мнение.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[17]: Адрес метода
От: Alexander Shargin Россия RSDN.ru
Дата: 20.07.01 16:14
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте Alexander Shargin, вы писали:



VD>Ну, а как на счет замены для вызова функции на asm-е? Я как то пробовал заменить его на вызов в стиле C-декл. (в смысле, глобальной функцией описанной как стандартная сишная ф-я), но что-то у меня не вышло. :( На asm-е получилось проще. :)



Ну, чувствую я, что меня опять назовут извращенцем. :)

На самом деле, чтобы вызвать функцию, достаточно какого-нибудь класса, у которого указатель на функцию "весит" 4 байта. Идеальный кандидат — сам CUniversalFuncPtr. Используя эту идею, вместо

int iRetVal;
__asm
{
// Эмулируем thiscall
mov ecx, UniversalFuncPtr.pThis;
mov eax, UniversalFuncPtr.pF;

//push i ; // закладываем значение параметра
call eax // ecx.f1(i);
mov iRetVal, eax
}
return iRetVal;


можно написать:

CUniversalFuncPtr *ptr;
int (CUniversalFuncPtr::*pfn)();

ptr = (CUniversalFuncPtr *)UniversalFuncPtr.pThis;
(void*&)pfn = UniversalFuncPtr.pF;

return (ptr->*pfn)();



С уважением,
Александр
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[18]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.07.01 17:20
Оценка:
Здравствуйте Alexander Shargin, вы писали:

AS>Ну, чувствую я, что меня опять назовут извращенцем. :)


Не присваивайте чужой славы! Извращенцем называли меня. :)

AS>На самом деле, чтобы вызвать функцию, достаточно какого-нибудь класса, у которого указатель на функцию "весит" 4 байта.


Собственно я так с самого начала думал, но толи не все так просто, толи я лоханулся при создании кода...

AS>Идеальный кандидат — сам CUniversalFuncPtr. Используя эту идею, вместо


AS> int iRetVal;

AS> __asm
AS> {
AS> // Эмулируем thiscall
AS> mov ecx, UniversalFuncPtr.pThis;
AS> mov eax, UniversalFuncPtr.pF;
AS>
AS> //push i ; // закладываем значение параметра
AS> call eax // ecx.f1(i);
AS> mov iRetVal, eax
AS> }
AS> return iRetVal;


AS>можно написать:


AS> CUniversalFuncPtr *ptr;

AS> int (CUniversalFuncPtr::*pfn)();

AS> ptr = (CUniversalFuncPtr *)UniversalFuncPtr.pThis;

AS> (void*&)pfn = UniversalFuncPtr.pF;

AS> return (ptr->*pfn)();


Не очень красиво получается. При таком подходе надо каждый раз создавать новый базовый класс. Надо подумать на счет чего-то более универсальной реализации. Может быть что-то на тех же шаблонах?

А можно ли передавать тип указателя на функцию в качестве параметра шаблона?

PS

Я давно мечтал о человеческой реализации фичи которую теперь называют делегатами.

В любом случае спасибо! И последний вопрос может, создадим статейку на эту тему. Ведь за последнее время эта тема собрало самую большую аудиторию.

К ALL: Если считаете, что такая статья нужна, шлите сюда свои постинги с подтверждением, а так же предложения.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[19]: Адрес метода
От: Alexander Shargin Россия RSDN.ru
Дата: 20.07.01 17:39
Оценка:
Здравствуйте VladD2, вы писали:

AS>>На самом деле, чтобы вызвать функцию, достаточно какого-нибудь класса, у которого указатель на функцию "весит" 4 байта.

VD>Собственно я так с самого начала думал, но толи не все так просто, толи я лоханулся при создании кода...

Не знаю, там единственная тонкость — приведение (void*&)pfn.


AS>>Идеальный кандидат — сам CUniversalFuncPtr. Используя эту идею, вместо

<skip>

AS>>можно написать:

<skip>

VD>Не очень красиво получается. При таком подходе надо каждый раз создавать новый базовый класс. Надо подумать на счет чего-то более универсальной реализации. Может быть что-то на тех же шаблонах?


Про шаблоны я уже сам занёс руку тебе написать, но вовремя остановился. Дело в том, что, раз уж мы заговорили о делегатах, нам нужна возможность "на лету" заменять указатель на функцию одного объекта указателем на функцию совсем другого. Поэтому придётся оставить всё, как есть.

А новые классы создавать не надо — зачем? Код, который я привёл, эквивалентен твоему ассемблеру: если ты правильно задал this и адрес функции, он будет работать для самых различных объектов... Он ведь просто помещает ptr в ecx и делает call pfn... Хотя это, конечно, извращение... ;)


Александр
--
Я думал, ты огромный страшный Бажище,
А ты недоучка, крохотный Бажик...
Re[20]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.07.01 19:47
Оценка:
Здравствуйте Alexander Shargin, вы писали:

Будет время попробую по-колдовать еще разок. Может выйдет что путное и красивое.

PS

Тут, вот, некоторые говорять извраЩщщение... а некоторые приводят примеры из ATL-ного сабкласинга и говорят красота. Так что — то что для одного изврат, для другого красота. ;)

Еще раз спасибо!
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 22.07.01 17:13
Оценка:
Здравствуйте VladD2, вы писали:

VD>Так всеже можно тоже самое сделать на сях без asm-а?


Смотря что вы подразумеваете под "темже самым".
А по другому конечно можно :)

структура классов CA, CB, CC таже, повторять нет смысла.

template<class R, class T> class mem_fun_t2
{
typedef R (T::*MFPtr)();
MFPtr pF; // указатель на функцию класса T
public:
explicit mem_fun_t2(MFPtr p) : pF(p) {}
R operator() (T* p) { return (p->*pF)(); } // вызов фунции из конкретного объекта класса T
};

int main(int argc, char* argv[])
{
typedef int (CC::*CPtr)();
CPtr pF= CC::B;
mem_fun_t2<int, CC> fB(pF); // в объекте fB теперь находиться указатель на CC::B

CC c;
printf("mem_fun_t2 returns %d", fB(&c)); // вызов c.B();
printf("c.B() returns %d", (c.*pF)());
return 0;
}

Между прочим, это не я придумал, оригинальный класс mem_fun_t находиться
в файле "functional" из STL.
Можно продолжить идею создания "универсального" указателя на функцию и, в частности, callback. Например, так:

// mem_fun так-же описана в "functional"
template<class R, class T> mem_fun_t2<R,T> mem_fun2(R (T::*f)())
{
return mem_fun_t2<R,T>(f); // всего лишь создаем и возвращаем объект mem_fun_t2
}

// вместо этой функции может быть и класс
template<class R, class Function> R f(Function F)
{
//...
CC c;
R m=F(&c); // F нет в классе СС!
//...
return m;
}

int f2(CC* pC)
{
// все что угодно
//...
return 4;
}

int main(int argc, char* argv[])
{
int m=f<int>(mem_fun2(CC::B)); // вызов CC::B
m=f<int>(f2); // вызов f2
return 0;
}
Кто ищет то всегда найдет!
Re[19]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 22.07.01 18:10
Оценка:
Здравствуйте VladD2, вы писали:

VD>Но мне-то это извращение НЕ НУЖНО! Мне больше нравится реализация в ATL. Она значительно более гибка! Кстати, в ATL есть несколько мест, которые являются извращениями ни чуть не меньшими чем мое... ну и почему все используют ATL и не говорят, что это "не переносимо, шаманство, ...", а? А в Delphi половина базового класса TObject на asm-е написано... Попробуйте сказать Дельфинарию слова про шаманство! :)


Подобные вещи (использование asm там где можно и без него обойтись) перестауют быдь шаманством только тогда, когда они хорошо протестированы на всех доступных системах и окружениях. И даже тогда остается покрайне мере несколько ситуаций когда они (реализации) будут кардинально не верны.


AF>>Я не считаю вызовы функций-членов класса кривыми и я понимаю, почему они реализованы именно так, а не иначе.


VD>А я считаю, что указатели на функции в старом добром C были мощным и удобным инструментом, а указатели на функции-члены в C++ стали совершенно бесполезным хламом. Любое использование этой фичи (которое может прийти мне на ум) можно заменить более изящными средствами (теми же интерфейсами, шаблонами...), а там где это действительно нужно они просто непригодны, из-за "ПРЯПОЙ" реализации. Мне нужно вызывать метод у любого объекта по некоторой ссылке. Именно это и недает мен данная реализация.

VD>MS пытается продать делегаты как свое открытие, тогда как реально их использовали еще на C. И только по вине Страуструпа в C++ не появилось такого же удобного, гибкого и мощного средства как "указатели на функции экземпляра объекта".

Средство "указатели на функции экземпляра объекта" подразумевает две вещи:
1. указатель на объект
2. указатель на функцию в _этом_ объекте
Все это присутствует в С++
Избавивщись от первого мы избавляемся от самого объекта. Так может программировать тогда С? Или использовать глобальные функции, или статические. А может быть по-другому спроектировать программу. Вот ты критикуешь С++ и хвалишь ATL (читай MS) за то что они исправляют "ошибки" Страуструпа. Интерестно какова сложность проектирования C++ и ATL (MFS & etс.)? Где и кем используется С++ и ATL? В конце концов ATL это всего лишь библиотека которую написали как смогли, на том _подмножестве_ C++ которое знали эти программисты и которое поддерживает этот компилятор.

PS: нет такого языка, который бы всем нравился и всех удовлетворял

С уважением, IAZ.
Кто ищет то всегда найдет!
Re[14]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 22.07.01 18:54
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте Alex Fedotov, вы писали:


AF>>Как компилятор узнает, что p указывает на функцию из класса B, а не из класса A?


VD>Если функция не виртуальная, то ее адрес всегда фиксирован. Адресс функции — это всеголишь адресс ее первой инструкции.


VD>А если так, то смешение может относиться только к сдвизке this (о чем собственно и коворил Шаргин). Структура всех классов известна компилятору и он может добавлять или вычитать смещение сам без доп информации. Так вот, мне и непонятно почему такие плевые операции компилятор не может расчитать сам?


Почему не может? В определенных случаях он даже может виртуальную функцию сделать inline. Но в общем случае пологаться на это нельзя.
Кто ищет то всегда найдет!
Re[15]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.07.01 19:10
Оценка:
Здравствуйте IAZ, вы писали:


IAZ>Почему не может? В определенных случаях он даже может виртуальную функцию сделать inline. Но в общем случае пологаться на это нельзя.


Гы-гы. ("может"... "пологаться на это нельзя")

PS
Вы или не разобрались в вопросе, или не хотите меня пнять.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[19]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 22.07.01 19:12
Оценка:
Здравствуйте VladD2, вы писали:

VD>В любом случае спасибо! И последний вопрос может, создадим статейку на эту тему. Ведь за последнее время эта тема собрало самую большую аудиторию.


VD>К ALL: Если считаете, что такая статья нужна, шлите сюда свои постинги с подтверждением, а так же предложения.


Сомневаюсь, что такая статья необходима — зачем переливать из пустого в порожнего: так нельзя попробуйте по другому. Это тянет не больше чем на маленький FAQ.
А если все таки написать статью, то неизвестно как она повлияет на неокрепшую психику начинающего программиста. Из-за отсутвия опыта и знаний он может это использовать не поназначению. А чтобы охватить все аспекты этой проблемы одной статьи мало.

С уважением, IAZ.
Кто ищет то всегда найдет!
Re[16]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 22.07.01 19:36
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте IAZ, вы писали:



IAZ>>Почему не может? В определенных случаях он даже может виртуальную функцию сделать inline. Но в общем случае пологаться на это нельзя.


VD>Гы-гы. ("может"... "пологаться на это нельзя")


VD>PS

VD>Вы или не разобрались в вопросе, или не хотите меня пнять.

Очепяток тут хватает, я бы не стал на этом заострять внимание :)

Вопрос был в следующем: присвоить указателю на глобальную функцию указатель на функцию-член. Стандарт это не позваляет. Во многих случаях во время исполнения не происходит добавления этого смещения — добавляется при компиляции. Так что реально такая функция становиться самой обычной (как глобальная). Но еще раз сошлюсь на стандарт: он не позволит обращаться с таким указателем как с обычным. Но asm'е это можно успешно преодалеть, только работать будет не всегда.
Кто ищет то всегда найдет!
Re[20]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.07.01 21:22
Оценка:
Здравствуйте IAZ, вы писали:

IAZ>Подобные вещи (использование asm там где можно и без него обойтись) перестауют быдь шаманством только тогда, когда они хорошо протестированы на всех доступных системах и окружениях.


Ну, "когда они хорошо протестированы на всех доступных системах", это Вы хватили. :)
Кто же даст на свою систему залезть, да и вся жизнь на это уйдет. ;)

Вполне достаточно логического обоснования, ну и может тестирования на компиляторах которые необходимо поддерживать. Для меня – это, пока, VC 6 и VC 7.

IAZ> И даже тогда остается покрайне мере несколько ситуаций когда они (реализации) будут кардинально не верны.


Т.е. если есть asm-вставка, то обязательно когда ни будь грохнет? А как же половина MS CRT — написанная на asc-е? И куски из ATL?

Дело в том, что если есть человеческий метод решения на основном языке, то выпендриваться на asm-е, просто, нет необходимости (если конечно не нужно оптимизировать производительность). Но, не всегда это возможно. Иногда это реальная необходимость (недоработки языка или компилятора), а иногда просто в голову не приходит. :) Вот Шаргин тут давичи показал пример использования шаблона-объединения. Понятно, что вещь стандартная и предусмотренная языком, но вот не пришла она в голову, а на асм-е я там совсем примитивную вещь делал – 4-е байта копировал. Неужели от этого могут быть какие-то проблемы? Да и спецификация thiscall-а полностью описана. Так что проблема здесь может возникнуть, только если первые 4-е байта указателя на функцию класса будут содержать что ни будь отличное от указателя на функцию. В VC такое невозможно, в других компиляторах... возможно конечно, но мало вераятно. Будет возникать проблема с переносимостью разобрались бы с конкретным компилятором и... Ну, а asm здесь вообще не причем напишите такой код хоть на C, хоть на C++, хоть на VB, ровным счетом ни чего не изменится. Здесь налицо зависимость от компилятора.

IAZ>Средство "указатели на функции экземпляра объекта" подразумевает две вещи:

IAZ>1. указатель на объект
IAZ>2. указатель на функцию в _этом_ объекте

Ага.

IAZ>Все это присутствует в С++


Ага. Но по отдельности. И указатель на функцию одного класса нельзя нормальными средствами преобразовать в указатель на функцию другого объекта. Вот и приходится выходить из положения. Можно конечно воспользоваться указателями на интерфейс (как мне тут советовали), но я уже так ... (почему это неудобно можно почесть из моего ответа тому кто советовал).

IAZ>Избавивщись от первого мы избавляемся от самого объекта. Так может программировать тогда С? Или использовать глобальные функции, или статические.


Причем здесь C и глобальные функции я не знаю, но именно "от первого" и избавился СтраусТруп при создании своих указателей. Может он подразумевал, что указатели на функции разных классов можно будет попросту приводить друг к другу? Я не знаю но реалии жизни, в лице VC, плюют на все предпложения...

IAZ>А может быть по-другому спроектировать программу.


Можно конечно (можно нагородить кучу кода для создания простого алгоритма), а можно и язык по-другому... или небольшой классик/шаблончик нарисовать...

IAZ> Вот ты критикуешь С++ и хвалишь ATL (читай MS) за то что они исправляют "ошибки" Страуструпа.


Страуструпа, да пожалуй, я критикую. Пару тройку правильных решений он не учел.
А "хвалишь ATL" – боже упаси. Я просто заметил, что когда гуру из MS пишут нетривиальный код (иногда использующий asm) для того чтобы потом работать легко и просто, то их называют молодцами, а код называют "красивым". Ну, а если аналогичные действию делают другие, то сразу начинается ханжество (заранее извините, если я кого оскорбил этим словом). Еще раз повторюсь – я с удовольствием переписал бы этот код на чистом C++, но пока красивого законченного решения у меня нет. :(

IAZ> Интерестно какова сложность проектирования C++ и ATL (MFS & etс.)? Где и кем используется С++ и ATL? В конце концов ATL это всего лишь библиотека которую написали как смогли, на том _подмножестве_ C++ которое знали эти программисты и которое поддерживает этот компилятор.


Ннн-да, думаю, что программисты эти были одними из лучших в мире. По крайней мере, нас с Вами они, похоже, превосходят. Так, что можно не сомневаться, что C++ они знают в совершенстве. И не в реализации дело. В VC реализовано (я говорю только о нашей проблеме) то, что было описано в стандарте. Т.е. указатель на некоторую функцию КОНКРЕТНОГО класса! А мне нужен указатель на функцию ЭКЗЕМПЛЯРА объекта. Улавливаете разницу? Так что здесь налицо неграмотное планирование! Планирование элемента языка программирования. Как я уже говорил эту фичу (глубоко наплевав на Дельфи) MS продала за новую идею, назвала ее делегатами, и попыталась встроить ее в Яву. :)

И почему Вы думаете, что "ATL это всего лишь библиотека которую НАПИСАЛИ КАК СМОГЛИ", а C++ это незыблемая вещь которую написали бессмертные гении? Страуструп совсем непохож на Моцарта, да и у последнего не все произведения становились бессмертными. Лажался Страуструп (при создании этого языка) не мало. Так что мы всего лишь замазываем за ним дырки. Не, я понимаю, что многих идей просто не было в то время, например сборки мусора, но указатели на функции он попытался перенести в объектно-ориентированную среду, но … но облажался. Получилась никчемная реализация (из хорошей идеи).

IAZ>PS: нет такого языка, который бы всем нравился и всех удовлетворял


Полностью согласен, но большинство вещей в C++ меня устраивает (недоработок много, но большинство из них можно залатать средствами самого языка). А так... значит если я встретил проблему, то лапки к верху и пошел делать так как получается? Не-ааа, это не для меня. Криво — поправим. Скрипит — подмажем...

А, за Ваш совет (в другом постинге) спасибо. Правда, пока я не разобрался можно ли из него почерпнуть чего интересного, но все равно спасибо!

Будет время, попробую по колдовать. Может и сгодится идея перебивать оператора ().
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[20]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.07.01 21:33
Оценка:
Здравствуйте IAZ, вы писали:

IAZ>Здравствуйте VladD2, вы писали:



IAZ>Сомневаюсь, что такая статья необходима — зачем переливать из пустого в порожнего: так нельзя попробуйте по другому. Это тянет не больше чем на маленький FAQ.


Так это зависит от количества написанного? А вопрос этот (в той или другой форме) подымается очень часто. Но все равно спасибо за мнение.

IAZ>А если все таки написать статью, то неизвестно как она повлияет на неокрепшую психику начинающего программиста.


Ну, на психику влиять можно по разному. :) Если хорошенько объяснить, что хорошо, а что плохо, что можно смело использовать, а что с осторожностью... Главный вопрос понимания того, что есть и чего нет! А выводы лучше оставить читателю.

IAZ>Из-за отсутвия опыта и знаний он может это использовать не поназначению.


Ну, батенька — это уже цензура какая-то! Эдак и про нож можно сказать, что его описывать нельзя — "А вдруг его кто не по назначению использует?". :)

IAZ>А чтобы охватить все аспекты этой проблемы одной статьи мало.


А, тут Вы ошибаетесь. Думаю одной статейки будет как раз, а если и не влезет, сделаем сериал. :)

С уважением, Влад.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[17]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.07.01 21:42
Оценка:
Здравствуйте IAZ, вы писали:

IAZ>Здравствуйте VladD2, вы писали:


VD>>Здравствуйте IAZ, вы писали:


IAZ>Очепяток тут хватает, я бы не стал на этом заострять внимание :)


Я не об опечатка (сам не без греха). Я о противоречии самому себе в одном обзаце, и о недоверии компилятору. :)

IAZ>Вопрос был в следующем: присвоить указателю на глобальную функцию указатель на функцию-член. Стандарт это не позваляет. Во многих случаях во время исполнения не происходит добавления этого смещения — добавляется при компиляции. Так что реально такая функция становиться самой обычной (как глобальная). Но еще раз сошлюсь на стандарт: он не позволит обращаться с таким указателем как с обычным. Но asm'е это можно успешно преодалеть, только работать будет не всегда.


Не-аа, вопрос был о том, что в С++ не реслизована возможность вызвать фукцию класса по универсальному указателю! Указатель на функцию класса нафих не нужен, а нужен указатель следующего вида:

this // некоторого экземпляра
func_ptr // указатель на функцию

причем если задавать эти параметры во время когда объект создан, то нет проблем ри в вычислении правильного this, ни в вычислении правильного адреса функции (даже если она будет виртуальной). Идея проста как 3 копейки но увы...

А вызов он хоть будь трижды виртуальным все равно вревратится в:

f1(this, xxx);
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Адрес метода
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 23.07.01 03:47
Оценка:
VD>Кстати, а ни кто не пробывал создать унивирсальный указатель на функцию любого класса. На asm-е я такие штуки прокатывал, а как на Плюсах?

Я изголялся с этой задачей — решается через шаблонные функции и классы. Но компилятор должен быть путевым, что бы такое вытянуть.

простейший вариант выглядит так

пардон, что со шрифтами фигня.

////////////////////////////////////////////////////////////////////////////////
//&Ecirc;&euml;&agrave;&ntilde;&ntilde;-&ccedil;&agrave;&atilde;&euml;&oacute;&oslash;&ecirc;&agrave; &auml;&euml;&yuml; &icirc;&aacute;&uacute;&yuml;&acirc;&euml;&aring;&iacute;&egrave;&yuml; &ograve;&egrave;&iuml;&agrave; __closure_think

namespace closure_v1{ //&egrave;&ntilde;&ecirc;&euml;&thorn;&divide;&agrave;&aring;&igrave; &aring;&atilde;&icirc; &egrave;&ccedil; &iuml;&eth;&icirc;&ntilde;&ograve;&eth;&agrave;&iacute;&ntilde;&ograve;&acirc;&agrave; &egrave;&igrave;&aring;&iacute; structure

struct t_closure_think
{
void f(){;}
void f()const{;}
};

typedef (t_closure_think::*__closure_think)();
};//namespace closure_v1

////////////////////////////////////////////////////////////////////////////////
// &Ecirc;&Euml;&Agrave;&Ntilde;&Ntilde;&Ucirc; &Auml;&Euml;&szlig; &Oacute;&Iuml;&ETH;&Agrave;&Acirc;&Euml;&Aring;&Iacute;&Egrave;&szlig; &Icirc;&Aacute;&ETH;&Agrave;&Aacute;&Icirc;&Ograve;&times;&Egrave;&Ecirc;&Icirc;&Igrave; &Aacute;&Aring;&Ccedil; &Agrave;&ETH;&Atilde;&Oacute;&Igrave;&Aring;&Iacute;&Ograve;&Icirc;&Acirc;

template<class TObj>
class t_closure_0_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef void (TObj::*TMemFunc)();

public:
static void call(void* obj,t_think think)
{
assert(obj!=NULL);
assert(think!=NULL);

((*(TObj*)obj).*(*(TMemFunc*)&think))();
}
};

//------------------------------------------------------------------------------
class t_closure_0
{
public: //typedefs
typedef t_closure_0 t_closure;
typedef closure_v1::__closure_think t_think;

//&oacute;&ecirc;&agrave;&ccedil;&agrave;&ograve;&aring;&euml;&uuml; &iacute;&agrave; &ocirc;&oacute;&iacute;&ecirc;&ouml;&egrave;&thorn;, &ccedil;&agrave;&iacute;&egrave;&igrave;&agrave;&thorn;&ugrave;&oacute;&thorn;&ntilde;&yuml; &acirc;&ucirc;&ccedil;&icirc;&acirc;&icirc;&igrave; &igrave;&aring;&ograve;&icirc;&auml;&agrave;
typedef void (*t_caller)(void* obj,t_think);

private:
void* obj;
t_think think;
t_caller caller;

public:
t_closure_0()
:obj(NULL),think(NULL),caller(NULL){;}

t_closure_0(void* obj,t_think _think,t_caller _caller)
:obj(obj),think(_think),caller(_caller){;}

t_closure& operator = (int x)
{
assert(x==0); //&ograve;&icirc;&euml;&uuml;&ecirc;&icirc; NULL
return *this=t_closure();
}

t_closure& operator = (void* pv)
{
assert(pv==0); //&ograve;&icirc;&euml;&uuml;&ecirc;&icirc; NULL
return *this=t_closure();
}

void operator () ()const
{
assert(caller!=NULL);
caller(obj,think);
}

operator bool () const
{
return obj!=NULL && think!=NULL && caller!=NULL;
}

bool operator ! () const
{
return !(bool)*this;
}
};//class t_closure_0

//------------------------------------------------------------------------------
//&ntilde;&icirc;&ccedil;&auml;&agrave;&iacute;&egrave;&aring; &oacute;&ecirc;&agrave;&ccedil;&agrave;&ograve;&aring;&euml;&yuml; &iacute;&agrave; &icirc;&aacute;&eth;&agrave;&aacute;&icirc;&ograve;&divide;&egrave;&ecirc;
template<class TObj>
t_closure_0 make_closure(TObj* obj,void (TObj::*MemFunc)())
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_0_caller<TObj> t_caller;
typedef t_closure_0 t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[10]: Адрес метода
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 23.07.01 03:49
Оценка:
после отправки cтало выглядеть еще хуже :(
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[21]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 23.07.01 18:36
Оценка:
Здравствуйте VladD2, вы писали:

IAZ>> И даже тогда остается покрайне мере несколько ситуаций когда они (реализации) будут кардинально не верны.

VD>Т.е. если есть asm-вставка, то обязательно когда ни будь грохнет? А как же половина MS CRT — написанная на asc-е? И куски из ATL?

Разве мало случаев когда грохает?

VD><skip> а на асм-е я там совсем примитивную вещь делал – 4-е байта копировал. Неужели от этого могут быть какие-то проблемы? Да и спецификация thiscall-а полностью описана. Так что проблема здесь может возникнуть, только если первые 4-е байта указателя на функцию класса будут содержать что ни будь отличное от указателя на функцию.


Да, именно, 4 байта. А если указатель на функцию занимает 8 или 12 байт? Тут прямая зависимость от компилятора. Например, возьмет он и оптимизирует код вызова функции, кастрирует к 4 байтам, там где предполагалось 8. А asm вставку он, разумеется, менять не может. И все, полетел asm-вызов.

VD>Будет возникать проблема с переносимостью разобрались бы с конкретным компилятором и... Ну, а asm здесь вообще не причем напишите такой код хоть на C, хоть на C++, хоть на VB, ровным счетом ни чего не изменится. Здесь налицо зависимость от компилятора.


При переносе на другую платформу (Ex. I64) программу на C/C++ возможно придется просто перекомпилировать. Ну а на asm’e, сами понимаете, переписать.

IAZ>>Средство "указатели на функции экземпляра объекта" подразумевает две вещи:

IAZ>>1. указатель на объект
IAZ>>2. указатель на функцию в _этом_ объекте
IAZ>>Все это присутствует в С++

VD>Ага. Но по отдельности. И указатель на функцию одного класса нельзя нормальными средствами преобразовать в указатель на функцию другого объекта.


При вызове они соединяются :)

IAZ>>Избавивщись от первого мы избавляемся от самого объекта. Так может программировать тогда С? Или использовать глобальные функции, или статические.


VD>Причем здесь C и глобальные функции я не знаю, но именно "от первого" и избавился СтраусТруп при создании своих указателей. Может он подразумевал, что указатели на функции разных классов можно будет попросту приводить друг к другу? Я не знаю но реалии жизни, в лице VC, плюют на все предпложения...


Я это к тому, что в С и глобальные и статические функции можно приводить к void*, а, соответственно, и друг к другу. А в С++ функция принадлежит классу, а вызов осуществляется при помощи объекта этого класса. Как в стандарте написано, так и реализовано. VC++ в этом случае точна.

IAZ>> Интерестно какова сложность проектирования C++ и ATL (MFS & etс.)? Где и кем используется С++ и ATL? В конце концов ATL это всего лишь библиотека которую написали как смогли, на том _подмножестве_ C++ которое знали эти программисты и которое поддерживает этот компилятор.


VD>Ннн-да, думаю, что программисты эти были одними из лучших в мире. По крайней мере, нас с Вами они, похоже, превосходят. Так, что можно не сомневаться, что C++ они знают в совершенстве. И не в реализации дело.

<skip>
VD>И почему Вы думаете, что "ATL это всего лишь библиотека которую НАПИСАЛИ КАК СМОГЛИ", а C++ это незыблемая вещь которую написали бессмертные гении? Страуструп совсем непохож на Моцарта, да и у последнего не все произведения становились бессмертными. Лажался Страуструп (при создании этого языка) не мало. Так что мы всего лишь замазываем за ним дырки. Не, я понимаю, что многих идей просто не было в то время, например сборки мусора, но указатели на функции он попытался перенести в объектно-ориентированную среду, но … но облажался. Получилась никчемная реализация (из хорошей идеи).

Да я и не говорил что C++ это незыблемая вещь которую написали бессмертные гении. Но С++ разрабатывается (он продолжает развиваться) с 1983 года. Причем группой очень умных людей. Помимо Страуструпа, интеллектуальный уровень которого мы объективно не сможем оценить, но достаточно того что он задумал этот язык, там есть и Керниган и Ричи и Кениг, т.е. те кто приложил руку :) к созданию С и Unix. А кто участвовал в проектировании и реализации ATL, MFC, Windows? Черт их знает. Я точно не знаю.
Сборка мусора существует с начала появления информатики, как теории. Где она была впервые реализована, навскидку сейчас не вспомню (Algol, PL, LISP?) но точно до 80-х. А указатели на функции он никуда не переносил, какие в С были такие и остались в С++. Было только принято о не возможности приведения функций-членов к void*. Из-за их потенциональной опастности. Многие "недоработки" и "ошибки" С++ происходят из-за не возможности отойти от С на достаточно большое растояние.

VD>А, за Ваш совет (в другом постинге) спасибо. Правда, пока я не разобрался можно ли из него почерпнуть чего интересного, но все равно спасибо!

VD>Будет время, попробую по колдовать. Может и сгодится идея перебивать оператора ().

Особенно посмотрите на концовку. Это чистый callback и даже немного delegate :). Правда приминим только для С++ программ.

Вам тоже спасибо за интерестный диалог.
С уважением, IAZ.
Кто ищет то всегда найдет!
Re[21]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 23.07.01 18:43
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте IAZ, вы писали:


IAZ>>Сомневаюсь, что такая статья необходима — зачем переливать из пустого в порожнего: так нельзя попробуйте по другому. Это тянет не больше чем на маленький FAQ.


VD>Так это зависит от количества написанного?


Зависит :)

IAZ>>Из-за отсутвия опыта и знаний он может это использовать не поназначению.


VD>Ну, батенька — это уже цензура какая-то! Эдак и про нож можно сказать, что его описывать нельзя — "А вдруг его кто не по назначению использует?". :)


IAZ>>А чтобы охватить все аспекты этой проблемы одной статьи мало.


У меня было два предложения но записанные в одну строку :) Поскольку программер неопытный ему надо представить проблему как можно шире. Я согласен: пусть сам решает.

VD>А, тут Вы ошибаетесь. Думаю одной статейки будет как раз, а если и не влезет, сделаем сериал. :)


VD>С уважением, Влад.


С уважением, IAZ.
Кто ищет то всегда найдет!
Re[10]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 24.07.01 01:23
Оценка:
Здравствуйте Коваленко Дмитрий:

Я тут тоже поизглялся, но результат мне еще не очень нравится. Вызов и инициализация получается красивыми, а вот задание самой функции нет. Так что свой вариант пока не приведу, но Ваш посмотреть очень хочется. То что получилось ... читается очень тяжело. :) Может еще одну попытку? Да через какой нибудь нотпэд?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[22]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 24.07.01 01:47
Оценка:
Здравствуйте IAZ, вы писали:

IAZ>Здравствуйте VladD2, вы писали:


IAZ>>> И даже тогда остается покрайне мере несколько ситуаций когда они (реализации) будут кардинально не верны.

VD>>Т.е. если есть asm-вставка, то обязательно когда ни будь грохнет? А как же половина MS CRT — написанная на asc-е? И куски из ATL?

IAZ>Разве мало случаев когда грохает?


Скажу честно ошибки в ATL и CRT я видел, но чтобы грохало... у меня не разу не случалось.

IAZ>Да, именно, 4 байта. А если указатель на функцию занимает 8 или 12 байт? Тут прямая зависимость от компилятора. Например, возьмет он и оптимизирует код вызова функции, кастрирует к 4 байтам, там где предполагалось 8. А asm вставку он, разумеется, менять не может. И все, полетел asm-вызов.


Ну, 12 это уже фигня, а 8? На, восемь это код расчитан. Главное иметь правильный this. Указатель же все равно будет ровно 4 байта. Это же 32-битный код.

IAZ>При переносе на другую платформу (Ex. I64) программу на C/C++ возможно придется просто перекомпилировать. Ну а на asm’e, сами понимаете, переписать.


"просто перекомпилировать" Ну, ну. Попробуйте на досуге. :) Даже над маленькой будите сидеть часа 3 (если конечно сразу не писать насчитывая на 64-битную переносимость. А asm-ом... да конечно, но его так мало, что по сравнению с другими проблемами...

IAZ>При вызове они соединяются :)


Ага тлько уже поздно :(

IAZ>Я это к тому, что в С и глобальные и статические функции можно приводить к void*, а, соответственно, и друг к другу. А в С++ функция принадлежит классу, а вызов осуществляется при помощи объекта этого класса. Как в стандарте написано, так и реализовано. VC++ в этом случае точна.


Стандарт это бумага, а что в голове у Страуса осталось??? может он просто изложить не сумел? :)


А кто участвовал в проектировании и реализации ATL, MFC, Windows? Черт их знает. Я точно не знаю.

Ну, в MFC люди были знаменитые, но что толку? Слишком тогда была популянрна идея ОО и выращивания всего из одного корня. :)

А авторов ATL я по именам не знаю, но люди точно не ординарные. Красивый код, однако, пишут!

IAZ>Сборка мусора существует с начала появления информатики, как теории. Где она была впервые реализована, навскидку сейчас не вспомню (Algol, PL, LISP?) но точно до 80-х. А указатели на функции он никуда не переносил, какие в С были такие и остались в С++. Было только принято о не возможности приведения функций-членов к void*. Из-за их потенциональной опастности. Многие "недоработки" и "ошибки" С++ происходят из-за не возможности отойти от С на достаточно большое растояние.


"какие в С были такие и остались в С++" НЕ СОГЛАСЕН!!! В С++ идея указателей на функции была УГРОБЛЕНА! Вспомнете как часто испльзуестя callback в C? И подумайте почему он не применяется в C++ (ну, почти).

Что им мешало ввсести возможность создать базовый указатель на функцию любого класса. Ну, 8 байт, ну смещение для класса. Да хоть 16 с this-ом в предачу, но чтобы работать удобно было!
Тут, катати, похожая проблема была с определением размерности и/или инициализированности массивов в VB... Мне дня три (фанаты) докозывали, что это не MS облажался, а КОНЦЕПЦЫЫЫЯ! Ну, и что? Пока спорили MS выпустил бету 1 .Net-а в которой все сделано как я говорил. Так что проблема в том, что есть очень много фанатов которые даже нехотят задумываться над тем, что в их любимой "игрушке" есть недостатки, и в том что сами авторы Ооочень не любят призновать свои ошибки. Ну, что же их понять можно... вот только прощать не хочется. Ведь мучаемся то мы с вами!

IAZ>Особенно посмотрите на концовку. Это чистый callback и даже немного delegate :). Правда приминим только для С++ программ.


Ну, до "delegate" это дело явно не дотягивает. Нужна простая и удобная реализация. Но, кое что возьмем на вооружение.

IAZ>Вам тоже спасибо за интерестный диалог.


И Вам тоже.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[23]: Адрес метода
От: Андрей Тарасевич Беларусь  
Дата: 24.07.01 06:57
Оценка:
Здравствуйте VladD2, вы писали:

IAZ>>Сборка мусора существует с начала появления информатики, как теории. Где она была впервые реализована, навскидку сейчас не вспомню (Algol, PL, LISP?) но точно до 80-х. А указатели на функции он никуда не переносил, какие в С были такие и остались в С++. Было только принято о не возможности приведения функций-членов к void*. Из-за их потенциональной опастности. Многие "недоработки" и "ошибки" С++ происходят из-за не возможности отойти от С на достаточно большое растояние.


VD>"какие в С были такие и остались в С++" НЕ СОГЛАСЕН!!! В С++ идея указателей на функции была УГРОБЛЕНА! Вспомнете как часто испльзуестя callback в C? И подумайте почему он не применяется в C++ (ну, почти).


Ответ очень прост. Всякий раз, когда С++ программист ловит себя на использовании (попытке использования) callback в границах своего кода, он понимает, что угодил в ловушку C-мышления и прошел мимо какого-то соверешенно естественного design или implementation patterna. Callback тут же летит в мусорное ведро, а на его место становится нормальный человеческий указатель на объект некоторого класса.

Трудно представить себе профессионального С++ программиста, в коде которого даже возникший каким-то образом callback живет более одного-двух дней. Он (callback) ему (программисту) просто спать спокойно не даст.

VD>Что им мешало ввсести возможность создать базовый указатель на функцию любого класса. Ну, 8 байт, ну смещение для класса. Да хоть 16 с this-ом в предачу, но чтобы работать удобно было!

VD>Тут, катати, похожая проблема была с определением размерности и/или инициализированности массивов в VB... Мне дня три (фанаты) докозывали, что это не MS облажался, а КОНЦЕПЦЫЫЫЯ! Ну, и что? Пока спорили MS выпустил бету 1 .Net-а в которой все сделано как я говорил. Так что проблема в том, что есть очень много фанатов которые даже нехотят задумываться над тем, что в их любимой "игрушке" есть недостатки, и в том что сами авторы Ооочень не любят призновать свои ошибки. Ну, что же их понять можно... вот только прощать не хочется. Ведь мучаемся то мы с вами!

Это .NET, а не C++. С++ разрабатывался, разрабатывается и будет разрабатываться в соответствии с zero overhead principle. И всякие "композитные" указатели там так же к месту, как команды управления кофемолкой.

И не бывает абсолютных недостатков и абсолютных преимуществ. Все надо рассматривать в контексте. В контексте С++ отсутствие указателей на конкретный метод конкретного экземпляра класса недостатком не является. Тебе такой нужен — возьми и реализуй его сам.

Best regards,
Андрей.
Best regards,
Андрей Тарасевич
Re[23]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 24.07.01 17:39
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте IAZ, вы писали:


IAZ>>Да, именно, 4 байта. А если указатель на функцию занимает 8 или 12 байт? Тут прямая зависимость от компилятора. Например, возьмет он и оптимизирует код вызова функции, кастрирует к 4 байтам, там где предполагалось 8. А asm вставку он, разумеется, менять не может. И все, полетел asm-вызов.


VD>Ну, 12 это уже фигня, а 8? На, восемь это код расчитан. Главное иметь правильный this. Указатель же все равно будет ровно 4 байта. Это же 32-битный код.


Посмотри размер указателя на f() в такой конструкции:
class A
{
public:
virtual int f(){ return 1; }
};

class B : virtual public A
{
public:
virtual int f(){ return 2; }
};

class C : virtual public A
{
public:
virtual int f(){ return 3; }
};

class D : public B, public C, virtual public A
{
public:
virtual int f(){ return 4; }
void sizef()
{
cout << _T("sizeof(D::B::f())==") << sizeof(&B::f) << endl;
cout << _T("sizeof(D::C::f())==") << sizeof(&C::f) << endl;
cout << _T("sizeof(D::f())==") << sizeof(&D::f) << endl;
}
};


VD>Что им мешало ввсести возможность создать базовый указатель на функцию любого класса. Ну, 8 байт, ну смещение для класса. Да хоть 16 с this-ом в предачу, но чтобы работать удобно было!


Должна быть совместимость с C. Там указатель на функцию в структуре был четыре байта, поэтому в С++ в простейших случаях (одиночное не виртуланое наследование) сделано так-же. А удобство работы иногда конфликтует с эффетивностью.

С уважением, IAZ.
Кто ищет то всегда найдет!
Re[24]: Адрес метода
От: IAZ http://iaz.simb.ru
Дата: 24.07.01 17:45
Оценка:
Здравствуйте Андрей Тарасевич, вы писали:

Нельзя не согласиться с вами. Замочим Влада :)) (VladD2, не принимай это как вызов)

С уважением, IAZ.
Кто ищет то всегда найдет!
Re[11]: Адрес метода
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.07.01 04:50
Оценка:
Здравствуйте VladD2, вы писали:

VD>Здравствуйте Коваленко Дмитрий:


VD>Я тут тоже поизглялся, но результат мне еще не очень нравится. Вызов и инициализация получается красивыми, а вот задание самой функции нет. Так что свой вариант пока не приведу, но Ваш посмотреть очень хочется. То что получилось ... читается очень тяжело. :) Может еще одну попытку? Да через какой нибудь нотпэд?


привет еще раз. я вчера отправил тебе этот файл, но не знаю дошел он или как?
Поэтому еще раз но сюда.

////////////////////////////////////////////////////////////////////////////////
//Классы для хранения указателей на объектные обработчики событий
//Аналог объектных указателей на функции из Builder C++
// Коваленко Дмитрий. 5 мая 2001 года.
#ifndef _t_closure_v1_H_
#define _t_closure_v1_H_

//Структура представленного решения ограничена возможностями BCB3
//
//способ использования:
// 1 определяете тип указателя на объектный обработчик
// typedef t_closure_1<int> t_on_change;
// 2 определяете переменную
// t_on_change on_change;
// 3 устанавливаете обработчик
// on_change=make_closure(&x,TX::handle_change);
// где:
// x — объект класса TX
// TX::handle_change — метод с сигнатурой void handle_change(int);
//
// Вся специфика способа вызова (__stdcall/__fastcall) находиться
// в t_closure_xxx_caller. Поэтому t_closure_xxx зависит только от
// набора аргументов и возвращаемого типа.
//
//как сделать лучше:
// BCB3 не хочет получать указатель на метод в качестве аргумента
// шаблона t_closure1_caller.Если бы это удалось сделать то:
// 1 template<class TObj,class TArg1,void (TObj::*MemFunc)(TArg1)>
// class t_closure1_caller;
// 2 в t_closure_1_caller добавляем статический константный член
// sm_MemFunc=MemFunc и используем его в call в качестве указателя на метод
// 3 в t_closure_1_caller::call не передаем think
// 4 исключаем t_closure_1_caller::think за ненадобностью

namespace structure{
////////////////////////////////////////////////////////////////////////////////
//список классов

//вызов процедур
class t_closure_0;

template<class TA1>
class t_closure_1;

//вызов функций
template<class TResult>
class t_closure_r0;

template<class TResult,class TA1,class TA2>
class t_closure_r2;

template<class TResult,class TA1,class TA2,class TA3,class TA4>
class t_closure_r4;

//__stdcall функции
template<class TResult,class TA1,class TA2>
class t_closure_r2_std;

template<class TResult,class TA1,class TA2,class TA3,class TA4>
class t_closure_r4_std;

////////////////////////////////////////////////////////////////////////////////
//Класс-заглушка для объявления типа __closure_think

namespace closure_v1{ //исключаем его из пространства имен structure

struct t_closure_think
{
void f(){;}
void f()const{;}
};

typedef (t_closure_think::*__closure_think)();
};//namespace closure_v1

////////////////////////////////////////////////////////////////////////////////
// КЛАССЫ ДЛЯ УПРАВЛЕНИЯ ОБРАБОТЧИКОМ БЕЗ АРГУМЕНТОВ

template<class TObj>
class t_closure_0_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef void (TObj::*TMemFunc)();

public:
static void call(void* obj,t_think think)
{
assert(obj!=NULL);
assert(think!=NULL);

((*(TObj*)obj).*(*(TMemFunc*)&think))();
}
};

//------------------------------------------------------------------------------
class t_closure_0
{
public: //typedefs
typedef t_closure_0 t_closure;
typedef closure_v1::__closure_think t_think;

//указатель на функцию, занимающуюся вызовом метода
typedef void (*t_caller)(void* obj,t_think);

private:
void* obj;
t_think think;
t_caller caller;

public:
t_closure_0()
:obj(NULL),think(NULL),caller(NULL){;}

t_closure_0(void* obj,t_think _think,t_caller _caller)
:obj(obj),think(_think),caller(_caller){;}

t_closure& operator = (int x)
{
assert(x==0); //только NULL
return *this=t_closure();
}

t_closure& operator = (void* pv)
{
assert(pv==0); //только NULL
return *this=t_closure();
}

void operator () ()const
{
assert(caller!=NULL);
caller(obj,think);
}

operator bool () const
{
return obj!=NULL && think!=NULL && caller!=NULL;
}

bool operator ! () const
{
return !(bool)*this;
}
};//class t_closure_0

//------------------------------------------------------------------------------
//создание указателя на обработчик
template<class TObj>
t_closure_0 make_closure(TObj* obj,void (TObj::*MemFunc)())
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_0_caller<TObj> t_caller;
typedef t_closure_0 t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}

////////////////////////////////////////////////////////////////////////////////
// КЛАССЫ ДЛЯ УПРАВЛЕНИЯ ОБРАБОТЧИКОМ С 1 АРГУМЕНТОМ

template<class TObj,class TA1>
class t_closure_1_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef void (TObj::*TMemFunc)(TA1);

public:
static void call(void* obj,t_think think,TA1 Arg1)
{
assert(obj!=NULL);
assert(think!=NULL);

((*(TObj*)obj).*(*(TMemFunc*)&think))(Arg1);
}
};

//------------------------------------------------------------------------------
template<class TA1>
class t_closure_1
{
public: //typedefs
typedef t_closure_1<TA1> t_closure;
typedef closure_v1::__closure_think t_think;

//указатель на функцию, занимающуюся вызовом метода
typedef void (*t_caller)(void* obj,t_think,TA1);

private:
void* obj;
t_think think;
t_caller caller;

public:
t_closure_1()
:obj(NULL),think(NULL),caller(NULL){;}

t_closure_1(void* obj,t_think _think,t_caller _caller)
:obj(obj),think(_think),caller(_caller){;}

t_closure& operator = (int x)
{
assert(x==0); //только NULL
return *this=t_closure();
}

t_closure& operator = (void* pv)
{
assert(pv==0); //только NULL
return *this=t_closure();
}

void operator () (TA1 arg1)const
{
assert(caller!=NULL);
caller(obj,think,arg1);
}

operator bool () const
{
return obj!=NULL && think!=NULL && caller!=NULL;
}

bool operator ! () const
{
return !(bool)*this;
}
};

//------------------------------------------------------------------------------
//создание указателя на обработчик
template<class TObj,class TA1>
t_closure_1<TA1> make_closure(TObj* obj,void (TObj::*MemFunc)(TA1))
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_1_caller<TObj,TA1> t_caller;
typedef t_closure_1<TA1> t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}

////////////////////////////////////////////////////////////////////////////////
// ВЫЗОВ ФУНКЦИИ-ЧЛЕНА БЕЗ АРГУМЕТОВ

template<class TObj,class TResult>
class t_closure_r0_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef TResult (TObj::*TMemFunc)();

public:
static TResult call(void* obj,t_think think)
{
assert(obj!=NULL);
assert(think!=NULL);

return ((*(TObj*)obj).*(*(TMemFunc*)&think))();
}
};

//------------------------------------------------------------------------------
template<class TResult>
class t_closure_r0
{
public: //typedefs
typedef t_closure_r0<TResult> t_closure;
typedef closure_v1::__closure_think t_think;

//указатель на функцию, занимающуюся вызовом метода
typedef TResult (*t_caller)(void* obj,t_think);

private:
void* obj;
t_think think;
t_caller caller;

public:
t_closure_r0()
:obj(NULL),think(NULL),caller(NULL){;}

t_closure_r0(void* obj,t_think _think,t_caller _caller)
:obj(obj),think(_think),caller(_caller){;}

t_closure& operator = (int x)
{
assert(x==0); //только NULL
return *this=t_closure();
}

t_closure& operator = (void* pv)
{
assert(pv==0); //только NULL
return *this=t_closure();
}

TResult operator () ()const
{
assert(caller!=NULL);
return caller(obj,think);
}

operator bool () const
{
return obj!=NULL && think!=NULL && caller!=NULL;
}

bool operator ! () const
{
return !(bool)*this;
}
};//class t_closure_r0

//------------------------------------------------------------------------------
//создание указателя на обработчик
template<class TObj,class TResult>
t_closure_r0<TResult> make_closure(TObj* obj,TResult (TObj::*MemFunc)())
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_r0_caller<TObj,TResult> t_caller;
typedef t_closure_r0<TResult> t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}

////////////////////////////////////////////////////////////////////////////////
// ВЫЗОВ ФУНКЦИИ-ЧЛЕНА С 2 АРГУМЕНТАМИ

template<class TObj,class TResult,class TA1,class TA2>
class t_closure_r2_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef TResult (TObj::*TMemFunc)(TA1,TA2);

public:
static TResult call(void* obj,t_think think,TA1 a1,TA2 a2)
{
assert(obj!=NULL);
assert(think!=NULL);

return ((*(TObj*)obj).*(*(TMemFunc*)&think))(a1,a2);
}
};

//------------------------------------------------------------------------------
template<class TResult,class TA1,class TA2>
class t_closure_r2
{
public: //typedefs
typedef t_closure_r2<TResult,TA1,TA2> t_closure;
typedef closure_v1::__closure_think t_think;

//указатель на функцию, занимающуюся вызовом метода
typedef TResult (*t_caller)(void* obj,t_think,TA1,TA2);

private:
void* obj;
t_think think;
t_caller caller;

public:
t_closure_r2()
:obj(NULL),think(NULL),caller(NULL){;}

t_closure_r2(void* obj,t_think _think,t_caller _caller)
:obj(obj),think(_think),caller(_caller){;}

t_closure& operator = (int x)
{
assert(x==0); //только NULL
return *this=t_closure();
}

t_closure& operator = (void* pv)
{
assert(pv==0); //только NULL
return *this=t_closure();
}

TResult operator () (TA1 a1,TA2 a2)const
{
assert(caller!=NULL);
return caller(obj,think,a1,a2);
}

operator bool () const
{
return obj!=NULL && think!=NULL && caller!=NULL;
}

bool operator ! () const
{
return !(bool)*this;
}
};//class t_closure_r2

//------------------------------------------------------------------------------
//создание указателя на обработчик
template<class TObj,class TResult,class TA1,class TA2>
t_closure_r2<TResult,TA1,TA2> make_closure(TObj* obj,TResult (TObj::*MemFunc)(TA1,TA2))
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_r2_caller<TObj,TResult,TA1,TA2> t_caller;
typedef t_closure_r2<TResult,TA1,TA2> t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}

////////////////////////////////////////////////////////////////////////////////
// ВЫЗОВ ФУНКЦИИ-ЧЛЕНА С 4 АРГУМЕНТАМИ

template<class TObj,class TResult,class TA1,class TA2,class TA3,class TA4>
class t_closure_r4_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef TResult (TObj::*TMemFunc)(TA1,TA2,TA3,TA4);

public:
static TResult call(void* obj,t_think think,TA1 a1,TA2 a2,TA3 a3,TA4 a4)
{
assert(obj!=NULL);
assert(think!=NULL);

return ((*(TObj*)obj).*(*(TMemFunc*)&think))(a1,a2,a3,a4);
}
};

//------------------------------------------------------------------------------
template<class TResult,class TA1,class TA2,class TA3,class TA4>
class t_closure_r4
{
public: //typedefs
typedef t_closure_r4<TResult,TA1,TA2,TA3,TA4> t_closure;
typedef closure_v1::__closure_think t_think;

//указатель на функцию, занимающуюся вызовом метода
typedef TResult (*t_caller)(void* obj,t_think,TA1,TA2,TA3,TA4);

private:
void* obj;
t_think think;
t_caller caller;

public:
t_closure_r4()
:obj(NULL),think(NULL),caller(NULL){;}

t_closure_r4(void* obj,t_think _think,t_caller _caller)
:obj(obj),think(_think),caller(_caller){;}

t_closure& operator = (int x)
{
assert(x==0); //только NULL
return *this=t_closure();
}

t_closure& operator = (void* pv)
{
assert(pv==0); //только NULL
return *this=t_closure();
}

TResult operator () (TA1 a1,TA2 a2,TA3 a3,TA4 a4)const
{
assert(caller!=NULL);
return caller(obj,think,a1,a2,a3,a4);
}

operator bool () const
{
return obj!=NULL && think!=NULL && caller!=NULL;
}

bool operator ! () const
{
return !(bool)*this;
}
};//class t_closure_r4

//------------------------------------------------------------------------------
//создание указателя на обработчик
template<class TObj,class TResult,class TA1,class TA2,class TA3,class TA4>
t_closure_r4<TResult,TA1,TA2,TA3,TA4> make_closure(TObj* obj,TResult (TObj::*MemFunc)(TA1,TA2,TA3,TA4))
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_r4_caller<TObj,TResult,TA1,TA2,TA3,TA4> t_caller;
typedef t_closure_r4<TResult,TA1,TA2,TA3,TA4> t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}

////////////////////////////////////////////////////////////////////////////////
// ВЫЗОВ STDCALL ФУНКЦИИ-ЧЛЕНА С 2 АРГУМЕНТАМИ

template<class TObj,class TResult,class TA1,class TA2>
class t_closure_r2_std_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef TResult __stdcall (TObj::*TMemFunc)(TA1,TA2);

public:
static TResult call(void* obj,t_think think,TA1 a1,TA2 a2)
{
assert(obj!=NULL);
assert(think!=NULL);

return ((*(TObj*)obj).*(*(TMemFunc*)&think))(a1,a2);
}
};

//------------------------------------------------------------------------------
//создание указателя на обработчик
template<class TObj,class TResult,class TA1,class TA2>
t_closure_r2<TResult,TA1,TA2> make_closure(TObj* obj,TResult __stdcall (TObj::*MemFunc)(TA1,TA2))
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_r2_std_caller<TObj,TResult,TA1,TA2> t_caller;
typedef t_closure_r2<TResult,TA1,TA2> t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}

////////////////////////////////////////////////////////////////////////////////
// ВЫЗОВ STDCALL ФУНКЦИИ-ЧЛЕНА С 4 АРГУМЕНТАМИ

template<class TObj,class TResult,class TA1,class TA2,class TA3,class TA4>
class t_closure_r4_std_caller
{
public: //typedefs
typedef closure_v1::__closure_think t_think;

typedef TResult __stdcall (TObj::*TMemFunc)(TA1,TA2,TA3,TA4);

public:
static TResult call(void* obj,t_think think,TA1 a1,TA2 a2,TA3 a3,TA4 a4)
{
assert(obj!=NULL);
assert(think!=NULL);

return ((*(TObj*)obj).*(*(TMemFunc*)&think))(a1,a2,a3,a4);
}
};

//------------------------------------------------------------------------------
//создание указателя на обработчик

template<class TObj,class TResult,class TA1,class TA2,class TA3,class TA4>
t_closure_r4<TResult,TA1,TA2,TA3,TA4> make_closure_ex(TObj* obj,TResult __stdcall (TObj::*MemFunc)(TA1,TA2,TA3,TA4))
{
typedef closure_v1::__closure_think t_think;
typedef t_closure_r4_std_caller<TObj,TResult,TA1,TA2,TA3,TA4> t_caller;
typedef t_closure_r4<TResult,TA1,TA2,TA3,TA4> t_closure;

return t_closure(obj,*(t_think*)&MemFunc,t_caller::call);
}

////////////////////////////////////////////////////////////////////////////////
};//namespace structure
#endif
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[12]: Адрес метода
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 25.07.01 05:19
Оценка:
Мама моя родная как я утомился читать эти ветки про адрес метода
господа — переходите на BCB. там есть клевая вещь __closure которая ваше желание с лихвой выполняет. предложенное выше было создано для переноса одной библиотеки на VC. как оказалось зря изголялся — STL на VС6 просто отстой.

А иделогия в этом коде проста до безобразия — с помощью шаблонов генеритуется код длz
1 функции (void*, список аргуметов)
2 классов, которые хранят указатель на объект/указатель на функцию из пункта 1.

но компиляторы с шаблонами просто хреново работают. говорят что BCB5 все чудесно обрабатывает,
но это пока до него не доберешься :)
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[12]: Адрес метода
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.07.01 17:03
Оценка:
Здравствуйте Коваленко Дмитрий, вы писали:

Ссенькаю! Код прикопал. Будет свободное время и свежая башка разберусь. Думаю по этому вопросу мжно новый поток открывать. :)
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Адрес метода
От: Аноним  
Дата: 20.02.06 09:06
Оценка:
Здравствуйте, Alexander Shargin, Вы писали:


class A
{
int m_a;
...
};

class B;
{
int m_b;
...
};

class C : A, B;
{
int m_c;
...
};


AS>Но теперь функции класса B, которые понятия не имеют о существовании класса C, используют для доступа к члену m_b адрес this+0. Если вызвать функцию класса B для класса C, он будет работать с членом A::m_a по адресу this+0, как будто это B::m_b. Вот почему для обеспечения корректности программы приходится прибавлять смещение к указателю this при использовании функций класса B для объекта C. Поэтому в указателе на функцию для класса C появляется дополнительное поле — смещение. Для любых функций из классов A и C это смещение будет равно нулю, для функций класса B — четырём.


может класса А?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.