9

私の質問が非常に長く技術的なもので申し訳ありませんが、他の人が興味を持っていることは非常に重要だと思います

一部のソフトウェアの内部構造を C++ での表現から明確に分離する方法を探していました。

boost::any クラスを使用して任意の種類の値を含めることができる汎用パラメーター クラス (後でコンテナーに格納する) があります。

私はこの種の基本クラスを(大まかに)持っています(もちろんもっとあります)

class Parameter 
{
public:
    Parameter()
    template typename<T> T GetValue() const { return any_cast<T>( _value ); }
    template typename<T> void SetValue(const T& value) { _value = value; }
    string GetValueAsString() const = 0;
    void SetValueFromString(const string& str) const = 0;
private:
    boost::any _value;
}

派生クラスには 2 つのレベルがあります。最初のレベルでは、型と文字列との間の変換を定義します (たとえば、ParameterInt または ParameterString)。2 番目のレベルでは、動作と実際の作成者を定義します (たとえば、ParameterInt から ParameterAnyInt と ParameterLimitedInt を派生させるか、または ParameterFilename から ParameterFilename を派生させます)。 GenericString)

実際の型に応じて、基本クラスに仮想メソッドを追加せず、奇妙なキャストを行わずに、特定のパラメーターの型に応じて動作する外部関数またはクラスを追加したい

たとえば、パラメーターの種類に応じて適切な GUI コントロールを作成したいと思います。

Widget* CreateWidget(const Parameter& p)

もちろん、RTTI を使用するか、自分で (enum と switch ケースを使用して) 実装しない限り、ここから実際のパラメーターの型を理解することはできませんが、これは正しい OOP 設計ソリューションではありません。

古典的な解決策は、ビジター デザイン パターンhttp://en.wikipedia.org/wiki/Visitor_patternです。

このパターンの問題は、どの派生型が実装されるかを前もって知る必要があることです。そのため (ウィキペディアに書かれているものと私のコードをまとめると)、次のようになります。

struct Visitor 
{
  virtual void visit(ParameterLimitedInt& wheel) = 0;
  virtual void visit(ParameterAnyInt& engine) = 0;
  virtual void visit(ParameterFilename& body) = 0;
};

すべての具象型を事前に知る必要がなく、元の訪問者を派生させることなく、他の方法でこの動作を取得する解決策はありますか?


編集: ピザ博士の解決策は私が考えていたものに最も近いようですが、問題は同じであり、メソッドは実際には動的キャストに依存しています。

ビジターパターンを引き合いに出さずに、何らかの解決策を考えて、心をきれいにした方がよいのではないでしょうか。目的は、次のような機能を持つことです。

Widget* CreateWidget(const Parameter& p)

タイプに関する情報を失うことなく、「具体的な」パラメータごとに異なる動作をする

4

5 に答える 5

4

Vistorの一般的な実装については、 Loki ライブラリの一部であるLoki Visitorをお勧めします。

于 2008-08-29T00:02:18.950 に答える
1

私はこれ(「非環式ビジター」) を効果的に使用しました。これにより、既存のクラスをある程度変更することなく、新しいクラスを階層に追加できます。

于 2008-08-28T10:06:48.740 に答える
0

完全を期すために:

もちろん、オブジェクトのマルチメソッド ポインター テーブルの独自の実装を作成し、実行時にメソッド アドレスを手動で計算することは完全に可能です。マルチメソッドの実装のトピックに関する Stroustrup による論文があります(ただし、コンパイラで)。

私は誰にもこれをするように勧めません。実装を適切に実行することは非常に複雑であり、それを使用するための構文はおそらく非常に扱いにくく、エラーが発生しやすくなります。他のすべてが失敗した場合でも、これはまだ進むべき道かもしれません.

于 2008-08-28T10:36:55.127 に答える
0

これを正しく理解すれば...

さまざまなハードウェア オプションを使用できるオブジェクトがありました。これを容易にするために、Device の抽象インターフェースを使用しました。デバイスには、特定のイベントで起動される一連の機能がありました。使用方法は同じですが、Device のさまざまな実装は、完全な機能を備えているか、すぐに戻るだけです。生活をさらに楽にするために、関数は無効で、何か問題が発生したときに例外をスローしました。

于 2008-08-28T09:58:49.867 に答える
0

あなたの要件を理解できません。しかし、いわば私自身の言葉で、私が理解している状況を次のように述べます。

  • 最終的にいくつかの具象クラス (例: ParameterLimitedInt) にサブクラス化される抽象 Parameter クラスがあります。

  • これらのパラメーターを一般的な方法で渡す別の GUI システムがありますが、問題は、パラメーター クラスの具象型に固有の GUI コンポーネントを提示する必要があることです。

  • 制限は、RTTID を実行したくないこと、および可能なすべての型の具体的なパラメーターを処理するコードを書きたくないことです。

  • あなたは訪問者パターンを使用することにオープンです。

これらがあなたの要件であるため、このような状況をどのように処理するかを次に示します。

accept() がブール値を返す訪問者パターンを実装します。基本 Parameter クラスは、仮想の accept() 関数を実装し、false を返します。

Parameter クラスの具体的な実装には、訪問者の visit() を呼び出す accept() 関数が含まれます。それらは true を返します。

ビジター クラスはテンプレート化された visit() 関数を使用するため、サポートしたい具体的なパラメーター タイプに対してのみオーバーライドします。

class Visitor
{
public:
  template< class T > void visit( const T& param ) const
  {
    assert( false && "this parameter type not specialised in the visitor" );
  }
  void visit( const ParameterLimitedInt& ) const; // specialised implementations...
}

したがって、accept() が false を返す場合は、パラメーターの具象型がビジター パターンをまだ実装していないことがわかります (ケースバイケースで処理したい追加のロジックがある場合)。ビジター パターンで assert() がトリガーされる場合は、特殊化を実装した Parameter タイプを訪問していないためです。

これらすべての欠点の 1 つは、サポートされていない訪問が実行時にしかキャッチされないことです。

于 2008-08-28T21:31:24.897 に答える