2011-07-14 132 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的模板引擎。我从来没有尝试过,所以我不是理想的人谈论它,但如果我在你的位置,我会调查这个方向。