I want to create a library that :-
- User adds callback via
addCallback<Base>(Callback* callback)
(usually at first timestep) Later, usually in a different
.cpp
, whenever user callactor<Bx>()
:-- if
Bx
inherit fromBase
, callcallback->callback()
- else do nothing
- if
(Information) I know for sure that every
Bx
always inherit fromA
.
Here is the initial code :-
#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
}
How to do it?
My poor solutions
Solution 1 : std::is_base_of
I really love to use std::is_base_of<Base,Derive>
.
However, it is impossible because users want to invoke only a single type Bx
in actor<Bx>()
for convenience.
std::is_base_of
need name of two classes not one.
Solution 2 (MCVE demo) : virtual destructor + std::function
It can be optimized more but I want to keep it simple :-
#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
It works, but there are some disadvantages :-
- I have to add virtual destructor to
A
to make it polymorphic. I feel that it is a nasty hack. - In my game, in some time-steps,
A::~A()
is potentially called >100,000 times per seconds.
I can reduce the cost by makeB1
andC
final and do a batch deletion via derived class, but in some places, it is unsuitable and inconvenient. - I have to create instance of
Bx
just for the dynamic_cast check.
This may cause some complication if its constructor do something special.
Are there any better way?