How to make a runtime subclass system
I am implementing a subclass system that can be defined at runtime. I have a subclass that forwards a method for a table ( std::map
), if the method is not available on the table, the superclass method is used.
Example (function parameters and return type are not a 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);
}
};
I am now working on macros to make it easier to create classes _subclass
. 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 works well for me, except that I have to repeat the arguments once with types ( int a, int b
) and once to call the base function ( a,b
). I was wondering if there is a better way to do this.
source to share
If you want to remove the redundancy of parameter names in a declaration (appearing once in the declaration list with their types and once in the invocation list just like names), you can do so by specifying the names and types as two separate lists, and simply button them together with a matching two-parameter macro.
Assuming the above is accurate, it looks like the actual parameter names are not too important either, so you can lighten your declaration by pre-defining the list of bogus names and leaving it outside 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))
In most cases, this is a utility (for example CAT
). Expanding ZIP
, NARGS
and GENSYMS
to take more arguments (four is a bit of little practical) should be trivial.
The method declaration simply takes a list of argument types, zips them along with the names from the predefined list to declare, and simply zips the length with names to generate the invocation list.
source to share