我希望创建一个库,实现以下功能:-
- 用户可以通过
addCallback<Base>(Callback* callback)
方法添加回调函数(通常在第一时间步骤中)。 稍后,在通常不同的
.cpp
文件中,当用户调用actor<Bx>()
时:- 如果
Bx
继承自Base
,则调用callback->callback()
- 否则不执行任何操作
- 如果
(信息) 我确定每个
Bx
都继承自A
。
这是初始代码:-
#include <iostream>
class A{};
class Callback{
public: virtual void callback()=0;
};
template<class Base> void addCallback(Callback* callback){
//???
};
template<class Bx> void actor(){
//???
}
//^^^^^^ my library end here
class B : public A{};
class B1 : public B{};
class C : public A{};
class CallbackCustom1: public Callback{
public: virtual void callback(){std::cout<<"A"<<std::endl;}
};
class CallbackCustom2: public Callback{
public: virtual void callback(){std::cout<<"B"<<std::endl;}
};
int main(){
CallbackCustom1 call1;
CallbackCustom2 call2;
addCallback<A>(&call1);
addCallback<B>(&call2);
//vvv below is usually in another .cpp
actor<B1>(); // should print "A" and "B"
actor<C>(); // should print "A" only
}
如何做到呢?
我提供的糟糕解决方案
解决方案1:std::is_base_of
我非常喜欢使用std::is_base_of<Base,Derive>
。
但是,这是不可能的,因为用户希望在actor<Bx>()
中只调用单个类型Bx
以方便使用。
std::is_base_of
需要两个类的名称而不是一个。
解决方案2 (MCVE演示):虚析构函数+std::function
它可以进行更多优化,但我希望保持简单:-
#include <iostream>
#include <functional>
class A{public: virtual ~A()=default; };
class Callback{
public: virtual void callback()=0;
};
class MyTuple{public:
std::function<bool(A*)> func;
Callback* callback;
};
std::vector<MyTuple> myTuples;
template<class Base> void addCallback(Callback* callback){
std::function<bool(A*)> func=
[](A* a){return dynamic_cast<Base*>(a)!=nullptr;};
MyTuple tuple; tuple.func=func; tuple.callback=callback;
myTuples.push_back(tuple);
}
template<class Bx> void actor(){
Bx b;
for(auto tuple:myTuples){
if(tuple.func(&b)){
tuple.callback->callback();
}
}
}
//^^^^^^ my library end here
它能够工作,但有一些缺点:
- 我必须给
A
添加虚析构函数使其成为多态类型。我感觉这是一种不好的 hack。 - 在我的游戏中,在某些时间步骤中,
A::~A()
每秒潜在被调用 >100,000 次。
我可以通过让B1
和C
变为 final 并通过派生类进行批量删除来降低成本,但在某些地方,这是不适宜和不便的。 - 我必须创建
Bx
的实例仅进行 dynamic_cast 检查。
如果它的构造函数执行某些特殊操作,则可能会导致一些复杂性。
有更好的方法吗?
throw
和catch
来测试非多态类之间的公共继承关系。如果你敢尝试这样做,可以使用一些std::type_index
缓存结果,并确保每个新类型只执行一次该步骤。 - ascheplerthrow
和catch
需要在不同的函数中。 - aschepler