2011-07-14 133 views
2

我收集了大約50個小的,非常類似的結構化類, 都來自一個共同的基礎。這些類表示在文件中以 作爲字符串對讀取的項目,其中第一個字符串用於標識對的類型(應使用哪個派生類來表示數據),第二個是數據本身。還有訪問者(如訪客模式) 與派生類關聯的類和用於從類型標識字符串生成 相應派生類的工廠類。C++替代預處理器宏代碼生成?

的設置看起來是這樣的:

class NodeItemVisitor; // Forward declaration. 

class NodeItemBase 
{ 
    public: 
     std::string get_val() const { return val; } 
     virtual std::string idstr() const = 0; 
     virtual void accept(NodeItemVisitor& v) = 0; 

    private: 
     std::string val; 
}; 

// Forward declarations of derived classes. 
class NodeItemA; 
class NodeItemB; 
... 
class NodeItemZ; 

class NodeItemVisitor 
{ 
    public: 
     virtual void visit(NodeItemA& ni) = 0; 
     ... 
     virtual void visit(NodeItemZ& ni) = 0; 
}; 

class NodeItemA : public NodeItemBase 
{ 
    public: 
     virtual std::string idstr() const { return "A"; } 
     virtual void accept(NodeItemVisitor& v) { v.visit(*this); return; } 
}; 

... 

class NodeItemZ : public NodeItemBase 
{ 
    public: 
     virtual std::string idstr() const { return "Z"; } 
     virtual void accept(NodeItemVisitor& v) { v.visit(*this); return; } 
}; 

class NodeItemFactory 
{ 
    public: 
     // Uses a lookup table to map the input string to one of the "mkni" 
     // functions below and then calls it. 
     static NodeItemBase* mknifromid(const std::string& id); 

    private: 
     static NodeItemBase* mkniA(void) { return new NodeItemA(); } 
     ... 
     static NodeItemBase* mkniZ(void) { return new NodeItemZ(); } 
}; 

由於這個代碼是非常重複,佔用了大量的空間,而且由於加入了 新的項目類型將需要記住要在幾個地方添加線條,我 使用宏來創建派生類,並添加:

#define ADD_NODE_ITEMS \ 
    ADD_NODE_ITEM(A); \ 
    ... 
    ADD_NODE_ITEM(Z); 

#define ADD_NODE_ITEM(ID) \ 
class NodeItem##ID : public NodeItemBase \ 
{ \ 
    public: \ 
     virtual std::string idstr() const { return #ID; } \ 
     virtual void accept(NodeItemVisitor& v) { v.visit(*this); return; } \ 
} 

ADD_NODE_ITEMS 
#undef ADD_NODE_ITEM 

class NodeItemVisitor 
{ 
    public: 
#define ADD_NODE_ITEM(ID) \ 
    virtual void visit(NodeItem##ID& ni) = 0; 
    ADD_NODE_ITEMS 
#undef ADD_NODE_ITEM 
}; 

class NodeItemFactory 
{ 
    public: 
     // Uses a lookup table to map the input string to one of the "mkni" 
     // functions below and then calls it. 
     static NodeItemBase* mknifromid(const std::string& id); 

    private: 
#define ADD_NODE_ITEM(ID) \ 
    static NodeItemBase* mkni##ID(void) { return new NodeItem##ID(); } 
    ADD_NODE_ITEMS 
#undef ADD_NODE_ITEM 
}; 

#undef ADD_NODE_ITEMS 

現在的問題:使用宏來「緊湊型」這段代碼的「正確」的方式 要做到這一點,還是有莫重新優雅/清潔的方法?建議使用 的替代設計也值得歡迎:我對 面向對象編程還很陌生,對於什麼是「正確」還沒有好的感覺。

非常感謝您提前!

+1

爲什麼你需要這麼多派生類?他們有什麼不同? –

回答

0

我會推出一個小型生成器腳本,並且決不會手動觸摸生成的源代碼。有明確的代碼(無宏)總是更好的調試。

2

您可能想查看Andrei Alexandrescu撰寫的「Modern C++ Design」的副本,其中展示瞭如何使用C++模板系統自動生成大部分代碼。 Alexandrescu爲訪客模式專門設計了兩章,並自動生成類層次結構,這看起來與您正在尋找的內容完全相同。我不會嘗試在這個答案中複製代碼,主要是因爲它非常密集,我可能會弄錯,而且這本書有更好的解釋。 :-)

1

也許有所有繼承的原因,但它看起來好像很多代碼。

template<typename T> 
    struct class_trait; 

#define QUOTE(X) #X 

#define CLASS_TRAIT(NAME) \ 
    template<> struct class_trait<NAME> { \ 
    static std::string class_string() {return QUOTE(NAME);} \ 
    } 

template<typename T> 
    std::string GetClassString() { 
    return class_trait<T>::class_string(); 
    } 

通常,我不希望需要內部類型來創建一個訪客。我懷疑你的使用是違反公開/封閉的原則。我建議熟悉boost :: variant和boost :: static_visitor,看看他們是如何做到的。也許我有點冒失,不確定。

0

我會投入我的代碼生成器/模板語言的努力。我已經用Nvelocity模板引擎專業地完成了這些工作,它是一種非常簡單和優秀的語言(但不是一個很好的解析器!),而C#則有很好的結果。

我不知道你是否使用Visual Studio 2010,但是我聽說它現在有一個名爲T4的模板引擎。我從來沒有嘗試過,所以我不是理想的人談論它,但如果我在你的位置,我會調查這個方向。