Let's say I want to create a hierarchy that reacts to certain event encoded in string. For example commands coming from network. Idea is that there is a Base
class that handling network connection, receiving buffer, splitting it etc and handling of commands reaction is in derived class (derived class can also add some new words to be handled). So my solution is:
class Base {
public:
typedef void (Base::*Method)();
typedef std::unordered_map<std::string, Method> Methods;
void reactToA();
void reactToB();
Base() :
methods{
{ "A", &Base::reactToA },
{ "B", &Base::reactToB }
}
{
}
void action( const std::string &s )
{
auto f = methods.find( s );
if( f != methods.end() )
(*this.*)(f->second)();
}
protected:
Methods methods;
};
class Child : public Base {
public:
void reactToB();
void reactToC();
Child() {
methods[ "B" ] = static_cast<Method>( &Child::reactToB );
methods[ "C" ] = static_cast<Method>( &Child::reactToC );
}
};
So I have to cast pointer to Child
method to pointer to Base
method. Is that cast well defined? Is there more elegant (or correct, if this leads to UB) solution?
From [expr.static.cast]:
A prvalue of type “pointer to member of
D
of type cv1T
” can be converted to a prvalue of type “pointer to member ofB
” of type cv2T
, whereB
is a base class (Clause 10) ofD
, if a valid standard conversion from “pointer to member ofB
of typeT
” to “pointer to member ofD
of typeT
” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [...] If classB
contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the behavior is undefined.
In our case, &Base::reactToB
can be converted to &Child::reactToB
, but since Base
does not contain the original member, the behavior is undefined.
You'd have to store something like a std::function<void(Base*)>
or void(*)(Base*)
.
If the former, you could add a member function to Base
like:
template <typename C>
void addMethod(std::string const& name, void (C::*method)()) {
methods[name] = [method](Base* b){
(static_cast<C*>(b)->*method)();
};
}
addMethod("B", &Child::reactToB);
If the latter, you could do something like:
methods[ "B" ] = +[](Base* b){
static_cast<Child*>(b)->reactToB();
};