2

これを考慮してください-おそらく不十分に書かれた-例:

class Command;

 class Command : public  boost::enable_shared_from_this<Command>
 { 
  public :
   void execute()
   { 
    executeImpl();
                // then do some stuff which is common to all commands ... 
   }

   // Much more stuff ...
     private:
      virtual void executeImpl()=0;
         // Much more stuff too ...
 };

と :

class CmdAdd : public Command
 {
 public:
  CmdAdd(int howMuchToAdd);
  void executeImpl();


  int _amountToAdd;
 };

// implementation isn't really important here .... 

これにより、次の構文を使用してコールバックを追加できます。

        boost::shared_ptr<Command> cmdAdd(CmdAdd(someValue));
     cmdAdd->execute();

それは完璧に動作します。私の「Command」クラスは、undo、redo、progress reportなどの実装など、すべてのコマンドに共通する多くのことを実行しますが、読みやすくするためにコードから削除しました。

今私の質問は簡単です:この呼び出しを置き換えることができるように、コマンドクラスを書き直す方法はありますか?

boost::shared_ptr<Command> cmdAdd(CmdAdd(someValue));
cmdAdd->execute();

のようなものによって:

CmdAdd(someValue); // preferably
or CmdAdd->execute(someValue)

私はそれについてたくさん考えてきましたが、概念的な問題があります:コマンドクラスを次のようにテンプレート化したかった

template <typename R,typename T1, typename T2, ..., typename Tn> class Command
{
    R1 execute(T1 p1, ...,Tn pn)
    { 
        return executeImpl(T1 p1, ...,Tn pn);
        // then do some stuff which is common to all commands ... 
    }
}

しかし、明らかに、ここに問題があります。構文template <typename R,typename T1, typename T2, ..., typename Tn>は合法的なC ++、AFAIKではありません。

次のように、n個のバージョンのコマンドを作成する必要がありますか?

template <typename R> class Command
template <typename R,typename T1> class Command
template <typename R,typename T1, typename T2> class Command
...

等々 ?(これが実際に機能するかどうかさえわかりません)

または、これを行うための別のよりエレガントな方法はありますか?ここで言及されている構文は、そこで使用されていますか?(関数f;)

私はロキのタイプリストを見てきました、そして彼らは仕事をしているようです。しかし、私はブーストでそれを見つけることができません。私はboost::mplがタイプリストを実装するために使用したいものであるとウェブ上で読みました、しかし私はMPLドキュメントによって少し混乱していますか?

これに関する洞察はありますか?よろしく、D。

4

4 に答える 4

2

一見すると、可変個引数テンプレートは完璧なソリューションのように見えます。残念ながら、仮想関数ではうまく機能しません。

template <typename... Args>
void execute(Args&&... args)
{
    executeImpl(std::forward<Args>(args)...);
}

これは仮想メンバー関数テンプレートexecuteImplである必要がありますが、C ++にはそのようなものはありません!

于 2010-06-27T09:10:00.853 に答える
2

AFAIK現在のC++標準では、これを実際に行うことはできません。一部のブーストコードは、マクロやその他の前処理を使用して可変個引数テンプレートをシミュレートします(boost::poolまたはboost::object_poolはそのようなものを使用すると思います)。

ただし、可変個引数テンプレートは次の標準C ++ 0xで提供され、このページによると、GCCはすでにv4.3以降の実装を提供しています:http://gcc.gnu.org/projects/cxx0x.html

使用している場合は、C++0xをアクティブにすることで有効にできます。

于 2010-06-27T02:26:30.207 に答える
2

興味深い質問:)

まず、見落としている問題があります。すべてに共通の基本クラスが必要であり、Commandそれらのスタックを使用する場合(元に/やり直し)、このクラスをテンプレート化することはできません。

したがって、あなたは立ち往生しています:

class Command
{
public:
  void execute(); 
private:
  virtual void executeImpl() = 0;
};

パラメータを使用して関数を実行するというあなたの要望は理解できますが、とにかく元に戻す/やり直し操作のためにそれらのパラメータを保存する必要があることを忘れないでください。コンストラクターを介してそれらを取得する方が簡単です。

ただし、テンプレート化されたメソッドを使用して、実際にコマンドを呼び出すことはできます。

template <class Command>
void execute() { Command cmd; cmd.execute(); }

template <class Command, class T0>
void execute(T0& arg0) { Command cmd(arg0); cmd.execute(); }

/// ...

int main(int argc, char* argv[])
{
  execute<MyLittleCommand>("path", 3);
}

これは、希望する構文に近いものです。ここでスタックを故意に忘れてしまったことに注意してください。私の考えでは、スタックを登録メソッドに渡す必要がありますexecute(完了したら)。

おそらくCommand、戦略パターンに近づくように設計を変更することになるわけではありません。

struct CommandImpl
{
  virtual ~CommandImpl();
  virtual void executeImpl() = 0;
};

class Command
{
public:
  template <class C>
  static Command Make() { return Command(new C()); }

  template <class C, class T0>
  static Command Make(T0& arg0) { return Command(new C(arg0)); }

  /// ....

  void execute(CommandStack& stack)
  {
    mImpl->executeImpl();
    stack.Push(*this);
  }

private:
  Command(CommandImpl* c): mImpl(c) {}
  boost::shared_ptr<CommandImpl> mImpl;
};

これは、非仮想インターフェースと実装イディオムへのポインターの典型的な組み合わせです。

于 2010-06-27T12:39:35.153 に答える
0

Klaimが指摘したように、可変個引数テンプレートはこの問題の究極の解決策です。ただし、型リストを使用して可変数のテンプレート引数を許可する方法があります。

template <class H, class T>
struct typelist
{
    typedef H head;
    typedef T tail;
};

typelist<typelist<int, float>, double>これにより、たとえば、を書くことができます。ただし、読み取りと書き込みを行うのは首の痛みであり、boost :: functionがブルートフォースアプローチ(テンプレート引数の数ごとにクラスを分ける)を使用する主な理由です:boost :: function0、boost :: function1 、boost::function2などのバックエンド実装。テンプレートメタプログラミングを介してタイプリストを再帰的にトラバースするよりもはるかに簡単です。

一般的な答えとして、私はあなたが最初にこの質問をした別のスレッドに別のスレッドと一緒に投稿しました。

于 2010-06-27T05:13:59.440 に答える