2016-09-18 4 views
1

Возможно ли (и как) получить индекс виртуальной функции в таблице виртуальных методов?C++ Получить таблицу виртуальных функций.

class A 
{ 
    virtual void foo(); 
} 

Я знаю foo является первым (0) элемент в таблице виртуальных методов

Однако я могу иметь foo и получить 0?

+1

Что вы пытаетесь достичь? – DeiDei

+4

Я подозреваю, что это реализация определена и зависит от ABI, поэтому (теоретически) нет в переносном режиме. – skypjack

+1

Возможный дубликат [Как получить каждый индекс виртуальной функции, как это делает компилятор?] (Http://stackoverflow.com/questions/3062647/how-to-get-every-virtual-function-index-just-as- the-compiler-does) – DeiDei

ответ

1

Как я уже сказал в комментариях, я предполагаю, что реализация определена (подробнее об этом написано в статье @SteveJessop за то, что она указала правильные условия в комментариях к ответу) и зависит от ABI, поэтому (теоретически) это невозможно в переносном режиме.
В качестве примера известного определения ABI см. here (Itanium C++ ABI).
В других терминах это терминология жаргона в стандарте C++ и означает - . Реализация должна документировать его.
Кроме того, как @ n.m. упомянутый в комментариях к вопросу, стандарт не содержит ничего, как vtable, поэтому вряд ли он управляется явно.
Реализации могут использовать их или нет, и если они будут использовать их, они могут предоставлять поддерживаемые средства для доступа к ним для доступа к ним.

Это еще раз: нет явного, портативного способа сделать это.

1

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

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

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

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

В качестве примечания, как GCC, так и MSVC++ документированы алгоритмы компоновки для их vtable и документированные алгоритмы для того, где vptr находится в объекте. Для GCC документацией является Common C++ ABI (a.k.a. Itanium C++ ABI). Для MSVC++ я не знаю, где находится документация или существует ли она напрямую, но компилятор гарантирует, что по меньшей мере классы без элементов данных будут выложены для совместимости с COM ABI.

-1

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

#include <iostream> 
#include <stdio.h> 
using namespace std; 

class test { 
public: 
    virtual void foo() { 
    cout << "foo" << endl; 
    } 
    virtual void goo() { 
    cout << "goo" << endl; 
    } 
    virtual void hoo() { 
    cout << "hoo" << endl; 
    } 
}; 

int find_function_index(test* p, void* f) { 
    for(int i = 0; i < 3; ++i) { 
    void* tmp = (void*)*((long*)*(int*)(p)+i); 
    if(tmp == f) 
     return i; 
    } 
    return -1; 
} 

int main() { 
    test* p = new test(); 

    void* f1 = reinterpret_cast<void*>(&test::foo); 
    cout << "foo: " << find_function_index(p, f1) << endl; 

    void* f2 = reinterpret_cast<void*>(&test::goo); 
    cout << "goo: " << find_function_index(p, f2) << endl; 

    void* f3 = reinterpret_cast<void*>(&test::hoo); 
    cout << "hoo: " << find_function_index(p, f3) << endl; 
} 

Следующая рис является адресом теста :: слизи и значение f2 (что хранить правильный адрес теста :: слизи) enter image description here

+0

Этот код не имеет никакого смысла. Это * не * Python; выполнение 'p-> pf1' не будет разрешено« привязанным »экземпляром метода, а _even, если он сделал_, он не может быть назначен типу' void (* func)() '. Вам нужно хранить где-нибудь скрытый аргумент 'test * const this'. В лучшем случае функция-член 'void (test :: *)()' _might_ может быть назначена 'void (*) (test *)', но даже это не гарантируется, потому что способ передать указатель 'this' (ABI) может отличаться между этими двумя вызовами. –

+0

PS: если вы хотите сохранить «привязанный вызов» к методу, вам нужно использовать либо функтор, либо лямбда: 'std :: bind (std :: mem_fn (& test :: foo), p)' или ' [p]() {p-> foo(); } 'оба будут работать, но ни одно из них не может быть присвоено' void (*)() ', потому что оба используют объект для хранения чего-либо (копия указателя p). –

+0

@ Javier, в случае с skypjack, ему просто нужно знать адрес виртуальной функции, а затем получить адрес функций в vtable в порядке. Зачем нужен тест * const this? Все объекты одного типа должны указывать на одну и ту же виртуальную таблицу, верно? Код, который я пробовал как f1(), f2(), f3(), если внутри этих функций с использованием любой переменной-члена, конечно, будет неопределенное поведение. Не предполагайте, вы можете попробовать код и проверить разборку самостоятельно. –

0

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

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

#include <iostream> 

struct A 
{ 
    virtual void foo() { std::cout << "A::foo\n"; } 
}; 

struct AA : A 
{ 
    virtual void foo() { std::cout << "AA::foo\n"; } 
}; 

struct AAA : AA 
{ 
    virtual void foo() { std::cout << "AAA::foo\n"; } 
}; 

void bar (A& a, void (A::* pMem)()) 
{ 
    (a.*pMem)(); 
} 

int main() { 
    A a; 
    AA aa; 
    AAA aaa; 

    bar (a, &A::foo); 
    bar (aa, &A::foo); 
    bar (aaa, &A::foo); 

    return 0; 
}