It's safe to say that duplicate code makes your code awfully hard to maintain. It makes your codebase unnecessary large and adds extra technical debt. On top of that, writing duplicate code is a waste of time that could have been better spent.
Simply put, it's when a snippet of code appears multiple times throughout a codebase. It happens for many reasons: Somebody wanted to reuse a function in a different class, and copy-paste was the quickest solution.
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();
}
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();
}
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.
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);
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With