1

I have a template class in c++ that depends on a few nontype template parameters. The implementation of some methods, and some field member, depend on the value of the template parameters, in a nontrivial way. This essentially require partial template specialization of the methods, which (being not directly doable in c++) is implemented with a series of if-constexpr constructs. I am looking for a refactoring of the code in a sensible and mantainable way.

I will try to illustrate the problem by making some simplifications. The class looks like that

constexpr property_A(unsigned int prop);
constexpr property_B(unsigned int prop);

template <unsigned int N, unsigned int prop, unsigned int type>
class Model {
   // Several common methods and fields (depending  on N)
   auto method_common();
   some_type m_common;

   // This depends on prop and type
   using type_field = ...
   type_field m_field;

   auto private_method() { // depends on prop and type }

public:

   auto some_public_method_typeA() { // depends on property_A(prop), calls private_method() }
   auto some_public_method_typeB() { //depends on property_B(prop), calls private_method() }
};


};

The template parameters prop and type are actually allowed to take a finite range of values. The two constexpr property_A and property_B conveniently map the template parameter prop to a value that defines the specialziation of methods some_public_method_tyepA and some_public_method_typeB. These 2 "type" of specializations are fundamentally different, something like:

property_A(0) = 0
property_A(1) = property_A(3) = 1;
property_A(2) = property_A(4) = 2;
property_B(0) = 0;
property_B(1) = property_B(2) = 1;
property_B(3) = property_B(4) = 2;

Additionally, there are methods that depends directly on prop (i.e., not through property_A(prop) or property_B(prop)).

The code is at the moment implemented for type=0 only, and the various partial specializations are realized with a std::conditional (or similar) and if-constexpr:

using_type_field = std::conditional_t<(property_A(prop) == 0), type1, type2>; // or more complex choice

some_public_method_type_A {
  if constexpr (property_A(prop) == 0) {
         // specialized code
  }
  else if constexpr (property_A(prop) == 1) {
         // specialized code
  }
}

some_public_method_type_B {
  if constexpr (property_B(prop) == 0) {
         // specialized code
  }
  else if constexpr (property_B(prop) == 1) {
         // specialized code
  }
}

I am going to implement the code for type=1. Since this "specialization" could require other methods, and also to keep some flexibility, I am inclined to use CRTP to "specialize" to type==1, while retaining the common interface:

constexpr property_A(unsigned int prop);
constexpr property_B(unsigned int prop);

template <unsigned int N, unsigned int prop, class Derived>
class Model_Base {
   // Several common methods and fields (depending  on N)
   auto method_common();
   some_type m_common;
    
   auto private_method() { return static_cast<Derived *>(this)->private_method(); }

public:

some_public_method_type_A {
  if constexpr (property_A(prop) == 0) {
         // specialized code
         // Calls private_method, implemented in Derived
         private_method();
}

some_public_method_type_B {
  if constexpr (property_B(prop) == 0) {
         // specialized code
  }
  else if constexpr (property_B(prop) == 1) {
         // specialized code
         // Calls private_method, implemented in Derived
         private_method();
  }
}
};

template <unsigned int N, unsigned int prop>
class Model_type0 : Model<N, prop, Model_type0>
   friend class Model<N, prop, Model_type0>; // To give access to private_method
   // Depends on prop
   using type_field = ...
   type_field m_field;
   auto private_method();
};

template <unsigned int N, unsigned int prop>
class Model_type1 : Model<N, prop, Model_type0>
   friend class Model<N, prop, Model_type1>; // To give access to private_method
   // Depends on prop, possibly different than Model_type0
   using type_field = ...
   type_field m_field;
   auto private_method();
   auto some_other_method();
};

// For convenience, can be more systematically defined if there are many possible values of template parameter type
template <unsigned int N, unsigned int prop, unsigned int type>
using Model = std::conditional_t<(type == 0), Model_type0<N, prop>, Model_type1<N, prop>>;

While this plan seems ok, I am looking for advice for a possibly better design, in terms of maintainability and flexibility. In particular, if I ever add another possible value of prop template parameter, I will need to add yet-another if-constexpr branch in the various public methods, rendering the code more complex...

Performance is important, so no virtual methods.

I use c++17, but I am open to use c++20 if this brings decisive advantages.

5
  • How many realizations of prop are you expecting realistically? My experience with heavily templated code is that it is often not worth the headache to use deep template resolution when there are only two or three variations in $prop. The reason being that possible error outputs become a certified nightmare. If you insist, I'd take a look at constexpr switch-case for your prop. That might end up being cleaner than a series of 'if constexpr{}'.
    – MPIchael
    Nov 20 at 9:25
  • I actually have 7 possible values of prop. I may in the future further specialize a few values of prop, by adding another template unsigned int relevant only for those values of prop; to do that, I would probably think to replace prop by a simple struct that holds this second parameter, when relevant.
    – francesco
    Nov 20 at 9:53
  • You could specialize via enums, as they are secretly ints anyway. (stackoverflow.com/questions/47666913/…)
    – MPIchael
    Nov 20 at 10:00
  • are the things dependant on property_A independant of property_B, so you could carve them out into separate classes, and have Model just compose those?
    – Caleth
    Nov 20 at 12:51
  • @Caleth good point, no they are not independent, in that all methods use various common data fields. Additionally, there are also methods that depend directly on prop (I amended the question)
    – francesco
    Nov 20 at 15:32

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.