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?
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:
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.
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.
Inherit basic_A
from value_A
. The same problems as in 2. apply, regarding constructors and forwarding of template parameters.
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; }
};