Duplicate code using c++11

I'm currently working on a project and I have the following issue.

I have a C++ method that I want to work in two different ways :

void MyFunction()
{
  foo();
  bar();
  foobar();
}

void MyFunctionWithABonus()
{
  foo();
  bar();
  doBonusStuff();
  foobar();
}

And I would like not to duplicate my code because the actual function is much longer. The issue is I must not under any circumstance add execution time to the program when MyFunction is called instead of MyFunctionWithABonus. That is why I cannot just have a boolean parameter that I check with a C++ comparison.

My idea would have been to use C++ templates to virtually duplicate my code, but I can't think of a way of doing in which I don't have additional execution time and I don't have to duplicate the code.

I'm not an expert with templates so I may be missing something.

Does any of you have an idea? Or is that just impossible in C++11?


Solution 1:

Something like that will do nicely:

template<bool bonus = false>
void MyFunction()
{
  foo();
  bar();
  if (bonus) { doBonusStuff(); }
  foobar();
}

Call it via:

MyFunction<true>();
MyFunction<false>();
MyFunction(); // Call myFunction with the false template by default

The "ugly" template can be all avoided by adding some nice wrappers to the functions:

void MyFunctionAlone() { MyFunction<false>(); }
void MyFunctionBonus() { MyFunction<true>(); }

You can find some nice informations on that technique there. That is an "old" paper, but the technique in itself stay totally right.

Provided you have access to a nice C++17 compiler you can even push further the technique, by using the constexpr if, like that:

template <int bonus>
auto MyFunction() {
  foo();
  bar();
  if      constexpr (bonus == 0) { doBonusStuff1(); }
  else if constexpr (bonus == 1) { doBonusStuff2(); }
  else if constexpr (bonus == 2) { doBonusStuff3(); }
  else if constexpr (bonus == 3) { doBonusStuff4(); }
  // Guarantee that this function will not compile
  // if a bonus different than 0,1,2,3 is passer
  else { static_assert(false);}, 
  foorbar();
}

Solution 2:

With template and lambda, you may do:

template <typename F>
void common(F f)
{
  foo();
  bar();
  f();
  foobar();
}

void MyFunction()
{
    common([](){});
}

void MyFunctionWithABonus()
{
  common(&doBonusStuff);
}

or else you can just create prefix and suffix function.

void prefix()
{
  foo();
  bar();
}

void suffix()
{
    foobar();
}

void MyFunction()
{
    prefix();
    suffix();
}

void MyFunctionWithABonus()
{
    prefix();
    doBonusStuff();
    suffix();
}

Solution 3:

Given some of the comments the OP has made regarding debugging, here's a version that calls doBonusStuff() for debug builds, but not release builds (that define NDEBUG):

#if defined(NDEBUG)
#define DEBUG(x)
#else
#define DEBUG(x) x
#endif

void MyFunctionWithABonus()
{
  foo();
  bar();
  DEBUG(doBonusStuff());
  foobar();
}

You can also use the assert macro if you wish to check a condition and fail if it is false (but only for debug builds; release builds will not perform the check).

Be careful if doBonusStuff() has side effects, as these side effects will not be present in release builds and may invalidate assumptions made in the code.

Solution 4:

Here is a slight variation on Jarod42's answer using variadic templates so the caller can provide zero or one bonus functions:

void callBonus() {}

template<typename F>
void callBonus(F&& f) { f(); }

template <typename ...F>
void MyFunction(F&&... f)
{
  foo();
  bar();
  callBonus(std::forward<F>(f)...);
  foobar();
}

Calling code:

MyFunction();
MyFunction(&doBonusStuff);

Solution 5:

Another version, using only templates and no redirecting functions, since you said you didn't want any runtime overhead. As fas as I'm concerned this only increases compile time:

#include <iostream>

using namespace std;

void foo() { cout << "foo\n"; };
void bar() { cout << "bar\n"; };
void bak() { cout << "bak\n"; };

template <bool = false>
void bonus() {};

template <>
void bonus<true>()
{
    cout << "Doing bonus\n";
};

template <bool withBonus = false>
void MyFunc()
{
    foo();
    bar();
    bonus<withBonus>();
    bak();
}

int main(int argc, const char* argv[])
{
    MyFunc();
    cout << "\n";
    MyFunc<true>();
}

output:
foo
bar
bak

foo
bar
Doing bonus
bak

There's now only one version of MyFunc() with the bool parameter as a template argument.