Rabbit
Rabbit

Reputation: 1831

Determine inheritance at compile time

I have some code that behaves like this:

class Base {};

class MyClass : public Base {};

MyClass* BaseToMyClass(Base* p)
{
  MyClass* pRes = dynamic_cast<MyClass*>(p);
  assert(pRes);
  return pRes;
}

Is there a way to add a compile time check so I can catch calls to this function where p is not an instance of MyClass? I had a look at Alexandrescu's SUPERSUBCLASS function but I'm not sure if it can do the job.

Thanks!

Upvotes: 1

Views: 2545

Answers (3)

sbi
sbi

Reputation: 224159

Is there a way to add a compile time check so I can catch calls to this function where p is not an instance of MyClass?

Generally, if you want to check this at compile-time, you'd take the derived class as an argument.

However, if the only thing you have is a Base* or a Base&, then you cannot know whether it refers to a MyClass object. It's the very nature of run-time polymorphism that this is to be found out at run-time. This check can only be done where the MyClass object/reference/pointer is converted to a Base*/Base&. That's why dynamic_cast<>() was invented.

Your function basically is a safe_cast. If you put it into the right syntax, it looks like this:

template< typename Derived, typename Base >
inline Derived* safe_cast(Base* pb)
{
  #if defined _NDEBUG
    return static_cast<Derived*>(pb);
  #else
    Derived* pd = dynamic_cast<Derived*>(pb);
    assert(pd);
    return pd;
  #endif
}

template< typename Derived, typename Base >
inline Derived& safe_cast(Base& rb)
{
  return *safe_cast<Derived*>(&rb);
}

Upvotes: 4

kennytm
kennytm

Reputation: 523654

(I'm posting this as a different answer since the strategy is different.)

You could use a static assert for a more flexible constraint, e.g.

#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>

template <class T>
MyClass* BaseToMyClass(T* x) {
    BOOST_STATIC_ASSERT(!(boost::is_convertible<T, MyClass>::value));
    // ^ This ensures T is not MyClass at compile time.

    MyClass* p = dynamic_cast<MyClass*>(x);
    assert(p);
    return p;
}

The error message looks like

x.cpp: In function 'MyClass* BaseToMyClass(T*) [with T = MyClass]':
x.cpp:26:22:   instantiated from here
x.cpp:13:1: error: invalid application of 'sizeof' to incomplete type 'boost::STATIC_ASSERTION_FAILURE<false>' 

Note that, it also cannot detect if you declare a pointer as Base* p = new MyClass.

This code uses Boost.StaticAssert and Boost.TypeTraits. The former can be replaced by the built-in static_assert in C++0x, while the latter is available as a built-in library since TR1.

Upvotes: 0

kennytm
kennytm

Reputation: 523654

Try this:

struct Base { virtual ~Base() {} };

struct MyClass : public Base {};

template <class T> MyClass* BaseToMyClass(T p) {
    int x[-(int)sizeof(T)];
    return 0;
}

template <> MyClass* BaseToMyClass(Base* p) {
    printf("1\n");
    MyClass* pRes = dynamic_cast<MyClass*>(p);
    assert(pRes != NULL);
    return pRes;
}

If the function is called with Base*, the 1st specialization will be used. If it is called with MyClass*, the generic version will be used. Since an array cannot have negative size, this will cause a compile-time error, which can be traced back to the point of instantiation.

The sizeof(T) is to ensure the error is shown only at instantiation time.

Note that this method is not suitable if Base has any other subclasses you want to pass.


Example usage:

int main () {           // 22
    MyClass p;          // 23
    Base q;             // 24
    BaseToMyClass(&q);  // 25
    BaseToMyClass(&p);  // 26
}                       // 27

Results in

x.cpp: In function 'MyClass* BaseToMyClass(T) [with T = MyClass*]':
x.cpp:26:22:   instantiated from here
x.cpp:11:27: error: creating array with negative size ('-0x00000000000000008')

Note that it cannot detect what the pointer is new-ed with. For instance, in

int main () {
    Base* p = new Base;
    Base* q = new MyClass;
    BaseToMyClass(p);
    BaseToMyClass(q);
    delete p;
    delete q;
}

the program will be compiled successfully.

Upvotes: 1

Related Questions