2010-06-27 30 views
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」類對所有命令都做了很多事情,比如實現撤銷,重做,進度報告等,但爲了可讀性,我從代碼中刪除了它。

現在我的問題很簡單: 有沒有辦法改寫命令類,這樣我可以取代這個呼叫:通過類似

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 
... 

等等? (甚至不知道這是否會工作確實)

或者是有另一種更優雅的方式來做到這一點? 是語法,提到here有什麼用處? (函數f;)

我一直在看Loki的類型列表,他們似乎做的工作。但我在Boost找不到任何東西。我在網上讀到boost :: mpl是用來實現類型列表的,但是我對MPL文檔有點困惑?

對此有何見解? Regads, D.

回答

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; 
}; 

它的非虛擬接口和指針,以實現成語的典型組合。

+0

非常感興趣!事實上,我添加了一個非模板的CommandBase類,這正是爲了容器的目的。保存/恢復參數也可以完美地工作(取消一​​些技巧)。 我最後選用使用這種方法 「模板類命令 模板類命令 模板類命令 」,用升壓預處理器一起。似乎是最簡單的方式,但我仍然有一些合成問題。 我發現你的方法比我的更優雅,所以非常感謝,我會看看這個strtegy模式:-) – Dinaiz 2010-06-28 00:37:23

2

AFAIK你不能用當前的C++標準來做到這一點。一些boost代碼使用宏和其他預處理來模擬可變模板(我認爲boost :: pool或boost :: object_pool使用類似的東西)。

然而,variadic templates將在下面的標準C++ 0x中來,並根據該頁面已經GCC提供實現開始V4.3:http://gcc.gnu.org/projects/cxx0x.html

如果你使用它,您可以通過啓用激活C++ 0x。

+0

+1這纔是真正的答案。但是,考慮到這個新的C++還沒有被所有的編譯器支持:你想要你的代碼是多麼便攜?它是否需要在大多數編譯器下編譯,或者是一個足夠適合您的單個特定編譯器? – 2010-06-27 09:26:00

+1

變量模板不能解決問題,請參閱我的答案。 – fredoverflow 2010-06-27 10:04:18

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等等的後端實現。它比通過模板元編程遞歸遍歷類型列表要容易得多。

至於一個普遍的答案,我把它發佈在另一個線程中,你最初有這個問題以及另一個線程。

+0

我想知道如果通過使用部分模板專業化,可以使用助推系統,但只有一個函數名稱,我。E,而不是: function0 <> 功能1 有 功能<> 功能 和編譯器選擇正確的版本取決於模板參數的個數您提供: MyFunction的:公共功能 - >將「選擇」功能1 如果我明白你說的話,應該寫MyFunction:public Function1 這個工作,對吧? – Dinaiz 2010-06-28 00:41:16

2

乍一看,可變模板看起來像是一個完美的解決方案。不幸的是,他們不虛函數發揮出色:

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

這需要executeImpl是一個虛擬成員函數模板,但在C++中沒有這樣的事!

+0

+1爲好。但他展示的方式仍然有效。像'template class Command; template struct命令 {/ * ... */virtual R executeImpl(P ...)= 0; };然後他可以通過'class CmdAdd:public Command {void executeImpl(int howMuch){/ * ... * /}};' – 2010-06-27 12:04:51

+0

@Johannes:您的解決方案意味着所有子類都使用相同的函數簽名, 對?那是Dinaiz想要的嗎?我不確定。 – fredoverflow 2010-06-27 12:20:50

+1

我同意,只是沒有所有命令通用的基類:) – 2010-06-27 12:24:11

相關問題