Is there a template that can generate static / dynamically bound versions of a class?

345 views Asked by At

I'm working on some library code, and I want users to be able to take advantage of static binding if they are able to. If they are unable to instantiate a class at compile time, I want there to be a dynamic version of the class, so that it can be instantiated at run-time.

For a quick example, say I have a structure template A:

template<bool dynamic, int value=0> struct A
{
    static const int Value = value;
};


template<> struct A<true>
{
    int Value;
    A(int value) : Value(value) {}
};

These definitions allows users of the library to instantiate A statically, and dynamically:

A<true> dynamicA = A<true>(5);

A<false, 5> staticA;

The problem with this method is that I have to write the definition of the class twice. I can think of a few ways of implementing a template that would generate both versions myself, but I can see it becoming a lot of work. Especially for classes that would use varying numbers of parameters, for example:

// It would be much harder to generate a static version of this class, 
// though it is possible with type lists.  Also, the way I'm imagining it, 
// the resulting classes probably wouldn't be very easy to use.
struct A
{
   vector<int> Values;
    A(vector<int> value) : Values(value) {}
};

Is there a name for this pattern / problem? Is there a meta programming library which has templates which can generate both definitions for me? How do I avoid having to write the definitions of my classes twice?

1

There are 1 answers

2
Arne Mertz On BEST ANSWER

There is a simple mechanism to get the parts that do not depend on the dynamic/static value problem into one single location: put them into another class, let's call it basic_A, and let's call the static/dynamic value container you show in the question value_A. There are different ways to connect value_A and basic_A to form the complete A class:

  1. Aggregation of basic_A inside value_A. This would mean you have to route every method of basic_A through value_A and provide the corresponding one-liners in both specializations of value_A. This is probably not much of a gain because you have to duplicate all of the one-liners, so scratch that.

  2. Aggregation of value_A inside basic_A. You would either have to make basic_A a template too, only to pass the parameters to the value_A and provide the correct constructors for both specializations, probably somehow disabling and enabling them via SFINAE. Not a very beautiful and maintainable piece of code, either. The alternative would be to make a common base class (Interface) for the two specializations of value_A, have a unique_ptr to that interface in basic_A and pass the ready constructed value_A into basic_A's constructor, at the cost of a virtual function call and poitner indirection whenever you want to access the value. Yuck, especially if A is meant to be a small and fast lightweight class.

  3. Inherit basic_A from value_A. The same problems as in 2. apply, regarding constructors and forwarding of template parameters.

  4. Inherit value_A from basic_A. The construction problem disappears, but now basic_A can not easily access value_A's value. One solution would be to have a pure virtual function getValue() in basic_A which the two specializations of value_A have to implement. This again has the cost of a virtual function dispatch which might not be desirable for a small lightweight class, but it enables encapsulation, since basic_A is a non-template and can hide its implementation in a .cpp file. The other approach would be to use compiletime polymorphism vía the CRTP, which would make basic_A a template again.

Here are two examples for the two approaches of 4.:

4a: getValue() as a virtual function:

//basic_a.hpp

struct basic_A {
  int foo() const;
  virtual int getValue() const = 0;
};

//basic_A.cpp
int basic_A::foo() const { return 10 * getValue(); }

4b: getValue() vía CRTP

template <class Value_t>
struct basic_A {
  int foo() const { return 10 * value_(); }
private:
  int value_() const { return static_cast<Value_t const&>(*this).getValue(); }
};

The template A aka. A_value for 4b follows. For 4a it is alsmost the same, just lose the template arguments and brackets from basic_A since it is a plain class:

template <bool dyn, int value = 0> 
struct A;

template <>
struct A<true, 0> : basic_A<A<true, 0>>
{
  int val;
  int getValue() const { return val; }
};

template <int value>
struct A<false, value> : basic_A<A<false,value>>
{
  int geValue() { return value; }
};