Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #30 : 20-11-2010 18:14 » |
|
имя он явно промазал ) Подправлено
|
|
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #31 : 22-11-2010 08:35 » |
|
Антон (LogRus), объясни чего я недопонял в первом варианте. >> 0. ... (со всеми типами и именами, можно макрос для упрощения задачи)... далее тест поочереди дёргает каждый из методов заглушки Пусть исходный интерфейс ITest содержит одну функцию. Тогда его проверка class CTest : public ITest { void F () { printf ("void F ();\n" } };
// ... CTest ct1; ITest * aTest = & ct1; aTest->F ();
и в логе получаем void F (); Далее во второй версии добавляем virtual void F (int) = 0; class CTest : public ITest { void F () { printf ("void F ();\n" } void F (int) { printf ("void F (int);\n" } };
Его проверка CTest ct1; ITest * aTest = & ct1; aTest->F (); aTest->F ((int)0);
Даёт в результате корректный но неправильный лог (перегруженная функция должна идти раньше) void F (); void F (int); Алексей1153++ за имена спасибо. И сорри автору. А куда делось сообщение (имя не смогу воспроизвести) о функции явного получения номера функции? После некоторой его доработки мне удалось получить и номера для функций с номером выше 32?
|
|
« Последнее редактирование: 22-11-2010 08:40 от mag »
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #32 : 22-11-2010 08:57 » |
|
mag, хм, его за это имя как раз и забанил. Ну и за неинтерактивность. Зря ?
|
|
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #33 : 22-11-2010 09:19 » |
|
Алексей1153++, Я уж не знаю досконально всех правил данного форума, но если не вдаваться в тонкости реализации и возможной тупиковости данного направления (кто-знает как будут выглядеть указатели на функции класса уже завтра или даже сегодня с другими флажками компилятора?), то это сообщение давало явный ответ на явный вопрос в заголовке темы.
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #34 : 22-11-2010 09:31 » |
|
значит, я поторопился (
ну а указатели будут выглядеть так же - хранилище адреса. Тут, мне кажется, ничего не поменяешь
|
|
|
Записан
|
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #35 : 22-11-2010 10:35 » |
|
Алексей1153++, >> ну а указатели будут выглядеть так же - хранилище адреса. Тут, мне кажется, ничего не поменяешь Я не совсем это имел ввиду. Речь идёт об указателях на функцию класса, в том числе виртуальную, которая представляет собой: void (ITest:: * pFunc) () = &ITest::F; В результате pFunc указывает на адрес, где расположена функция для вызова функции F, с учётом подготовки параметров, передачи this и смещения данной функции в виртуальной таблице. В виртуальной таблице всё останется как и прежде (в том числе благодаря COM'у), а вот это место вызова может изменяться. Как изменяется оно сейчас, например, для Debug и Release версии.
Если нельзя вернуть удалённые сообщения, то просьба опубликовать хотя бы имя этого респондента. Я опубликую свой вариант функции и выражу ему свою благодарность.
|
|
« Последнее редактирование: 22-11-2010 10:41 от mag »
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #36 : 22-11-2010 18:05 » |
|
mag, vydtdrt
бан отменил, почту щас в ЛС тебе напишу
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #37 : 23-11-2010 05:57 » |
|
Даёт в результате корректный но неправильный лог (перегруженная функция должна идти раньше) void F (); void F (int);
задача стоит очень просто: проверить, что дёргая методы классы через старый интерфейс будут дёргаться именно те методы которые дёргались и ранее, поэтому: 1. порядок в котором ты вызываешь методы не имеет значения для теста 2. твой код тест может работать не верно, т.к. компилятору в этой точке доступна реализация класса и он с радостью забивает на механизм виртуализации, именно по этому я предложил, разделить тест на 2 модуля. еще раз у нас есть 4 модуля: 1. тест для старого интерфейса 2. длл реализующая старый интерфейс 3. тест для нового интерфейса 4. длл реализующая новый интерфейс запускаем 1+2, затем 1+3, далее сравниваем логи запускаем 1+4 смотрим, что не падает на каких-то формальных проверках (которые захочется добавить) что касается указателей на методы класса (что-то в первый раз в них смутило, ну да ладно), то вот примерчик #include <iostream> #include <sys/time.h>
class I { public: virtual void a() = 0; virtual void b() = 0; virtual void c() = 0; virtual void a(int) = 0; };
class A : public I { public: virtual void c() {}; virtual void b() {}; virtual void a() {}; virtual void a(int) {}; };
int main (int argc, char * const argv[]) {
typedef void(I::*PI)(); PI ia = &I::a; PI ib = &I::b; PI ic = &I::c;
typedef void(A::*PA)(); PA aa = &A::a; PA ab = &A::b; PA ac = &A::c; std::cout << sizeof(ia) << "\n"; printf("I::a=%lld I::b=%lld I::c=%lld\n", ia, ib, ic); printf("A::a=%lld A::b=%lld A::c=%lld\n", aa, ab, ac); return 0; }
у меня 64-х битная платформа и GCC, поэтому указатели 64 бита и соотвествующий им printf. не сложными вычислениями получаем номер метода n = (aa - 1)/sizeof(aa), отсчёт от нуля но по мне, это не верный способ для промышленного ПО, но как способ раньнего оповещения о возможных проблемах вполне себе метод.
|
|
|
Записан
|
Странно всё это....
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #38 : 23-11-2010 06:43 » |
|
В результате некоторых изысканий для решения исходной задачи был найден следующий вариант. Проверено для VS-2008, в том числе для статических и невиртуальных функций класса. Особая благодарность vydtdrt - это его идея. Я её лишь слегка доработал. Впрочем, все вопросы на тему "почему это работает?" тоже к нему. Привожу свой вариант функции, так как исходный текст содержал операторы типа *++++++++f и *o ^= *o, которые являясь совершенно законными с точки зрения языка (и возможно более эффективные с точки зрения быстродействия) понятны далеко не всякому программисту. bool GetFunctionIndex (int * pIndex, ...) { va_list argList; va_start (argList, pIndex); unsigned char * pCall = (unsigned char*) va_arg(argList, void *); #ifdef _DEBUG pCall += 5 + *(int*)& pCall [1]; #endif switch (pCall [3]) { case 0x20: // Так вызывается функция с индексом 0 *pIndex = 0; break; case 0x60: // Так вызывается функция с индексами 1...31 *pIndex = pCall [4] >> 2; break; case 0xA0: // Так вызывается функция с индексом 32 и более *pIndex = (*(unsigned int*)& pCall [4]) >> 2; break; default: // Неизвестный способ. В том числе невиртуальная функция return false; } return true; }
Для получения индекса функции вызов функции производится следующим образом: GetFunctionIndex (& nIndex, &ITest::F);
Если имя функции перегружено, то лучше завести указатель на функцию // Наиболее корректный вариант void (ITest:: * pFunc) (int) = &ITest:: F; GetFunctionIndex (& nIndex, pFunc);
Использовать явное преобразование типа крайне нерекомендуется, так как в этом случае можно получить индекс функции, которой на самом деле нет в интерфейсе. GetFunctionIndex (& nIndex, (void (ITest:: *) ()) &ITest::F); GetFunctionIndex (& nIndex, (void (ITest:: *) const ()) &ITest::F); GetFunctionIndex (& nIndex, (void (ITest:: *) (int)) &ITest::F);
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #39 : 23-11-2010 07:20 » |
|
Антон (LogRus), а можешь указать то, что печатается у тебя в терминал? В релизной и дебагерной версии? Дело в том, что как я уже отмечал ранее &I::a есть не указатель на функцию void I::a() (кстати это должен быть 0!!!), а указатель на функцию, которая готовит аргументы и затем вызывает виртуальную функцию из таблицы с прописанным в таблице индексом. У меня в VS 2008 это выглядит так (я печатал по формату %p): Debug: 4 ::a=01043D02 I::b=01043D3E I::c=01045DA5 ::a=01043D02 A::b=01043D3E A::c=01045DA5 Release: 4 I::a=00EB1D30 I::b=00EB1D50 I::c=00EB1D70 A::a=00EB1D30 A::b=00EB1D50 A::c=00EB1D70 Боюсь, что разумным образом интерпретировать и преобразовать это я не смогу. >> у меня 64-х битная платформа и GCC, поэтому указатели 64 бита и соотвествующий им printf. Возможно, что в 64-x GCC по данному указателю (или первым его байтам) хранится именно сдвиг в виртуальной таблице >> не сложными вычислениями получаем номер метода n = (aa - 1)/sizeof(aa), отсчёт от нуля Тогда там не нужно вычитать 1, так как нулевой функцией должна быть void a (int); Кстати, насколько я могу судить, в твоем примере класс A совершенно не нужен. Ты не создаёшь объекты данного класса. Впрочем не видя результатов логов я могу ошибаться. А ещё мне бы хотелось увидеть твой лог для двух классов I1 и I2. Именно в таком виде без наследования. Классы A1 и A2 не нужны class I1 { public: virtual void a() = 0; virtual void b() = 0; virtual void c() = 0; }; class I2 { public: virtual void a() = 0; virtual void b() = 0; virtual void c() = 0; virtual void a(int) = 0; };
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #40 : 23-11-2010 07:53 » |
|
Offtopic:
>>текст содержал операторы типа *++++++++f и *o ^= *o, которые являясь совершенно законными с точки зрения языка (и возможно >>более эффективные с точки зрения быстродействия) понятны далеко не всякому программисту.
эффективности они не добавляют - а вот понтов и трудночитаемости да. Одна из причин моей реакции, кстати. Так как это признак гоблинства
например, *o ^= *o, легко заменяется на *o=0; а *++++++++f на
f+=4; *f
да и вообще, f лучше сделать типом void*
Поставлю в угол.
|
|
« Последнее редактирование: 23-11-2010 07:55 от Алексей1153++ »
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #41 : 23-11-2010 09:29 » |
|
Антон (LogRus), а можешь указать то, что печатается у тебя в терминал? В релизной и дебагерной версии? Дело в том, что как я уже отмечал ранее &I::a есть не указатель на функцию void I::a() (кстати это должен быть 0!!!), а указатель на функцию, которая готовит аргументы и затем вызывает виртуальную функцию из таблицы с прописанным в таблице индексом.
I1::a=1 I1::b=5 I1::c=9 I2::a=1 I2::b=5 I2::c=9 I2::a(int)=13
Как видишь реализация, очень специфична для платформы
|
|
|
Записан
|
Странно всё это....
|
|
|
mag
Интересующийся
Offline
Пол:
|
|
« Ответ #42 : 23-11-2010 14:09 » |
|
Антон (LogRus), да уж... Впрочем, я высказывал изрядную долю скепсиса при использовании таких указателей и в моём (vydtdrt) варианте.
>> Как видишь реализация, очень специфична для платформы Меня радует только то, что и в 6-й студии всё так же как 2008. Глядишь и впредь ничего ломать не станут. Можно тебя ещё попросить посмотреть в дебагере на саму виртуальную таблицу класса I2 (точнее придётся реализовать производный A2 чтобы создать экземпляр)? А то получается что для перегруженного имени 'a' функция a(int) располагается в конце списка, а не перед функцией a().
|
|
|
Записан
|
Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
|
|
|
Антон (LogRus)
|
|
« Ответ #43 : 23-11-2010 15:28 » |
|
сделал пару попыток поглядеть на табличку, её что-то не видно в моём IDE, а другого нет.
|
|
|
Записан
|
Странно всё это....
|
|
|
vydtdrt
Новенький
Offline
|
|
« Ответ #44 : 02-12-2010 07:30 » |
|
#include <stdio.h> #include <stdarg.h> #include <psapi.h>
#include <stdio.h> #include <iostream> #include <dbghelp.h>
#include <ntdll.h>
#pragma comment(lib, "psapi") #pragma comment(lib, "shlwapi") #pragma comment(lib, "dbghelp")
#define crash_if_fail(expr) \ do{ \ if (!(expr)) \ DebugBreak(); \ } while (0)
#define return_val_if_fail(expr, val) \ do{ \ if (!(expr)) \ return val; \ } while (0)
bool is_stack(void *p){ PTEB teb = NtCurrentTeb(); void *base = teb->Tib.StackBase; void *limit = teb->Tib.StackLimit; crash_if_fail(limit && base); if (p >= limit && p <= base) return true; return false; }
bool is_code_segment__(void *p){ char buff[MAX_PATH]; char *base; DWORD r = GetMappedFileName(GetCurrentProcess(), (HMODULE)p, buff, _countof(buff)); return_val_if_fail(r, false); char *name = PathFindFileName(buff); return_val_if_fail(name != buff, false); base = (char*)GetModuleHandle(name); return_val_if_fail(base, false); PIMAGE_NT_HEADERS pim; pim = ImageNtHeader(base); return_val_if_fail(pim, false); PIMAGE_SECTION_HEADER psh = (PIMAGE_SECTION_HEADER)(&pim->OptionalHeader + 1); for (int jj = 0; jj < pim->FileHeader.NumberOfSections; jj++){ char *q = base + pim->OptionalHeader.AddressOfEntryPoint; bool t = q >= base + psh->VirtualAddress && q <= base + psh->VirtualAddress + psh->Misc.VirtualSize; if (t){ t = p >= base + psh->VirtualAddress && p <= base + psh->VirtualAddress + psh->Misc.VirtualSize; return_val_if_fail(!t, true); } psh++; } return false; }
bool is_correct_prologue(void *p){ return !!"dummy"; }
class r{ r(const r&); r& operator=(const r&); public: r(){} template<class t> r& operator,(t *v){ return this->operator<<(*v); } template<class t> r& operator<<(t &o){ crash_if_fail(dynamic_cast<const void*>(&o)); int l = 0; int n = 0, n_ = sizeof(o) / sizeof(void*); while (n < n_){ void **a = *((void***)&o + n++); if (IsBadReadPtr(a, sizeof(void*)) || is_stack(a)){ continue; } int c = 0; void *d = a[c]; while (is_code_segment__(d)){ print_info(&l, o, n, d, c, a); d = a[++c]; } } return *this; } template<class t> void print_info(int *l, const t &o, int n, void *d, int c, void **a){ if (!*l){ puts("///////////////////////////////////////////////////"); printf("object address %p\n", &o); *l = 1; } if (!c){ puts("-=-=-=-=-=-=-=-=-=-"); printf("%15.1s%p:__vfptr %p\n", "+", ((char*)((void***)&o + n - 1)) - (char*)&o, a); } printf("%32.p\n", d); } };
class a{ public: virtual void f(){} virtual ~a(){} }; class b:public a{ public: virtual void f(){} virtual ~b(){} };
int main(){ a ao; b bo; r()<<ao<<bo; }
/////////////////////////////////////////////////// object address 0013FF54 -=-=-=-=-=-=-=-=-=- +00000000:__vfptr 0041780C 0041104B 004110B9 /////////////////////////////////////////////////// object address 0013FF48 -=-=-=-=-=-=-=-=-=- +00000000:__vfptr 0041781C 00411046 0041122B Для продолжения нажмите любую клавишу . . .
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #45 : 02-12-2010 08:38 » |
|
vydtdrt, извиняюсь за неправильный бан. Но ты сам в чём-то виноват - ник корявый, по человечьи не говоришь, код страшный
|
|
|
Записан
|
|
|
|
sss
Специалист
Offline
|
|
« Ответ #46 : 03-12-2010 06:26 » |
|
Интересно... Узкое место - если у исходного объекта есть структуры не кратные ( и не выравненные) размеру указателя... А так в принципе норм..
|
|
|
Записан
|
while (8==8)
|
|
|
|