私は現在、通常の「true」と「false」に加えて、「デフォルト」と (オプション) 「トグル」の状態を含むフラグを実装する賢い方法を考え出そうとしています。
フラグの一般的な問題は、関数があり、特定のパラメーターを渡すことによってその動作 (「何かを行う」または「何かをしない」) を定義したいということです。
シングルフラグ
単一の(ブール値)フラグを使用すると、ソリューションは簡単です。
void foo(...,bool flag){
if(flag){/*do something*/}
}
ここでは、関数を次のように変更するだけで、デフォルトを追加するのが特に簡単です。
void foo(...,bool flag=true)
flag パラメータなしで呼び出します。
複数のフラグ
フラグの数が増えると、私が通常目にして使用するソリューションは次のようになります。
typedef int Flag;
static const Flag Flag1 = 1<<0;
static const Flag Flag2 = 1<<1;
static const Flag Flag3 = 1<<2;
void foo(/*other arguments ,*/ Flag f){
if(f & Flag1){/*do whatever Flag1 indicates*/}
/*check other flags*/
}
//call like this:
foo(/*args ,*/ Flag1 | Flag3)
これには、フラグごとにパラメーターが必要ないという利点があります。つまり、ユーザーは好きなフラグを設定して、気にしないフラグを忘れることができます。foo (/*args*/, true, false, true)特に、どの true/false がどのフラグを示すかを数えなければならないような呼び出しは受けません。
ここでの問題は次のとおり
です。デフォルトの引数を設定すると、ユーザーがフラグを指定するとすぐに上書きされます。のようなことはできませんFlag1=true, Flag2=false, Flag3=default。
明らかに、3 つのオプション (true、false、default) が必要な場合は、フラグごとに少なくとも 2 ビットを渡す必要があります。したがって、必要ではないかもしれませんが、どの実装でも 4 番目の状態を使用してトグル (= !default) を示すのは簡単だと思います。
これには2つのアプローチがありますが、どちらにも満足していません。
アプローチ 1: 2 つのフラグを定義する
私は今までこのようなものを使ってみました:
typedef int Flag;
static const Flag Flag1 = 1<<0;
static const Flag Flag1False= 1<<1;
static const Flag Flag1Toggle = Flag1 | Flag1False;
static const Flag Flag2= 1<<2;
static const Flag Flag2False= 1<<3;
static const Flag Flag2Toggle = Flag2 | Flag2False;
void applyDefault(Flag& f){
//do nothing for flags with default false
//for flags with default true:
f = ( f & Flag1False)? f & ~Flag1 : f | Flag1;
//if the false bit is set, it is either false or toggle, anyway: clear the bit
//if its not set, its either true or default, anyway: set
}
void foo(/*args ,*/ Flag f){
applyDefault(f);
if (f & Flag1) //do whatever Flag1 indicates
}
ただし、これについて私が気に入らないのは、各フラグに 2 つの異なるビットが使用されていることです。これにより、「default-true」フラグと「default-false」フラグのコードが異なり、必要な if のナイスなビット操作の代わりになりapplyDefault()ます。
アプローチ 2: テンプレート
次のようにテンプレートクラスを定義することにより:
struct Flag{
virtual bool apply(bool prev) const =0;
};
template<bool mTrue, bool mFalse>
struct TFlag: public Flag{
inline bool apply(bool prev) const{
return (!prev&&mTrue)||(prev&&!mFalse);
}
};
TFlag<true,false> fTrue;
TFlag<false,true> fFalse;
TFlag<false,false> fDefault;
TFlag<true,true> fToggle;
applyコンパイル時に既知の 1 つを除くすべての引数を使用して、 を 1 つのビット単位の演算に凝縮することができました。したがって、を (gcc を使用して) を使用して、、またはwouldTFlag::applyと同じマシン コードに直接コンパイルすると、非常に効率的ですが、引数としてa を渡したい場合は、テンプレート関数を使用する必要があります。引数を継承して使用すると、仮想関数呼び出しのオーバーヘッドが追加されますが、テンプレートを使用する必要がなくなります。return true;return false;return prev;return !prev;TFlagFlagconst Flag&
ただし、これを複数のフラグにスケールアップする方法がわかりません...
質問
問題は次のとおりです。C++ の 1 つの引数に複数のフラグを実装して、ユーザーが簡単に「true」、「false」、または「default」(特定のフラグを設定しないことで) または (オプション) に設定できるようにするにはどうすればよいですか? 「デフォルトではないもの」を示しますか?
独自のビット単位演算子を使用したテンプレート アプローチのような同様のビット単位操作を使用して、2 つの int を持つクラスを使用する方法はありますか? もしそうなら、コンパイル時にほとんどのビット演算を実行するオプションをコンパイラに与える方法はありますか?
明確にするために編集:
「true」、「false」、「default」、「toggle」の4つの異なるフラグを関数に渡したくありません。
たとえば、「境界を描く」、「中心を描く」、「塗りつぶしの色を描く」、「ぼやけた境界を描く」、「円を上下に動かす」、「その他の空想を行う」などのフラグが使用される場所に描かれる円を考えてみてください。円でできること」、....
そして、これらの「プロパティ」のそれぞれについて、true、false、default、または toggle のいずれかの値を持つフラグを渡したいと思います。そのため、関数はデフォルトで境界線を描画し、色を塗りつぶし、中央に配置することを決定するかもしれませんが、残りは何もしません。おおよそ次のような呼び出しです。
draw_circle (DRAW_BORDER | DONT_DRAW_CENTER | TOGGLE_BLURRY_BORDER) //or
draw_circle (BORDER=true, CENTER=false, BLURRY=toggle)
//or whatever nice syntax you come up with....
境界線を描画し (フラグで指定)、中心を描画せず (フラグで指定)、境界線をぼやけさせ (フラグはデフォルトではないと言っています)、塗りつぶしの色を描画します (指定されていませんが、デフォルト)。
後でデフォルトで中心を描画せず、デフォルトで境界をぼかすことにした場合、呼び出しは境界を描画し(フラグで指定)、中心を描画せず(フラグで指定)、境界をぼかす必要はありません(現在はぼかしがデフォルトです) 、ただしデフォルトは必要ありません) 塗りつぶしの色を描画します (フラグはありませんが、デフォルトです)。