2016-10-01 1 views
3

1つの親から継承する2つ以上のクラスがあります。それらはすべてhandleメソッドをオーバーロードしましたが、各クラスにはハンドルメソッドのパラメータが異なります。クラスのセットを指定すると、一致するメソッドのパラメータを持つものを呼び出します。

class CommandHandler {}; 

class FooCommandHandler : public CommandHandler 
{ 
public: 
    string handle(const FooCommand& c) { return c.getStringSomehow(); } 
}; 

class BarCommandHandler : public CommandHandler 
{ 
public: 
    string handle(const BarCommand& c) { return c.getStringSomeOtherWay(); } 
    string handle(const OtherBarCommand& c) { return c.youGetThePicture(); } 
}; 

FooCommandHandler fooHandler; 
BarCommandHandler barHandler; 

どのクラスに正しい署名があるかを評価して呼び出す関数が必要です。これは可能ですか?

これはコンパイル時に行われるのが理想的であり、まったく1つの一致があることをstatic_assertします。署名template<typename C> string sendCommand(C c)考える

sendCommand<BarCommand>(c)fooHandler.handle(c)

+0

C++ 14である必要がありますか? – Rakete1111

+0

@ Rakete1111、それはC++である必要があります。私はそれがC + + 14の機能を使用する場合は気にしない、ちょうど動作する必要があります。 – ddouglascarr

答えて

4

通常の関数のオーバーロードは、あなたのケースのために働くだろう呼ぶだろうbarHandler.handle(c)

sendCommand<FooCommand>(c)を呼び出します。署名を求めるのはtemplate<typename C> string sendCommand(C c)なので、テンプレート引数は最初の関数引数の型です。次に、定義する:

string sendCommand(const FooCommand& c) { 
    fooHandler.handle(c); 
} 
string sendCommand(const BarCommand& c) { 
    barHandler.handle(c); 
} 

そして、それらを呼び出します。ここにテンプレートは必要ありません。

あなたはコマンドやハンドラの多くを持っている場合は、この試すことができます:あなたはすべてのハンドラを保持しているレジストリのようなものを必要とする

// We need a way to retrive handler by its type. Tuple is good for that. 
std::tuple<FooCommandHandler, BarCommandHandler> handlers; 

// Note the return type. If Handler on position handlerIndex does not have proper method, then instantiation will fail and, due to SFINAE, this function will just be ignored. 
template<class Command, size_t handlerIndex = 0> 
auto sendCommand(const Command& c) -> decltype(std::get<handlerIndex>(handlers).handle(c)) 
{ 
    return std::get<handlerIndex>(handlers).handle(c); 
} 

// Again, SFINAE technique. Compiler will stop search if the template above has been instantiated and will ignore this one. But in other case this template will be used and it will try to call next handler. 
template<class Command, size_t handlerIndex> 
std::string sendCommand(const Command& c) 
{ 
    return sendCommand<Command, handlerIndex + 1>()(c); 
} 

注意を。ここでは、そのようなレジストリとしてstd :: tupleを使用します。

+1

OPはコードの重複を避けたいのでOPがテンプレートを望んだと思う。 – Rakete1111

+0

そうです、私は数百のコマンドを持っています。むしろそれぞれのためにそれを書いてはいけません。 – ddouglascarr

+0

私はそれについてもっと考えると、おそらくそれを機能的に行うことができます。私が最後にしたのは、Javaであり、それはオプションではありませんでした。可能であれば、聞くことに興味があります。 – ddouglascarr

関連する問題