C++ 在std::is_Base_of<;这里&燃气轮机;
我想创建一个库,其中:C++ 在std::is_Base_of<;这里&燃气轮机;,c++,templates,inheritance,c++14,dynamic-cast,C++,Templates,Inheritance,C++14,Dynamic Cast,我想创建一个库,其中: 用户通过addCallback(callback*callback)添加回调(通常在第一个时间步) 之后,通常在不同的.cpp中,每当用户调用actor():- 如果Bx继承自Base,则调用callback->callback() 否则什么也不做 (信息)我确信每个Bx总是从A继承 以下是初始代码:- #include <iostream> class A{}; class Callback{ public: virtual void call
addCallback(callback*callback)
添加回调(通常在第一个时间步).cpp
中,每当用户调用actor()
:-
- 如果
继承自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
}
这是可行的,但也有一些缺点:-
- 我必须在
中添加虚拟析构函数,使其具有多态性。我觉得这是一个令人讨厌的黑客行为李>A
- 在我的游戏中,在某些时间步中,
可能每秒调用100000次以上。A::~A()
我可以通过制作
和B1
final并通过派生类进行批量删除来降低成本,但在某些地方,这样做不合适,也不方便李>C
- 我必须创建
的实例,仅用于动态\u cast检查。Bx
如果它的构造函数做了一些特殊的事情,这可能会导致一些复杂性李>
还有更好的办法吗 您是否可以要求用户指定允许的
Base
类型集?在这种情况下,任务变得简单():
用法如下:
MyCallbacks::addCallback<A>(&call1);
MyCallbacks::addCallback<B>(&call2);
// MyCallbacks::addCallback<B1>(&call2);// compile error (good)
//vvv below is usually in another .cpp
std::cout << R"(should print "A" and "B":)" << std::endl;
MyCallbacks::actor<B1>();
std::cout << R"(should print "A" only:)" << std::endl;
MyCallbacks::actor<C>();
MyCallbacks::addCallback(&call1);
MyCallbacks::addCallback(&call2);
//MyCallbacks::addCallback(&call2);//编译错误(良好)
//下面的vvv通常位于另一个.cpp中
std::cout我想到了一个更糟糕的黑客:throw
和catch
可能被滥用,在运行时测试非多态类之间的公共继承关系。如果你敢这么做,你可以使用一些std::type_index
来缓存结果,并确保每遇到一个新类型,这一步只执行一次。@aschepper-like-in?一个类似的想法,是的,但对于你的用例,throw
和catch
需要具有不同的功能。我盯着代码看了一会儿。这很难测试,您能提供一些MVCE(例如)吗?它看起来很有用。谢谢。@CPP初学者:添加了在线演示的链接(见我答案的第一段)。单例样式可能是糟糕的设计,但约束类型的思想也可以应用于非单例:CallbackSystem callbacks{};callbacks.add_callback(&call1);/*。。。传递实例(通过引用)callbacks.actor()
在您答案的第一个版本中(我更喜欢),用户需要额外键入。它有点容易出错,而且不易维护。(它比CRTP更干净、更好。)在这个版本中,用户必须使用正确的回调来调用actor,例如CallbackSystem::actor()
。尽管有MyCallbacks
alias,回调的有用性还是降低了。顺便说一下,为了简化MCVE,我使用了单例。我实际上并不使用这种模式。谢谢。:)@CPP初学者:是的,我同意我的建议(用户必须指定每个类的基)是容易出错的。此外,为了收集用户提供的信息,还需要某种程度的元编程。我还同意,如果使用CallbackSystem::actor()
而不是MyCallbacks::actor(),则当前版本是脆弱的代码>。请注意,如果回调是在非单例实例中处理的,则这不是问题:voiddo_stuff(MyCallbacks&callbacks){actor(callbacks);}
。
static_assert(__cplusplus >= 201703L, "example for C++17, but C++14 possible");
#include <iostream>
#include <type_traits>
#include <vector>
struct Callback {
virtual void callback() = 0;
};
template<class... RegisteredBases>
struct CallbackSystem {
template<class Base>
static auto& callbacks_for() {
static std::vector<Callback*> callbacks_for_base_{};
//
// For each `Base`, the callbacks are stored in a different vector.
// This way, we can avoid the in-loop branch (see `actor_impl`).
//
// TODO: consider performance cost of bad memory locality (can be
// improved if necessary).
//
return callbacks_for_base_;
}
template<class Base>
static void addCallback(Callback* callback) {
static_assert((... || std::is_same<Base, RegisteredBases>{}));
callbacks_for<Base>().push_back(callback);
}
template<class Derived, class RegisteredBase>
static void actor_impl() {// called from `actor` for each RegisteredBase
if(std::is_base_of<RegisteredBase, Derived>{}) {// branch outside loop
// if `RegisteredBase` matches then process all its callbacks
for(Callback* callback : callbacks_for<RegisteredBase>()) {
callback->callback();
}
}
}
template<class Derived>
static void actor() {
(actor_impl<Derived, RegisteredBases>(), ...);
}
};
using MyCallbacks = CallbackSystem<A, B> {};
MyCallbacks::addCallback<A>(&call1);
MyCallbacks::addCallback<B>(&call2);
// MyCallbacks::addCallback<B1>(&call2);// compile error (good)
//vvv below is usually in another .cpp
std::cout << R"(should print "A" and "B":)" << std::endl;
MyCallbacks::actor<B1>();
std::cout << R"(should print "A" only:)" << std::endl;
MyCallbacks::actor<C>();