Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: 1 [2]  Все   Вниз
  Печать  
Автор Тема: Номер виртуальной функции в таблице виртуальных функций  (Прочитано 45064 раз)
0 Пользователей и 3 Гостей смотрят эту тему.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #30 : 20-11-2010 18:14 » 

имя он явно промазал ) Подправлено
Записан

mag
Интересующийся

ru
Offline 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 » Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #32 : 22-11-2010 08:57 » 

mag, хм, его за это имя как раз и забанил. Ну и за неинтерактивность. Зря ?
Записан

mag
Интересующийся

ru
Offline Offline
Пол: Мужской

« Ответ #33 : 22-11-2010 09:19 » 

Алексей1153++, Я уж не знаю досконально всех правил данного форума, но если не вдаваться в тонкости реализации и возможной тупиковости данного направления (кто-знает как будут выглядеть указатели на функции класса уже завтра или даже сегодня с другими флажками компилятора?), то это сообщение давало явный ответ на явный вопрос в заголовке темы.
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #34 : 22-11-2010 09:31 » 

значит, я поторопился (

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

mag
Интересующийся

ru
Offline 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 » Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #36 : 22-11-2010 18:05 » 

mag,
vydtdrt

бан отменил, почту щас в ЛС тебе напишу
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #37 : 23-11-2010 05:57 » new

Даёт в результате корректный но неправильный лог (перегруженная функция должна идти раньше)
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
Интересующийся

ru
Offline 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
Интересующийся

ru
Offline 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;
};

Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline 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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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
Интересующийся

ru
Offline Offline
Пол: Мужской

« Ответ #42 : 23-11-2010 14:09 » 

Антон (LogRus), да уж... Впрочем, я высказывал изрядную долю скепсиса при использовании таких указателей и в моём (vydtdrt) варианте.

>> Как видишь реализация, очень специфична для платформы
Меня радует только то, что и в 6-й студии всё так же как 2008. Глядишь и впредь ничего ломать не станут. Можно тебя ещё попросить посмотреть в дебагере на саму виртуальную таблицу класса I2 (точнее придётся реализовать производный A2 чтобы создать экземпляр)? А то получается что для перегруженного имени 'a' функция a(int) располагается в конце списка, а не перед функцией a().
Записан

Чего-бы ты ни сделал, ты всегда чего-нибудь не сделал...
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #43 : 23-11-2010 15:28 » 

сделал пару попыток поглядеть на табличку, её что-то не видно в моём IDE, а другого нет.
Записан

Странно всё это....
vydtdrt
Новенький

ru
Offline 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
Для продолжения нажмите любую клавишу . . .
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #45 : 02-12-2010 08:38 » 

vydtdrt, извиняюсь за неправильный бан. Но ты сам в чём-то виноват - ник корявый, по человечьи не говоришь, код страшный
Записан

sss
Специалист

ru
Offline Offline

« Ответ #46 : 03-12-2010 06:26 » 

Интересно... Узкое место - если у исходного объекта есть структуры не кратные ( и не выравненные) размеру указателя... А так в принципе норм..  
Записан

while (8==8)
Страниц: 1 [2]  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines