How to do a runtime subclassing system

北城余情 提交于 2019-12-11 16:26:04

问题


I am doing a subclassing system which may be defined in runtime. I have a subclass that forwards the method for a table (std::map), if a method is not available in the table, the super class method is used.

Example (function parameters and return type are not the problem, I just simplified it):

class Superclass {
public:
    virtual void doSomething();
};

class Superclass_subclass : public Superclass {
public:
    std::map< std::string,std::function<void (Superclass_subclass*)> > table;

    int doSomething(int a, int b) {
        if( table.count("doSomething") == 0 ) return Superclass::doSomething(a,b);
        return table.at("doSomething")(this,a,b);
    }
};

Now I am working on macros to ease the process of creating the _subclass classes. Right now I got the following macros:

#define runtime_subclass_begin(Superclass) //...
#define runtime_subclass_method(Superclass,rtype,method,args_def,args_call) //...
#define runtime_subclass_end
#define GROUP(...) __VA_ARGS__


runtime_subclass_begin(Superclass)
runtime_subclass_method(Superclass,int,doSomething,GROUP(int a,int b),GROUP(a,b))
runtime_subclass_end

It is working well for me, except that I have to repeat the arguments, once with the types(int a, int b), and once for calling the underlying function(a,b). I was wondering if there is a better way to do that.


回答1:


If what you want is to remove the redundancy of the parameter names in the declaration (appearing once in the declaration list with their types, and once in the call list just as names), you can do this by supplying the names and the types as two separate lists, and just zipping them together with an appropriate two-parameter map macro.

Assuming the above is accurate, it looks like the actual parameter names aren't too important either, so you can lighten up your declaration further by predefining a list of dummy names, and leaving it out of the visible part of the declaration:

#define NARGS(...) NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N

#define CAT(A, B) CAT_(A, B)
#define CAT_(A, B) A##B
#define ID(...) __VA_ARGS__
#define APPEND(L, E) ID(L),E
#define FIRST(A, ...) A
#define REST(A, ...) __VA_ARGS__

#define ZIP(F, L1, L2) CAT(ZIP_, ID(NARGS L1))(F, L1, L2)

#define ZIP_4(F, L1, L2) F(ID(FIRST L1), ID(FIRST L2)), ZIP_3(F, (ID(REST L1)), (ID(REST L2)))
#define ZIP_3(F, L1, L2) F(ID(FIRST L1), ID(FIRST L2)), ZIP_2(F, (ID(REST L1)), (ID(REST L2)))
#define ZIP_2(F, L1, L2) F(ID(FIRST L1), ID(FIRST L2)), ZIP_1(F, (ID(REST L1)), (ID(REST L2)))
#define ZIP_1(F, L1, L2) F(ID(FIRST L1), ID(FIRST L2))
#define ZIP_0(F, L1, L2)


#define GENSYMS (_0, _1, _2, _3, _4, _5, _6, _7, _8, _9)

#define runtime_subclass_method(Superclass,rtype,method,args) \
    rtype method(ZIP(EMIT_DECL, args, GENSYMS)) { \
        if( table.count(#method) == 0 ) return Superclass::method(ZIP(EMIT_CALL, args, GENSYMS)); \
        return table.at(#method)(this, ID(ZIP(EMIT_CALL, args, GENSYMS))); \
    }
#define EMIT_DECL(T, N) T N
#define EMIT_CALL(T, N) N


runtime_subclass_method(Superclass,int,doSomething,(int,int))

Most of this example is utility stuff (such as CAT). Expanding ZIP, NARGS and GENSYMS to accept more arguments (four is a little small to be practical) should be trivial.

The method declaration just accepts a list of argument types, zips them together with names from a predefined list for the declaration, and just zips the length with the names to generate a call list.



来源:https://stackoverflow.com/questions/24643183/how-to-do-a-runtime-subclassing-system

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!