Search code examples
c++templatesconstexprcrtpdeterministic

Using CRTP To Deterministically Generate Code


I've been recently getting into template wizardry and in particular CRTP. I know that templates are used to make the compiler generate code for us so I was wondering if it were possible to make a template "decide" which parts of a function we would like it to include for a particular class. For example if I have the following code:

crtp.h

#include <iostream>
using std::endl;
using std::cout;

template<class T>
class A {
public:
    void func() {
        constexpr unsigned short mask = T::GetMask();
        if (mask & 1) {
            /*
            Do Something
            */
            cout << "Mask 1" << endl;
        }
        if (mask & 1 << 3) {
            /*
            Do Something else
            */
            cout << "Mask 2" << endl;
        }
    }
};

class B : public A<B> {
    friend class A<B>;
protected:
    static constexpr unsigned short GetMask() { return 0x0001; }
};

class C : public A<C> {
    friend class A<C>;
protected:
    static constexpr unsigned short GetMask() { return 0x0009; }
};

main.cpp

#include "ctrp.h"
#include <iostream>
#include <vector>

using std::cout;
using std::vector;
using std::getchar;
using std::endl;

int main() {
    B b;
    C c;
    cout << "B:" << endl;
    b.func();
    cout << endl << "C:" << endl;
    c.func();
    getchar();
}

Which when executed produces:

B:
Mask 1

C:
Mask 1
Mask 2

This works great, does exactly what I want it to. The problem is from my standpoint the if statements should be unnecessary. As I am dealing with constant expressions the compiler should have everything it needs to simply skip the branching and know to execute the first part for class B and both parts for class C.

I would like to cash in on this and specifically tell the compiler to remove the sections that are unnecessary for the particular class to avoid unnecessary branching at runtime. Unfortunately I have no idea how to do this, any ideas? Thanks in advance

Edit

In response to some of the awesome suggestions C++17's constexpr if expression is a near perfect solution that I had no idea existed, but am unfortunately unable to use. I am limited to using C++14.


Solution

  • Emulating if/else at compile time using template metaprogramming does not work that way. You have to imagine if/else using a different mindset.

    Instead of

        if (mask & 1) {
            /*
            Do Something
            */
            cout << "Mask 1" << endl;
        }
        if (mask & 1 << 3) {
            /*
            Do Something else
            */
            cout << "Mask 2" << endl;
        }
    

    you'll have to use something along the lines of:

       function1_selector<mask & 1>::dostuff();
       function2_selector<mask & 1 << 3 >::dostuff();
    

    where

    template <bool> struct function1_selector
    {
      static void dostuff() { /* Do nothing */ }
    };
    
    template <> struct function1_selector<true> // Specialize for true
    {
       static void dostuff() { /* Do something useful */ }
    };
    

    Add code for function2_selector similarly.