Как определить, имеет ли класс C виртуальную таблицу?

#c #vtable

#c #vtable

Вопрос:

Мой друг отправил мне следующую задачу ранее сегодня:

Учитывая следующий код, предложите реализацию, OBJECT_HAS_VTABLE чтобы программа печатала AnObject has a vtable = 0, AnObjectWithVTable has a vtable = 1 .

 class AnObject
{
    int m_a;
    void DoSomething() {}

public: 
    AnObject() {m_a = 0;}
};

class AnObjectWithVTable
{
    int m_b;
    virtual void DoStuff() { }

public: 
    AnObjectWithVTable() {m_b = 0;}
};

void main()
{
    printf("AnObject has a vtable = %i, AnObjectWithVTable has a vtable = %in",
           OBJECT_HAS_VTABLE(AnObject),
           OBJECT_HAS_VTABLE(AnObjectWithVTable));
}
  

Я придумал следующее решение, которое, на мой взгляд, является достаточно приличным:

 template <typename T>
bool objectHasVtable()
{
    class __derived : public T {};
    T t;
    __derived d;

    void *vptrT=*((void **)amp;t);
    void *vptrDerived=*((void **)amp;d);

    return vptrT != vptrDerived;
}

#define OBJECT_HAS_VTABLE(T) objectHasVtable<T>()
  

Есть ли лучшее решение этой проблемы?

Редактировать

Решение не обязательно должно быть универсальным для всех компиляторов. Он может работать на gcc, g , MSVC… Просто укажите, для какого компилятора известно, что ваше решение является допустимым. Мой предназначен для MSVC 2010.

Комментарии:

1. В общем случае вы не можете, потому что стандарт C не предписывает vtable. вы спрашиваете, как проверить, имеет ли класс виртуальную функцию?

2. Я спрашиваю, каким было бы ваше решение вопроса, заданного в задаче. В принципе, вы можете определить, имеет ли класс виртуальную таблицу или нет. Я могу ограничить вопрос MS VC , если вы считаете, что это невозможно с gcc / g .

3. Зачем мне это делать? Это не сайт для соревнований по программированию.

4. Это просто вызов. Нет другого смысла, кроме как посмотреть, можете ли вы это сделать или нет.

5. @unapersson: учитывая, что я посмотрел на SO, чтобы найти ответ, и не смог, учитывая, что я погуглил его и не нашел готового ответа из кулинарной книги, я подумал, что ему здесь самое место, поскольку он довольно информативен. Я не знаю, когда людям это понадобится, но когда они это сделают, они найдут ответ на SO now.

Ответ №1:

Стандартный метод заключается в использовании std::is_polymorphic C 11 / C 03 TR1 / Boost, чтобы определить, содержит ли класс (и его базы) какие-либо виртуальные члены.

 #include <type_traits>
#define OBJECT_HAS_VTABLE(T) (std::is_polymorphic<T>::value)
  

Комментарии:

1. Что, если реализация использует альтернативный метод для реализации полиморфного поведения? Просто шучу.

2. Кроме того, вызов Boost в качестве стандарта немного затруднен. И, как предполагает Мартин, он фактически не проверяет наличие vtable.

3. @unapersson: type_traits является частью TR1, а затем C 0x. Последние два являются стандартными.

4. Этот ответ неточен, согласно стандарту, std::is_polymorphic может обнаруживать только виртуальные функции , но виртуальное наследование без виртуальной функции также будет генерировать vtable , по крайней мере, для GCC 4.8.5

Ответ №2:

Для полноты картины, вот ответ, который только что прислал мне мой приятель. Судя по всему, это, вероятно, похоже на то, как это делает TR1 (хотя я сам не просматривал код).

 template<class T>
class HasVTable
{
public :
    class Derived : public T
    {
        virtual void _force_the_vtable(){}
    };
    enum { Value = (sizeof(T) == sizeof(Derived)) };
};

#define OBJECT_HAS_VTABLE(type) HasVTable<type>::Value
  

Комментарии:

1. 1 .. хороший ответ. исправлены некоторые синтаксические ошибки в вашем коде

2. Обратите внимание, что, если type он виртуально наследуется от своего базового класса, но не содержит никакой виртуальной функции, все равно он выдает ответ как true .

3. Я подумал об этом после прочтения вопроса и не смог разобраться… Этот ответ хорош, и действительно заставляет меня захотеть посмотреть себе в лицо…

4. Я думаю, что этот ответ должен быть принятым, этот может обнаруживать не только виртуальные функции, но и виртуальные наследования.

5. Это решение не работает с final классами. Это также дает неправильный ответ для struct alignas(8) C {}; .

Ответ №3:

Вы можете использовать следующие свойства C :

  1. dynamic_cast сбой во время компиляции, если аргумент не является полиморфным классом. Такой сбой может быть использован с SFINAE.
  2. dynamic_cast<void*> является допустимым приведением, которое возвращает адрес полного полиморфного объекта.

Следовательно, в C 11:

 #include <iostream>
#include <type_traits>

template<class T>
auto is_polymorphic2_test(T* p) -> decltype(dynamic_cast<void*>(p), std::true_type{});

template<class T>
auto is_polymorphic2_test(...) -> std::false_type;

template<class T>
using is_polymorphic2 = decltype(is_polymorphic2_test<T>(static_cast<T*>(0)));

struct A {};
struct B { virtual ~B(); };

int main() {
    std::cout << is_polymorphic2<A>::value << 'n'; // Outputs 0.
    std::cout << is_polymorphic2<B>::value << 'n'; // Outputs 1.
}