开发者

C++ alternatives to preprocessor macro code generation?

I have a collection of about 50 small, very similary structured classes that all derive from a common base. The classes represent items that are read in from a file as pairs of strings, where the first string is used to identify the type of the pair (which derived class should be used to represent the data) and the second is the data itself. There is also a visitor (as in visitor pattern) class associated with the derived classes and a factory class for generating the appropriate derived class from the type identification string.

The setup looks something like this:

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
{
    publ开发者_开发问答ic:
        // 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(); }
};

Since this code is very repetitive, takes up a lot of space, and since adding a new item type would require remembering to add lines in several places, I am using macros to create the derived classes and to add :

#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

Now for the question: is using macros to "compact" this code the "right" way to do this, or is there a more elegant/cleaner approach? Comments suggesting an alternative design are also welcome: I'm still pretty new to object-oriented programming and don't have a good feel for what's "right" yet.

Thank you very much in advance!


You may want to check out a copy of "Modern C++ Design," by Andrei Alexandrescu, which shows how to automatically generate most of this code using the C++ template system. Alexandrescu dedicates two chapters to the visitor pattern and automatically generating class hierarchies, which seems exactly like what you're looking for. I won't try to replicate the code in this answer, primarily because it's really dense, I'd probably get it wrong, and the book has a much better explanation. :-)


Perhaps there is a reason for all the inheritance, but it does seem rather like a lot of code.

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

Generally, I wouldn't expect to need internals of the type to create a visitor. I suspect your use for that is a violation of open/closed principal. I'd recommend getting familiar with boost::variant and boost::static_visitor to see how they do it. Maybe I'm being a bit presumptuous, not sure.


I would roll out a small generator script and never touch the generated sources by hand. Having explicit code (no macros) always better for debugging too.


I would invest my efforts in having a code generator/template language. I have already professionally done things with a combination of the Nvelocity template engine, which as very simple and good language (but a not so good parser!), and C# with very good results.

I don't know if you use Visual Studio 2010, but I heard it now has a template engine called T4. I never tried it, soI am not the ideal person to talk about it, but if I was in your place, I would investigate following this direction.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜