7

I've been using the Curiously recurring template pattern The general code looks like this:

template <typename T> void genericFunction(T &);
template <typename T> struct Functionality {
    void genericMethod() {
        genericFunction(*((T *)this)) ;
    }
};

struct Klass : public Functionality<Klass> {};

void main() {
    Klass obj ;
    obj.genericMethod();
}

template <> void genericFunction<Klass>(Klass &obj) {
    //do stuff with Klass &obj here
}

I ran into an error today which cost me about 90 minutes of hair-pulling fustration, this error was caused by using an incorrect template parameter for my base class inheritance declaration, somewhat like so:

struct Klass : public Functionality<SomeOtherKlass> {}; //SomeOtherKlass wrong!!!

I'd like to enhance my code so that this mismatch between the derived class and the base class template parameter is detected (runtime, compile time, anytime :) ), is this even possible?, thanks.

Gearoid Murphy
  • 11,834
  • 17
  • 68
  • 86

5 Answers5

3

You could assert the relation in e.g. genericMethod() using Boost or C++11 features:

BOOST_STATIC_ASSERT(( boost::is_base_of<Functionality<T>, T>::value ));

... although that is assuming that the other class doesn't derive from Functionality<T> as well.

An alternative could be to assert the relation at runtime in test-builds:

template <typename T> struct Functionality {
#ifdef TEST_BUILD
    virtual ~Functionality() {}
#endif
    void genericMethod() {
#ifdef TEST_BUILD
        assert(dynamic_cast<T*>(this));
#endif
        genericFunction(*((T *)this)) ;
    }
};

Note that the test won't work inside constructors and destructors

Community
  • 1
  • 1
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
2

In C++11, the following should work:

template<typename T> class Base
{
  friend T; // allowed in C++11
private:
  ~Base() {}
public:
  // ...
};

class Derived: public Base<Derived> {}; // OK

class WronglyDerived: public Base<Derived> {}; // Error: destructor of base class is private
celtschk
  • 19,311
  • 3
  • 39
  • 64
1

You could use a dynamic_cast, which will return null if you have the wrong parameter type. (You'll need at least one virtual function in the base for this to work - the destructor, say.)

If you're worried about efficiency, boost has a polymorphic_cast which does a dynamic cast in debug mode but a static cast for production.

(And in any case it would be nice to avoid the use of the C-style cast.)

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
  • 1
    I started using this template pattern to avoid the performance degradation introduced by virtual functions, the genericFunction in the example above is autogenerated by a script which creates serialisation functions for the associated class (reading and writing to std::iostream) – Gearoid Murphy Jan 08 '12 at 15:41
  • Would adding a virtual function that is never called, or only called once, really be a problem? You don't need to make all functions virtual. – Alan Stokes Jan 08 '12 at 15:43
  • One would think so but I noted the performance drop on functions which were not virtualised, simply inheriting from a base class with pure virtual functions was enough to cause the performance hit, my particular context uses vast numbers of tree structures, so any slight impact is magnified, unfortunately – Gearoid Murphy Jan 08 '12 at 15:48
  • 1
    @Gearoid: How about having the check etc. only compiled in in test-builds? – Georg Fritzsche Jan 08 '12 at 17:13
  • @Georg, such a solution would be fine but dynamic casting does not seem to be exposing the rift, how can I use RTTI from the context of the Functionality class to make this check? – Gearoid Murphy Jan 08 '12 at 18:52
  • @Gearoid Eh? dynamic_cast is RTTI. – Alan Stokes Jan 08 '12 at 18:59
  • @Gearoid: If some test-build macro is set add some virtual function and try to `dynamic_cast(this)`. – Georg Fritzsche Jan 08 '12 at 21:22
  • @Alan, I'm aware that RTTI supports dynamic_cast function, I've implemented Georg's suggestion for debug builds only but have not had any success. The dynamic cast function does not return a value even when the template parameters are correct, I'll update with an answer illustrating my approach... – Gearoid Murphy Jan 08 '12 at 23:03
0

Suppose you add a templated constructor to the base that takes a pointer to arbitrary type;

template<class U> Functionality(U *) { ... }

Then each derived class's constructor can pass its this pointer to the constructor, and in the body of the constructor you just static assert that U and T are the same type.

The constructor parameter is never actually used so should be optimised out entirely. And if this is the only base class constructor you can't forget to call it. The only problem would be if you passed something other than this.

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
-1

The most tangible suggestion thus far is to use dynamic_cast to expose malformed inheritance declarations in the Base class constructor, like so:

#include <iostream>
template <typename T> struct Base {
    Base() {
        std::cout<<dynamic_cast<T *> (this)<<std::endl;
    }
    virtual void iampolymorphic(){}
};
struct Decoy {} ;
struct Pass : public Base<Pass>{}; //correct
struct Fail : public Base<Decoy>{}; //incorrect
int main() {
    Pass p ;
    Fail f ;
    return 1 ;
}

This code compiles on g++ 4.6.1, Amd64 Xubuntu 11.10. The output for both dynamic cast operations is a null pointer. Comments, criticisms and observations are welcomed.

Gearoid Murphy
  • 11,834
  • 17
  • 68
  • 86