14

A friend of mine sent me the following challenge earlier today:

Given the following code, propose an implementation of OBJECT_HAS_VTABLE so the program prints 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 = %i\n",
           OBJECT_HAS_VTABLE(AnObject),
           OBJECT_HAS_VTABLE(AnObjectWithVTable));
}

I've came up with the following solution which I think is decent enough:

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

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

    return vptrT != vptrDerived;
}

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

Is there a better solution to this problem?

Edit

The solution doesn't have to be generic across all compilers. It can work on gcc, g++, MSVC... Just specify for which compiler your solution is known to be valid. Mine is for MSVC 2010.

joce
  • 9,624
  • 19
  • 56
  • 74
  • 4
    In general you can't, because the C++ standard does not mandate a vtable. are you asking how to test if a class has a virtual function? –  May 11 '11 at 20:41
  • I'm asking what would be your solution to the question asked in the challenge. Basically, can you determine if a class has a vtable or not. I can restrict the question to MS VC++ if you think it's impossible with gcc / g++. – joce May 11 '11 at 20:44
  • 3
    Why would I want to do it? This is not a coding competition site. –  May 11 '11 at 20:46
  • It's just a challenge. There's no point other than to see if you can do it or not. – joce May 11 '11 at 20:47
  • @unapersson: given that I looked on SO to find an answer and couldn't, given that I Googled it and didn't find a ready baked cookbook answer, I thought that it had its place here as it's quite informative. I don't know when people would need it, but when they do, they'll find the answer on SO now. – joce May 11 '11 at 21:03

3 Answers3

19

The standard method is to use std::is_polymorphic from C++11/C++03 TR1/Boost to determine if a class (and its bases) contain any virtual members.

#include <type_traits>
#define OBJECT_HAS_VTABLE(T) (std::is_polymorphic<T>::value)
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • What if the implementation uses an alternative method to implement polymorphic behavior? Just joking. – Martin York May 11 '11 at 20:53
  • Also, invoking Boost as a standard is a bit off. And as Martin suggests, it does not actually test for a vtable. –  May 11 '11 at 20:55
  • 4
    @unapersson: type_traits is part of TR1 and then C++0x. The latter two *are* standard. – kennytm May 11 '11 at 20:59
  • 3
    This answer is not accurate, according to the standard, `std::is_polymorphic` can **only detect virtual functions**, but **virtual inheritance without virtual function will also generate vtable**, at least it will for GCC 4.8.5 – Kelvin Hu Jul 01 '19 at 09:08
11

For completeness sake, here's the answer my buddy just sent me. From the look of it, it's probably similar to how TR1 does it (though I haven't looked at the code myself).

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
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
joce
  • 9,624
  • 19
  • 56
  • 74
  • 1
    +1 .. good answer. have corrected some syntax errors in your code – iammilind May 12 '11 at 02:15
  • 1
    Note that, If `type` is inherited virtually from it's base class but doesn't contain any virtual function, still it gives the answer as `true`. – iammilind May 12 '11 at 02:16
  • I had a think about this after reading the question, and couldn't figure it out... That answer is nice, and really makes me want to facepalm at myself... – Mark K Cowan Aug 21 '15 at 16:39
  • I think this answer should be the accepted one, this one can detect not only virtual functions, but also virtual inheritances. – Kelvin Hu Jul 01 '19 at 09:18
  • @iammilind I think this behavior is correct, as **virtual inheritances also produce vtables**, and the question asks about how to detect vtable, but not virtual functions. – Kelvin Hu Jul 03 '19 at 09:12
  • 1
    This solution doesn't work with `final` classes. It also gives wrong answer for `struct alignas(8) C {};`. – Maxim Egorushkin Jan 27 '20 at 21:00
3

You can use the following properties of C++:

  1. dynamic_cast fails at compile time if the argument is not a polymorphic class. Such a failure can be used with SFINAE.
  2. dynamic_cast<void*> is a valid cast that returns the address of the complete polymorpic object.

Hence, in 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.
}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271