1

Simplified I have the following class hierarchy:

class BaseVec {
  public:
    BaseVec() {};
    virtual ~BaseVec() {};

    virtual double get_double(int i) const = 0;
};

template<typename T>
class Vec : public BaseVec {
  public:
    Vec() { ... };
    ~Vec() { ... };

    T get(int i) const { ... };

    double get_double(int i) const {
      return get(i);
    };
};

In my project I repeatedly end up with code of the following form:

template<typename T>
double foo_template(void* p) {
  Vec<T>* v = reinterpret_cast<Vec<T>*>(p);
  // use v which involves calling get
  return result;
}

double foo(void* p, int type) {
  if (type == 0) {
    return foo_template<double>(p);
  } else if (type == 1) {
    return foo_template<int>(p);
  } else if (...) {
    // etc.
  } else {
    //unsupported type
  }
}

(I could use a switch and use enums, or first cast p to BaseVec and then do dynamic_casts, but the logic then remains the same)

This is not ideal to maintain. For example when I add an additional class I want to support I have to add a clause to each of the if-else-if blocks.

One possible way of simplifying this would be to cast p to BaseVec* and use the get_double method. However, since this method is called very often this results in poor performance. Furthermore, this is not alway possible: sometimes I want to call the get method as the type returned is important.

I experimented with the visitor-pattern, and although, this has some advantages, it still means I have to write a seperate piece of code for each possible template parameter.

Is there some way of making this code easier to maintain?

PS: I don't have (much) control over what comes into foo. foo gets called by an external programme (R to be exact). Therefore, I can only pass generic pointers, int, doubles and character vectors to foo.

PPS: Suggestions for a better title are also welcome.

Jan van der Laan
  • 8,005
  • 1
  • 20
  • 35

3 Answers3

1

First of all, don't use reinterpret_cast while converting to/from pointer to polymorphic class. You can write a simple pointer wrapper which allow you to use safe casting operator static_cast:

template <class Type>
class PointerWrapper
{
public:

    PointerWrapper(Type* object);
    PointerWrapper& operator=(Type* object);
    Type* operator->();

protected:

    Type* object;

};

template <class Type>
PointerWrapper<Type>::PointerWrapper(Type* object) :
    object(object)
{
}

template <class Type>
PointerWrapper<Type>& PointerWrapper<Type>::operator=(Type* object)
{
    this->object = object;
}

template <class Type>
Type* PointerWrapper<Type>::operator->()
{
    return object;
}

Now you can write:

typedef PointerWrapper<BaseVec> BaseVecPointer;

template<typename T>
double foo(void* p) {
    BaseVecPointer* vp = static_cast<BaseVecPointer*>(p);
    // ...
    // ... = (*vp)->get_double(...);
    // ...
    return result;
}

In this code polymorphism capabilities were used, i.e. function get_double was called instead of calling get.

But if you want to call just get, not get_double, i.e. you want to call template functions with different template arguments depending on the value of run-time variable, you can use the following method:

enum FooTypes
{
    NoFooType = -1,
    DoubleFooType = 0,
    IntegerFooType = 1,
    // ...
    FooTypesCount
};

template<FooTypes fooType>
struct ChooseType
{
    static
    const FooTypes value = NoFooType;

    typedef void Type;
};

template<>
struct ChooseType<DoubleFooType>
{
    static
    const FooTypes value = DoubleFooType;

    typedef double Type;
};

template<>
struct ChooseType<IntegerFooType>
{
    static
    const FooTypes value = IntegerFooType;

    typedef int Type;
};

Here you should write specializations of the class template ChooseType for all possible values of type variable. Following code describes the function ChooseFoo which selects what specialization of foo_template function template should be called:

typedef double (*FooFunction)(void*);

template<FooTypes fooType>
FooFunction ChooseFooImpl(int type)
{
    if (type == fooType)
    {
        if (ChooseType<fooType>::value != NoFooType)
        {
            return foo_template<typename ChooseType<fooType>::Type>;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        return ChooseFooImpl<(FooTypes)(fooType - 1)>(type);
    }
}

template<>
FooFunction ChooseFooImpl<NoFooType>(int type)
{
    return NULL;
}

FooFunction ChooseFoo(int type)
{
    return ChooseFooImpl<FooTypesCount>(type);
}

And this is foo function implementation:

double foo(void* p, int type)
{
    FooFunction fooFunction = ChooseFoo(type);

    if (fooFunction != NULL)
    {
        return fooFunction(p);
    }
    else
    {
        //unsupported type
        // ...
    }
}
Constructor
  • 7,273
  • 2
  • 24
  • 66
0

Why not change foo_template to be:

template<typename T>
double foo_template(Vec<T>*) {
  // use v which involves calling get
  return result;
}

and foo to be:

template<typename T>
double foo (Vec<T>* v )
{
return foo_template(v)
}

and let argument deduction do the work?

(You can probably get rid of one of the functions, but I wanted to keep is as close to the original)

yosim
  • 503
  • 2
  • 8
  • Thanks. That would be better. However, as I said in my PS, I have little influence over what exactly gets passed to `foo`. This code will end up in a library which gets called by another program (R to be exact). This program doesn't know about `Vec`s, so what I get is some generic pointer which I have to cast to right type. – Jan van der Laan Dec 08 '13 at 07:29
  • Updated question to reflect previous comment. – Jan van der Laan Dec 08 '13 at 07:35
0

Automatic dispatch in C++ happens with runtime-polymorphism by means of virtua function and with static_type polymorphism by mnas of a static_Cast, but you need to know what type to cast.

With a different design, avoiding void*, you can do the following:

template<class Derived>
class static_polimorphic {};

template<class A>
A& upcast(static_polymorphic<A>& sa)
{ return static_cast<A&>(sa); }

template<class A>
const A& upcast(const static_polymorphic<A>& sa)
{ return static_cast<const A&>(sa); }

Now, you classes shold be like

class C1: public static_polymorphic<C1>
{
 ....
};

class C2: public static_polymorphic<C2>
{
 ....
};

Polymorphism will then apply as

template<class A> 
void function(const static_polymorphic<A>& sa)
{
   A& a = upcast(sa);
   a.methods();
   ...
}

In other words, the type is anymore represented a base member variable, but by a base template parameter.

Note also that being the bases differentiated by the derived type, common functions will not reaqire to be virtual. You can so completely avoid runtimes-based polymorphism, unless you have to store different runtime-type created object into a same container or collection.

For that purpose you can use a second non-tempetised base with abstract virtual function as "launchers" for the one in the derived classes. (May be better to use the runtime polymorphic one as first base, to simplify run-time pointer convertion, since there will be no offset)

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • Thanks. Perhaps I don't completely understand what your example does, but as far as I understand this still does not avoid the `switch` or `if...elseif` statement that I have. When calling `function` I still have to know the type. With your code I get rid of BaseVec and don't need to use the `reinterpret_cast` and/or `dynamic_cast`. Could you perhaps explain how my function `foo` would look like? Thanks. – Jan van der Laan Dec 08 '13 at 11:12
  • The pont is "tere are as many `function` as `A` types. you don't have to switch since every one gets its own. You cannot reconduce to your specific case because what you have is a void* and a type information that is a runtime value. A void* kills whatever type information, and is not a good base for a polymorphic design. – Emilio Garavaglia Dec 08 '13 at 15:45
  • You are right, it would be better to avoid the void. However, as I mentioned I have not that much influence over what gets passed to `foo` as this is determined by another programme. Although, you have got me thinking on a completely different solution, which might still use your answer but avoids the void pointer. I will have try this out which will take some time. – Jan van der Laan Dec 08 '13 at 19:31