Search code examples
c++conditional-statementsaliastypedefusing

Conditionally define, during runtime, which of 3 classes will be used in the rest of C++ code


Here is what I am trying to do in C++. From an external library that I use I have 3 classes, MyClass1, MyClass2 and MyClass3 that are identical in their public functions. I would like to decide, at the beginning of runtime, which one will be used by in the rest of my code depending on the hardware configuraion of the user.

To better illustrate what I mean, let me give an example that I know does not work. If it were possible to conditionally define a typedef at runtime, what I am trying to achieve would look as follows:

const int x = GetX(); //where GetX() is any function that calculates the value of x
typedef std::conditional<x > 0, MyClass1,
            std::conditional< x < 0, MyClass2,
                MyClass3>::type>::type TheClass;

So that in the rest of the code I would only refer to TheClass, in such a way that it does not matter whether it is aliasing MyClass1, MyClass2 or MyClass3.

However, of course the code above does not work because when x has its value calculated from a function executed at the beginning of runtime, then std::conditional complains that x is not a constant. Which makes sense since typedef cannot be defined at runtime.

So, my question: is there a way to achieve what I am trying to do (not with typedef since I know it cannot be defined at runtime)? Remember that MyClass1, MyClass2 and MyClass3 are externally given by a library, and in such a way that I cannot easily alter them.


Solution

  • To do this at compile time, the GetX function must be constexpr. Using the comparison operators also conflicts with the template syntax. You will need to provide consexpr functions for less than and greater than:

    constexpr int GetX(){ return 0;}
    constexpr bool IsGreater(int x, int y) { return x > y;}
    constexpr bool IsLess(int x, int y) { return x < y;}
    
    typedef std::conditional<IsGreater(GetX(),0), MyClass1,
                std::conditional<IsLess(GetX(),0), MyClass2,
                    MyClass3>::type>::type TheClass;
    

    If you can't make GetX() constexpr (because the value is decided at runtime),
    then you are looking for a Sum Type. They are quite common in functional programming languages and C++ now has library support in the form of std::variant.

    Your example code could be turned into the following:

    int main(){
    
      //a type that can be 1 of 3 other types
      std::variant<MyClass1,MyClass2,MyClass3> TheClass;
    
      //decide what type it should be at runtime.
      const int x = GetX();
      if      (x > 0) { TheClass = MyClass1(); }
      else if (x < 0) { TheClass = MyClass2(); }
      else            { TheClass = MyClass3(); }
    }
    

    Here, you are deciding the type at runtime.
    You can go on to use pattern matching for evaluating what type is held.