Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a compile-time check that a downcast is valid in a CRTP?

I have a plain old CRPT (please don't get distracted by access restrictions - the question is not about them):

 template<class Derived>
 class Base {
     void MethodToOverride()
     {
        // generic stuff here
     }
     void ProblematicMethod()
     {
         static_cast<Derived*>(this)->MethodToOverride();
     } 
 };

that is as usual intended to be used like this:

 class ConcreteDerived : public Base<ConcreteDerived> {
     void MethodToOverride()
     {
        //custom stuff here, then maybe
        Base::MethodToOverride();
     }
 };

Now that static_cast bothers me. I need a downcast (not an upcast), so I have to use an explicit cast. In all reasonable cases the cast will be valid since the current object is indeed of the derived class.

But what if I somehow change the hierarchy and the cast now becomes invalid?

May I somehow enforce a compile-time check that an explicit downcast is valid in this case?

like image 681
sharptooth Avatar asked May 06 '11 06:05

sharptooth


4 Answers

At compile-time you can only check the static types, and that's what static_cast already does.

Given a Base*, it is only, and can only be, known at run-time what its dynamic type is, that is, whether it actually points to a ConcreteDerived or something else. So if you want to check this, it has to be done at runtime (for example by using dynamic_cast)

like image 120
jalf Avatar answered Oct 20 '22 19:10

jalf


For extra safety, you could add a protected constructor to Base, to make sure that something is derived from it. Then the only problem would be for the really stupid:

class ConcreteDerived : public Base<SomeOtherClass>

but that should be caught by the first code review or test case.

like image 20
Bo Persson Avatar answered Oct 20 '22 20:10

Bo Persson


To expand on what @Bo Persson said, you can do a compile time check in said constructor using for example Boost.TypeTraits or C++0x/11 <type_traits>:

#include <type_traits>

template<class Derived>
struct Base{
  typedef Base<Derived> MyType;

  Base(){
    typedef char ERROR_You_screwed_up[ std::is_base_of<MyType,Derived>::value ? 1 : -1 ];
  }
};

class ConcreteDerived : public Base<int>{
};

int main(){
  ConcreteDerived cd;
}

Full example on Ideone.

like image 3
Xeo Avatar answered Oct 20 '22 18:10

Xeo


It seems that there exists a way to check CRPT correctness at compile time.

By making Base abstract (adding some pure virtual method to Base) we guarantee that any Base instance is a part of some derived instance.

By making all Base constructors private we can prevent undesirable inheritance from Base.

By declaring Derived as friend of Base we allow the only inheritance expected by CRPT.

After this, CRPT downcast should be correct (since something is inherited from base and this "something" may be only Derived, not some other class)

Perhaps for practical purpose the first step (making Base abstract) is redundant since successful static_cast guaranties that Derived is somewhere in the Base hierarchy. This allows only an exotic error if Derived is iherited from Base <Derived> (as CRPT expects) but at the same time Derived creates another instance of Base <derived> (without inheritance) somewhere in Derived code (it can, because it is a friend). However, I doubt that someone may accidentally write such exotic code.

like image 2
user396672 Avatar answered Oct 20 '22 18:10

user396672