C ++替代预处理器宏代码生成?

时间:2011-07-14 18:21:20

标签: c++

我有大约50个小的,非常相似的结构化类的集合 所有这些都来自共同的基础。这些类表示读入的项目 从一个文件作为字符串对,其中第一个字符串用于标识 对的类型(应该使用派生类来表示数据)和 第二个是数据本身。还有一个访客(如访客模式) 与派生类关联的类和用于生成的工厂类 来自类型标识字符串的适当派生类。

设置看起来像这样:

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

现在提出的问题是:使用宏来实现" compact"这段代码"对"办法 要做到这一点,还是有更优雅/更清洁的方法?评论暗示 我们也欢迎另一种设计:我还很新 面向对象的编程,并没有对什么"对"有良好的感觉。爱好。

非常感谢你!

4 个答案:

答案 0 :(得分:2)

您可能需要查看Andrei Alexandrescu的“Modern C ++ Design”副本,其中显示了如何使用C ++模板系统自动生成大部分代码。 Alexandrescu将两个章节专门用于访问者模式并自动生成类层次结构,这看起来与您正在寻找的完全相同。我不会尝试在这个答案中复制代码,主要是因为它非常密集,我可能会弄错,而且这本书有更好的解释。 : - )

答案 1 :(得分: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,看看他们是如何做到的。也许我有点放肆,不确定。

答案 2 :(得分:0)

我会推出一个小型生成器脚本,而不是手动触摸生成的源代码。拥有显式代码(无宏)总是更适合调试。

答案 3 :(得分:0)

我会努力使用代码生成器/模板语言。我已经专业地完成了Nvelocity模板引擎的组合,它是非常简单和良好的语言(但是不是很好的解析器!)和C#,效果非常好。

我不知道您是否使用Visual Studio 2010,但我听说它现在有一个名为T4的模板引擎。我从来没有尝试过,所以我不是理想的人,但如果我在你的位置,我会按照这个方向进行调查。