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
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.