| 
			| 
					
						| Алексей++ 
								глобальный и пушистыйГлобальный модератор    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=9I2::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)  |  |  | 
	|  |