2016-04-06 7 views
0

私はこれに見てきましたが、それは私が望んでいないのです。(、Convert string to variable name or variable type文字列を参照にするには?

私はiniファイルを読み込むコード、QHashテーブルに格納するデータを持って、ハッシュキーの値をチェック下記参照)、値が "1"の場合はWorldに追加されます。

コード例:

World theWorld; 
AgentMove AgentMovement(&theWorld); 

if(rules.value("AgentMovement") == "1") 
     theWorld.addRule(&AgentMovement); 

INIファイルは:私は何をしたいです

AgentMovement=1 

、動的にINIファイルから読み取られ、ハードコードされた変数への参照を設定します。

for(int j = 0; j < ck.size(); j++) 
    if(rules.value(ck[j]) == "1") 
     theWorld.addRule("&" + ck[j]); 
          ^
          = &AgentMovement 

上記のように、どのように文字列を参照にしますか?

+2

正しい方法はありません。しかし、あなたはそれをすることができます。明らかな方法は 'std :: map 'を使うことです。 –

+1

'AgentMovement'は単にシンボルの名前です。コンパイラがその仕事をした後、この名前はもはや存在しません。参照するオブジェクトに名前をマップする必要があります(例えば、 'std :: map'を使って) – user463035818

+0

' theWorld'オブジェクトの型は何ですか? – vcp

答えて

0

名前からオブジェクトへのマッピングを自分で提供する必要があります。このアプローチの問題は、あなたがオブジェクトがマップで実際にはないこと、要求された場合に何をすべきかを決定しなければならないということです

template <typename T> 
struct ObjectMap { 
    void addObject(std::string name,T* obj){ 
     m[name] = obj; 
    } 
    T& getRef(std::string name) const { 
     auto x = m.find(name); 
     if (x != m.end()) { return *(x->second);} 
     else { return dummy; } 
    } 
    private: 
     std::map<std::string,T*> m; 
     T dummy; 
} 

:私は、クラスの中にこのような何かを、それをラップします。参照は常に何かを参照する必要があります(0にできるポインタとは対照的に)。参照をダミーオブジェクトに戻すことにしました。ただし、参照の代わりにポインタを使用することを検討することもできます。オブジェクトがマップ内にない場合は、エラーをスローするオプションもあります。

+0

...またはエラーをスローします。 –

+0

@MartinBonner thxの提案。私は通常投げないので、質問があります: 'x == m.end()'でも単純に 'return x-> second'を呼び出す方法がいくつかあります。 ) 'には' second 'はありませんか?それとも、これはUBだけでしょうか? – user463035818

+0

これと私のコードを使って目的の出力を生成する過程で、この例をありがとう。 – Kanyenke

1

これはプログラミングの共通のテーマです:セットの1つにすぎない値(enum、整数の有限集合の1つ、または可能な文字列値の集合、あるいはいくつかのボタンGUIで)何らかのアクションを実行するための基準として使用されます。単純なアプローチは、スイッチ(原子型の場合)または複雑な型のif/elseチェーンを使用することです。それはあなたが現在やっていることであり、それには何の問題もようがない:、年間であなたを含め、誰でも理解:

if(rules.value(ck[j]) == "1") theWorld.addRule(&AgentMovement); 
else if(rules.value(ck[j]) == "2") theWorld.addRule(&AgentEat); 
else if(rules.value(ck[j]) == "3") theWorld.addRule(&AgentSleep); 
// etc. 
else error("internal error: weird rules value %s\n", rules.value(ck[j])); 

このパターンの主な利点は、それがクリスタルクリアであることを私の経験でありますすぐに何が起こっていて、どの基準がどの行動につながるかをすぐに見ることができます。驚くべき利点であるデバッグも簡単です。特定のアクションでのみ中断することができます。

主な欠点は、保守性です。さまざまな場所で異なるものを切り替えるために同じ基準(enumなど)が使用されている場合は、新しいenum値が追加された場合など、これらの場所をすべて維持する必要があります。アクションには、サウンド、アイコン、状態変更、ログメッセージなどが付いてくる場合があります。これらが同時に(同じスイッチ内で)発生しない場合は、アクション列挙型(または文字列値に対してif/then/else)で何度も切り替えることになります。その場合、アクションに関連付けられたすべての情報をデータ構造体にバンドルし、アクションをキーとしてマップ/ハッシュテーブルに構造体を配置する方がよいでしょう。すべてのスイッチが単一のコールに崩壊します。

struct ActionDataT { Rule rule; Icon icon; Sound sound; }; 
map<string, AcionDataT> actionMap 
    = { 
     {"1", {AgentMovement, moveIcon, moveSound} } 
     {"2", {AgentEat,  eatIcon, eatSound } } , 
     // 
    }; 

を使用量は、たとえば、

for(int j = 0; j < ck.size(); j++) 
    theWorld.addRule(actionMap[rules.value(ck[j])].rule); 

、他の場所のようになる:

if(actionFinished(action)) removeIcon(actionMap[action].icon); 

これはかなり洗練されて、このようなマップは次のようになりますのコンパイル時の初期化。これは、ソフトウェア設計の2つの原則を示しています。1.「コンピュータ科学のすべての問題は、別のレベルの間接指向で解決できます」(David Wheeler)2.多くの場合、より多くのデータまたはより多くのコードが選択できます。単純なアプローチはコード指向であり、マップアプローチはデータ指向です。

スイッチが複数の状況で発生する場合、その都度データをコーディングすることがメンテナンスの悪夢となるため、データ中心主義アプローチは不可欠です。

データ中心のアプローチでは、新しいアクションが追加されたときに、アクションが使用される場所はどれも触れなければならないことに注意してください。これは必須です。このメカニズムは、仮想メンバ関数の呼び出しに似ています(原則と実装では、実際には)。呼び出しコードは実際に何が行われているのか分からず、実際には興味がありません。責任はオブジェクトに移されます。呼び出しコードは、書かれたときに存在しなかったプログラムのライフサイクルの後半でアクションを実行することがあります。対照的に、多くの明示的なスイッチを持つプログラムと比較すると、アクションが追加されたときに1回の使用ごとに調べる必要があります。

データ中心主義アプローチに含まれる間接参照は欠点ですが、ではという別のレベルで間違いが解決できません。コードはより抽象的になり、デバッグするのが難しくなりにくくなります。

+0

私のプロジェクトは1つのINIファイルしか扱えませんでした。そのファイルの値はハードコードされていました。 (あなたの例としてif、else if、else switch)。誰かがINIファイルに新しい値を追加し、プログラムがそれらを読み込めるようにするため、複数のINIファイルを動的に処理できるようにすることが推奨されていました。 プログラムは時間の経過とともにデータをシミュレートするので、各INIファイルはシミュレーションの設定を表します。ユーザーは複数のシミュレーションファイルを持つことができるため、静的から動的に変更する必要があります。 – Kanyenke

関連する問題