【问题标题】:How to do a runtime subclassing system如何做一个运行时子类化系统
【发布时间】:2014-07-08 23:35:14
【问题描述】:

我正在做一个可以在运行时定义的子类系统。我有一个子类转发表的方法(std::map),如果表中没有方法,则使用超类方法。

示例(函数参数和返回类型不是问题,我只是简化了):

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

现在我正在研究宏以简化创建_subclass 类的过程。现在我得到了以下宏:

#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

这对我来说效果很好,除了我必须重复参数,一次使用类型(int a, int b),一次用于调用底层函数(a,b)。我想知道是否有更好的方法来做到这一点。

【问题讨论】:

  • 我想知道如何,但你说你已经解决了地图中函数签名的问题。那么可变参数模板可能有用吗?喜欢template&lt;typename ...Args&gt; void doSomething(Args ...args) { table.at("doSomething")(this, args...) }
  • @leemes 这样做是行不通的,因为我需要 doSomething 覆盖 SuperClass::doSomething,你知道我的意思吗?顺便说一句,我使用void* 解决了参数传递问题并将其转换为正确的std::function 变体。
  • 为什么?您仍然必须将其编写为代码,因此对我来说使用传统的虚函数似乎更明智。而且很有可能快 10-100 倍。
  • @MatsPetersson std::function 是用解释语言定义的,比如 Lua 或 Python。

标签: c++ metaprogramming c-preprocessor


【解决方案1】:

如果您想要删除声明中参数名称的冗余(在声明列表中出现一次及其类型,一次在调用列表中作为名称出现),您可以通过提供名称和将类型作为两个单独的列表,并使用适当的双参数映射宏将它们压缩在一起。

假设以上是准确的,看起来实际参数名称也不是太重要,因此您可以通过预定义一个虚拟名称列表来进一步简化您的声明,并将其排除在声明的可见部分之外:

#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))

此示例的大部分内容都是实用程序(例如CAT)。扩展ZIPNARGSGENSYMS 以接受更多参数(四个有点小,实用)应该是微不足道的。

方法声明只接受参数类型列表,将它们与声明的预定义列表中的名称压缩在一起,然后将长度与名称压缩在一起以生成调用列表。

【讨论】:

    猜你喜欢
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 1970-01-01
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多