Consider the following code:
struct Param {};
struct Base {
virtual char f(Param const &) const = 0;
};
struct A : Base {
explicit A(Param const &) {}
virtual char f(Param const &) const {return 'A';}
};
struct B : Base {
explicit B(Param const &) {}
virtual char f(Param const &) const {return 'B';}
};
struct C : Base {
explicit C(Param const &) {}
virtual char f(Param const &) const {return 'C';}
};
char my_function(Param const & param, Base const & base)
{
if(A const * const p = dynamic_cast<A const *>(&base)) {
return p->f(param);
}
if(B const * const p = dynamic_cast<B const *>(&base)) {
return p->f(param);
}
if(C const * const p = dynamic_cast<C const *>(&base)) {
return p->f(param);
}
return '0';
}
Then if I write
Param x, y;
std::cout << my_function(x, B(y)) << std::endl;
it outputs B
.
My goal is to change the implementation of my_function
so that it can support a set of subtypes of Base
defined at compile-time. Here I have hand-written the expanded code I would obtain for the set of types {A, B, C}
.
I would like to templatize my_function
with a set of types, and call it this way:
std::cout << my_function<boost::mpl::set<A, B, C> >(x, B(y)) << std::endl;
outputting B
, or, for instance:
std::cout << my_function<boost::mpl::set<A, C> >(x, B(y)) << std::endl;
outputting 0
.
I don't know which MPL construct to use to achieve this result.
I initially thought a call to find_if
could allow to find the first type in the set to which base
can be dynamic_casted, but in fact this algorithm, like most MPL algorithms, produces its result statically so it obviously can't be used with a runtime parameter (base
).
The only MPL algorithm that is categorized "runtime" is for_each
, but I don't manage to figure out how to use it to produce the same effect as my multiple return statements (and I even don't know whether it is possible).
Thanks to any MPL-speaker who could help me.
PS: Don't tell me I should avoid dynamic casts or I could simply have
char my_function(Param const & param, Base const & base) {return base.f(param);}
I have simplified the code sample compared with my real-life problem and I cannot change the existing design.
Something like this should work :
struct functor
{
public:
functor(Param const & param, Base const & base)
: param_(param),
base_(base),
found_(false)
{
}
template<typename Child>
void operator()(Child*)
{
if (!found_)
{
if(Child const * const p = dynamic_cast<Child const *>(&base_))
{
value_ = p->f(param_);
found_ = true;
}
}
}
char get_value()
{
return value_;
}
private:
Param const & param_;
Base const & base_;
bool found_;
char value_;
};
template <typename ...Types>
char my_function(Param const & param, Base const & base)
{
typedef boost::mpl::vector<Types...>::type children;
functor f(param, base);
boost::mpl::for_each<children>(std::ref(f));
return f.get_value();
}
int main()
{
Param x, y;
std::cout << my_function<A*, B*, C*>(x, B(y)) << std::endl;
return 0;
}