So I have a nontemplate base
class which contains "default" settings for it's methods. Then I'm trying to use inheritance with a templated class. Here is a snippet of example code for illustration.
// enums for as a template selector
enum class version
{
ver1,
ver2,
ver3
};
// Base class with fabricated methods
struct base
{
virtual void propertyOne()
{
// some default action
}
virtual void propertyTwo()
{
// some default action
}
};
// derived class
template <version V>
struct derived : public base
{
virtual void propertyOne()
{
helper< One, V >();
}
virtual void propertyTwo()
{
helper< Two, V >();
}
}
I'm using a helper function to perform a "generic" algorithm on different "fields" which are used in class traits.
For example: A field is something similar to this
struct field
{
int thingone;
constexpr field(int i):thingone(i){}
};
In c++ 11, in order to give field instances external linkage I wrapped them as static members of another structure (c++14 relaxes these rules, oh well). The whole reason I'm doing this is because I need constant expression values from it (e.g. member variable thingone is needed as a template params of another method which requires it to be a constant expression).
struct fields
{
static constexpr field One{1};
static constexpr field Two{2};
};
// defining trait class from structure above
template< const field& T, revision R >
class fieldTraits;
// sample fieldTrait definitions for illustrative purposes
template< >
class fieldTraits< fields::One, revision::ver3>
{
public:
// Let's say I have common field names
// with different constants that I want to plug
// into the "helper" algorithm
static constexpr size_t field_val = 1;
};
template< >
class fieldTraits< fields::Two, revision::ver1>
{
public:
// Let's say I have common field names
// with different constants that I want to plug
// into the "helper" algorithm
static constexpr size_t field_val = 1;
};
// Main guts of the class methods above
template< const field& F, revision R, typename TT = traitClass<F,R> >
void helper()
{
// Let's pretend I'm doing something useful with that data
std::cout << F.thingone << std::endl;
std::cout << TT::field_val << std::endl;
}
The problem I hit is trying to instantiate for example
derived<revision::rev1> l_derived;
Since I only defined the trait class for ver3
, I can't instantiate the class without defining the trait class for ver1
, and ver2
explicitly. But if the traits classes are exactly the same for, say, ver1
- ver 3
, is any kind of enable_if condition I had to make this template class valid for all revs <= ver3
?
I couldn't find anything in the traits_type header which mainly offers compile time "type" checks such as std::is_same
, etc.
I know one option is to copy-paste the trait classes for ver1
-ver3
but that seemed to be redundant since I wanted to avoid copy-pasting repetitive code.
Another option is to create different classes for each revision and leverage dynamic polymorphism where I can define a class for each revision. And then I only need to include trait revisions changes where they are needed. For example,
class derived_ver1 : public base
{
virtual void propertyOne()
{
helper< fields::One, revision::ver1 >();
}
virtual void propertyTwo()
{
helper< fields::Two, revision::ver1 >();
}
};
class derived_ver2 : public derived:ver1
{
virtual void propertyTwo()
{
helper< fields::Two, revision::ver2 >();
}
};
class derived_ver3 : public derived:ver2
{
virtual void propertyTwo()
{
helper< Two, revision::ver3 >();
}
};
In this example propertyOne()
could reuse the traits class from revision 1 for revision2 and revision 3 because it didn't change past revision 1 (and avoids copy-paste of traits).
Is there a better design I could approach?
In summary: is there a way to use my original template inheritance and use some template feature (e.g. std::enable_if
) to reuse a trait class for undefined revisions. Instead of explicitly definining a trait for each revision (which leads to copy-paste).
Or is the second approach using dynamic polymorphism a better way to do this (which I read, has increased costs of vtable lookup that comes w/it)?