現在、プロジェクトに取り組んでいますが、次の問題があります。
2つの異なる方法で作業したいC++メソッドがあります。
void MyFunction()
{
foo();
bar();
foobar();
}
void MyFunctionWithABonus()
{
foo();
bar();
doBonusStuff();
foobar();
}
また、実際の機能ははるかに長いため、コードを複製したくないと思います。問題は、MyFunctionWithABonusの代わりにMyFunctionが呼び出されたときに、どのような状況でもプログラムに実行時間を追加してはならないことです。そのため、C++比較で確認するブール型のパラメーターを取得することはできません。
私のアイデアは、C++テンプレートを使用してコードを事実上複製することでしたが、実行時間を追加せず、コードを複製する必要がない方法は考えられません。
私はテンプレートの専門家ではないので、何かが足りないかもしれません。
あなたの誰かがアイデアを持っていますか?それとも、C++ 11では不可能なのでしょうか?
テンプレートとラムダを使用すると、次のことができます。
template <typename F>
void common(F f)
{
foo();
bar();
f();
foobar();
}
void MyFunction()
{
common([](){});
}
void MyFunctionWithABonus()
{
common(&doBonusStuff);
}
または、prefix
およびsuffix
関数を作成することもできます。
void prefix()
{
foo();
bar();
}
void suffix()
{
foobar();
}
void MyFunction()
{
prefix();
suffix();
}
void MyFunctionWithABonus()
{
prefix();
doBonusStuff();
suffix();
}
そのような何かがうまくいくでしょう:
template<bool bonus = false>
void MyFunction()
{
foo();
bar();
if (bonus) { doBonusStuff(); }
foobar();
}
次の方法で呼び出します:
MyFunction<true>();
MyFunction<false>();
MyFunction(); // Call myFunction with the false template by default
関数にニースラッパーを追加すると、「 "い」テンプレートをすべて回避できます。
void MyFunctionAlone() { MyFunction<false>(); }
void MyFunctionBonus() { MyFunction<true>(); }
そのテクニックに関するいくつかの素晴らしい情報を見つけることができます there 。それは「古い」紙ですが、技術自体は完全に正しいままです。
Nice C++ 17コンパイラにアクセスできる場合は、次のようにconstexpr ifを使用して、さらにテクニックをプッシュすることもできます。
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();
}
OPがデバッグに関して行ったコメントの一部を考えると、デバッグビルドではdoBonusStuff()
を呼び出しますが、リリースビルド(NDEBUG
を定義する)ではありません:
_#if defined(NDEBUG)
#define DEBUG(x)
#else
#define DEBUG(x) x
#endif
void MyFunctionWithABonus()
{
foo();
bar();
DEBUG(doBonusStuff());
foobar();
}
_
条件をチェックし、それがfalseの場合に失敗する場合は、 assert
マクロ を使用することもできます(ただし、デバッグビルドのみ。リリースビルドはチェックを実行しません)。
doBonusStuff()
に副作用がある場合は注意してください。これらの副作用はリリースビルドには存在せず、コードで行われた仮定が無効になる可能性があるためです。
可変テンプレートを使用したJarod42の回答のわずかなバリエーションを次に示します。これにより、発信者はゼロまたは1つのボーナス機能を提供できます。
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();
}
呼び出しコード:
MyFunction();
MyFunction(&doBonusStuff);
別のバージョン。テンプレートのみを使用し、リダイレクト機能は使用していません。実行時のオーバーヘッドは必要ないと言ったためです。私が懸念している限り、これはコンパイル時間を増やすだけです:
_#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
_
bool
パラメーターをテンプレート引数として使用するMyFunc()
のバージョンは1つのみになりました。
tag dispatchingと単純な関数のオーバーロードを使用できます。
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();
}
これは読みやすく、理解しやすく、汗をかいて拡張することはできません(また、ボイラープレートif
句-タグを追加することで)、そしてもちろん実行時のフットプリントを残しません。
呼び出し構文はそのままで非常に使いやすいですが、もちろんバニラ呼び出しにラップすることができます:
void MyFunctionAlone() { MyFunction(Tag_DisableBonus{}); }
void MyFunctionBonus() { MyFunction(Tag_EnableBonus{}); }
タグのディスパッチは広く使用されている一般的なプログラミング手法です。 here は基本に関する素晴らしい投稿です。