user23167
user23167

Reputation: 487

Compile-time type based dispatch

Following techniques from 'Modern C++ Design', I am implementing a persistence library with various compile-time optimisations. I would like the ability to dispatch a function to a templated member variable if that variable derives from a given class:

template<class T, template <class> class Manager = DefaultManager> class Data
{
private:
   T *data_;

public:
   void Dispatch()
   {
      if(SUPERSUBCLASS(Container, T))
      {
         data_->IKnowThisIsHere();
      }
      else
      {
         Manager<T>::SomeGenericFunction(data_);
      }
   }
}

Where SUPERSUBCLASS is a compile-time macro to determine object inheritance. Of course, this fails in all cases where T does to inherit from Container (or T is an intrinsic type etc etc) because the compiler rightly complains that IKnowThisIsHere() is not a data member, even though this code path will never be followed, as shown here after preprocessing with T = int:

private:
   int *data_;

public:
   void Dispatch()
   {
      if(false)
      {
         data_->IKnowThisIsHere();

Compiler clearly complains at this code, even though it will never get executed. A suggestion of using a dynamic_cast also does not work, as again a type conversion is attempted at compile time that is not possible (for example with T=double, std::string):

void Dispatch()
   {
      if(false)
      {
         dynamic_cast<Container*>(data_)->IKnowThisIsHere();

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, DefaultManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)
error: cannot dynamic_cast '((const Data<std::string, DefaultManager>*)this)->Da<sttad::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)

I really need to emulate (or indeed persuade!) having the compiler emit one set of code if T does inherit from Container, and another if it does not.

Any suggestions?

Upvotes: 5

Views: 4633

Answers (5)

Konrad Rudolph
Konrad Rudolph

Reputation: 545618

You require a kind of compile-time if. This then calls a function depending on which case is true. This way, the compiler won't stumble upon code which it can't compile (because that is safely stored away in another function template that never gets instantiated).

There are several ways of realizing such a compile-time if. The most common is to employ the SFINAE idiom: substitution failure is not an error. Boost's is_base_of ist actually an instance of this idiom. To employ it correctly, you wouldn't write it in an if expression but rather use it as the return type of your function.

Untested code:

void Dispatch()
{
    myfunc(data_);
}

private:

// EDIT: disabled the default case where the specialisation matched
template <typename U>
typename enable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) {
    data_->IKnowThisIsHere();
}

template <typename U>
typename disable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { // default case
    Manager<U>::SomeGenericFunction(data_);
}

Upvotes: 2

Nicola Bonelli
Nicola Bonelli

Reputation: 8287

Overloading can be useful to implement compile-time dispatching, as proposed by Alexandrescu in his book "Modern C++ Design".

You can use a class like this to transform at compile time a boolean or integer into a type:

template <bool n>
struct int2type
{ enum { value = n}; };

The following source code shows a possible application:

#include <iostream>

#define MACRO()   true  // <- macro used to dispatch 

template <bool n>
struct int2type
{ enum { value = n }; };

void method(int2type<false>)
{ std::cout << __PRETTY_FUNCTION__  << std::endl; }

void method(int2type<true>)
{ std::cout << __PRETTY_FUNCTION__  << std::endl; }

int
main(int argc, char *argv[])
{
    // MACRO() determines which function to call
    //

    method( int2type<MACRO()>()); 

    return 0;
}

Of course what really makes the job is the MACRO() or a better implementation as a metafunction

Upvotes: 3

user23167
user23167

Reputation: 487

Unfortunately I've been through that too (and it is, also, a runtime call ;) ) The compiler complains if you pass in non polymorphic or class types, in a similar way to before:

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, RawManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class)

or

error: cannot dynamic_cast '((const Data<std::string, DefaultRawManager>*)this)->Data<std::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic)

Upvotes: 0

Mark Kegel
Mark Kegel

Reputation: 4510

Look into the boost template meta programming library. Also, depending on what you are trying to accomplish look at the boost serialization library, since it may already have what you need.

Upvotes: 0

David Pierre
David Pierre

Reputation: 9535

Boost traits has something for that : is_base_of

Upvotes: 1

Related Questions