Is there a template that can generate static / dynamically linked versions of a class?
I am working on some library code and I want users to be able to use static bindings if they are able. If they can't instantiate a class at compile time, I want a dynamic version of the class in there so that it can be instantiated at runtime.
For a quick example, let's say I have a struct 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 allow library users 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 class definition twice. I can think of several ways to implement a template that will generate both versions on its own, but I can see that it becomes a lot of work. Especially for classes that will use a different number 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 metaprogram library that has templates that can generate both definitions for me? How can I avoid having to write my class definitions twice?
source to share
There is a simple mechanism to get the parts that are not affected by the dynamic / static value problem in one place: put them in another class, call it basic_A
and call the static / dynamic value container you are showing in the question value_A
. There are various ways to connect value_A
and basic_A
to form a complete class A
:
-
Aggregation
basic_A
insidevalue_A
. This would mean that you have to channel each methodbasic_A
throughvalue_A
and provide the appropriate one-liners in both specializationsvalue_A
. It probably isn't that much because you need to duplicate all the one-liners, so scratch that. -
Aggregation
value_A
insidebasic_A
. You will also need to make abasic_A
template just to pass parametersvalue_A
and provide the correct constructors for both specializations, perhaps by disabling them somehow and enabling them via SFINAE. Not very nice and convenient code. An alternative would be to make a common base class (interface) for the two specializationsvalue_A
, have anunique_ptr
interface for that in,basic_A
and pass a ready constructed constructorvalue_A
inbasic_A
, at the cost of a virtual function call and indirect indirection when you want to access a value. Yuck, especially if it isA
intended for the small and fast light class. -
Inherit
basic_A
fromvalue_A
. The same issues as in 2. apply regarding constructors and template parameter passing. -
Inherit
value_A
frombasic_A
. The design problem goes away, but nowbasic_A
can't easily access the valuevalue_A
. One solution would be to have a pure virtual functiongetValue()
inbasic_A
that two specializations must performvalue_A
. This again comes with the overhead of sending a virtual function, which may not be desirable for a small lightweight class, but allows encapsulation as itbasic_A
is non-template and can hide its implementation in the .cpp file. Another approach would be to use compiletime polymorphism in CRTP, which would do thebasic_A
template again.
Here are two examples for two approaches to 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. For 4a it's pretty much the same, just lose the template arguments and parentheses from basic_A
, as it's a simple 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; }
};
source to share