2012-05-17 6 views
13

この巨大なツリーは、基本的にキーとメタデータの種類に応じて共通のオブジェクト上で文字列キーと異なる関数呼び出しを持つ大きなスイッチ/ケースです。プリプロセッサを使ったメタプログラミングC/C++

すべてのエントリは、基本的にdo_somethingが異なる呼び出しを持つことができるので、私は関数ポインタを使用することはできません。この

} else if (strcmp(key, "key_string") == 0) { 
    ((class_name*)object)->do_something(); 
} else if (... 

のように見えます。また、一部のキーでは、オブジェクトをサブクラスにキャストする必要があります。

これを高水準言語でコーディングすると、ラムダ辞書を使用してこれを簡略化します。

私が

case_callは、最初のコードスニペットにこのコードを拡大するマクロだろう
case_call("key_string", class_name, do_something()); 
case_call(/* ... */) 

のようなものにこれを簡素化するためにマクロを使用することができることを私に起こりました。

しかし、それは良いスタイルと見なされるかどうか私はフェンスに非常にです。つまり、タイピング作業を減らしてコードのDRY性を向上させることができますが、実際にマクロシステムを多少乱用しているようです。

あなたはその道を歩いて行くのか、それとも全体を打ち込むのですか?そうすることについてのあなたの推論は何でしょうか?

編集

いくつかの明確化:

このコードは、C++ APIのような単純なキーと値の特性のいくつかの異なる側面をアクセスする簡略化されたスクリプティングAPIの間に接着層として使用されます。プロパティはC++ではさまざまな方法で実装されていますが、一部にはゲッター/セッターメソッドがあり、一部は特殊な構造体に設定されています。スクリプトアクションは、共通の基本クラスにキャストされたC++オブジェクトを参照します。ただし、一部のアクションは特定のサブクラスでのみ使用可能であり、キャストダウンする必要があります。

私は実際のC++ APIを変更する可能性がありますが、現時点では変更できないとみなされる必要があります。また、これは組み込みコンパイラで動作する必要があるため、boostやC++ 11は(悲しいことに)利用できません。

+5

投票を閉じる理由 - これは非常に有効で有効な質問です。私はここで何が欠けていますか? –

+3

「この質問は私たちのQ&A形式には適していませんが、一般的には事実、参考文献、または特定の専門知識を含む回答が期待されます」*この質問は意見、討論、議論、投票、ディスカッション。* " – Fanael

+0

すでに+5点ですので、自分自身も含めて他の人が結果に興味を持っていると言います。以下の答えは、すでにかなり良いです。 –

答えて

6

私はあなたが少し役割を逆転させることをお勧めします。オブジェクトが特定の状況を処理する方法を知っている何らかのクラスであると言っているので、virtual void handle(const char * key)を基本クラスに追加し、オブジェクトがそれに適用される場合にはオブジェクトをチェックインさせて必要な処理を行います。

これは、長いif-else-ifチェーンを排除するだけでなく、より安全な型になり、これらのイベントをより柔軟に処理できるようにします。

+0

これは長いif-else-ifチェーンを排除するものではなく、単に移動/再配置するだけです。しかし、これはまだ本当に良いアイデアです。 –

+1

@MooingDuck個々のifを排除するわけではありませんが、扱わなければならないケースの中心的で長いリストを排除します。各チェックは、実際の処理が行われるクラスにあるため、新しい機能を追加するために変更する必要がある場所は近くにあります。 – Fozi

+0

それは興味深い考えです。実装クラスを変更する必要がありますが、これはさまざまな理由から今は望ましくありませんが、これは確かに良いアイデアです。 – bastibe

6

それは私にはマクロの適切な使用のようです。彼らは結局のところ、構文の繰り返しを逃れるために作られています。しかし、構文の繰り返しがある場合、必ずしも言語の欠陥ではありません。おそらく、この決定を完全に避けることができるようにするより優れた設計選択肢があります。

一般的な知恵は、アクションにテーブルマッピングキーを使用することです:

std::map<std::string, void(Class::*)()> table; 

は、次に検索し、一度にアクションを呼び出す:

object->*table[key](); 

または失敗を確認するためにfindを使用します。

const auto i = table.find(key); 
if (i != table.end()) 
    object->*(i->second)(); 
else 
    throw std::runtime_error(...); 

しかし、あなたが言うように、関数の共通の署名がない場合(つまり、、あなたはメンバー関数ポインタを使用することはできません)あなたが実際に何をする必要がありますあなたが私の知らないあなたのプロジェクトの詳細に依存します。それは、あなたが見ている繰り返しを削除するための唯一の方法であるかもしれません、またはそれについてもっと良い方法があるかもしれません。

自分自身に質問してください:なぜ私の関数は異なる引数を取るのですか? なぜ私はキャストを使用していますか?オブジェクトの型をディスパッチする場合は、共通のインタフェースを導入する必要があります。

+0

"do_somethingは異なる呼び出しを持つことができるので、関数ポインタを使うことはできません。" < - すべての関数が 'void(Class :: *)()'になるように見えます。一部には引数が必要な場合があります。少なくともそれは私が理解したものです。 – mfontanini

+0

@fontanini:ああ、それを逃した。編集します。 –

+0

実際には、2つの異なる種類の呼び出しがあります。したがって、2つのマップを使用すると、実際には機能します。 – bastibe

関連する問題