Duplicate code using c++11

C++C++11Templates

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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 - C++

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 - C++

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 - C++

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 - C++

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.

Solution 6 - C++

You can use tag dispatching and simple function overload:

struct Tag_EnableBonus {};
struct Tag_DisableBonus {};

void doBonusStuff(Tag_DisableBonus) {}

void doBonusStuff(Tag_EnableBonus)
{
    //Do bonus stuff here
}

template<class Tag> MyFunction(Tag bonus_tag)
{
   foo();
   bar();
   doBonusStuff(bonus_tag);
   foobar();
}

This is easy to read/understand, can be expanded with no sweat (and no boilerplate if clauses - by adding more tags), and of course will leave no runtime footprint.

The calling syntax it quite friendly as it is, but of course can be wrapped into vanilla calls:

void MyFunctionAlone() { MyFunction(Tag_DisableBonus{}); }
void MyFunctionBonus() { MyFunction(Tag_EnableBonus{}); }

Tag dispatching is a widely used generic programming technique, here is a nice post about the basics.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionplougueView Question on Stackoverflow
Solution 1 - C++user6418134View Answer on Stackoverflow
Solution 2 - C++Jarod42View Answer on Stackoverflow
Solution 3 - C++CornstalksView Answer on Stackoverflow
Solution 4 - C++Chris DrewView Answer on Stackoverflow
Solution 5 - C++Sebastian SternView Answer on Stackoverflow
Solution 6 - C++Ap31View Answer on Stackoverflow