Как использовать Boost MPL, чтобы иметь несколько точек возврата в функции?

Рассмотрим следующий код:

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';
}

Тогда если я напишу

Param x, y;
std::cout << my_function(x, B(y)) << std::endl;

это выводит B,

Моя цель — изменить реализацию my_function так что он может поддерживать набор подтипов Base определяется во время компиляции. Здесь я написал написанный вручную расширенный код для набора типов {A, B, C},
Я хотел бы шаблонизировать my_function с набором типов, и назовите это так:

std::cout << my_function<boost::mpl::set<A, B, C> >(x, B(y)) << std::endl;

Выведение Bили, например:

std::cout << my_function<boost::mpl::set<A, C> >(x, B(y)) << std::endl;

Выведение 0,

Я не знаю, какую конструкцию MPL использовать для достижения этого результата.

Я изначально думал, что вызов find_if может позволить найти первый тип в наборе, к которому base может быть dynamic_casted, но на самом деле этот алгоритм, как и большинство алгоритмов MPL, выдает свой результат статически, поэтому его явно нельзя использовать с параметром времени выполнения (base).

Единственный алгоритм MPL, который классифицируется как «время выполнения», это for_each, но мне не удается понять, как использовать его для получения того же эффекта, что и мои множественные операторы return (и я даже не знаю, возможно ли это).
Спасибо любому MPL-спикеру, который мог мне помочь.

PS: не говорите мне, что я должен избегать динамических бросков, или я мог бы просто иметь

char my_function(Param const & param, Base const & base) {return base.f(param);}

Я упростил пример кода по сравнению с моей реальной проблемой, и я не могу изменить существующий дизайн.

2

Решение

Примерно так должно работать:

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;
}
2

Другие решения

Вот решение, которое я нашел.

namespace detail {

template <typename First, typename Last>
struct my_function_impl {
static char apply(Param const & param, Base const & base)
{
BOOST_MPL_ASSERT_NOT((boost::is_same<First, Last>));
typedef typename boost::mpl::deref<First>::type Child;
BOOST_MPL_ASSERT((boost::is_base_of<Base, Child>));
if(Child const * const p = dynamic_cast<Child const *>(&base)) {
return p->f(param);
}
typedef typename boost::mpl::next<First>::type Next;
return my_function_impl<Next, Last>::apply(param, base);
}
};

template <typename It>
struct my_function_impl<It, It> {
static char apply(Param const &, Base const &)
{
return '0';
}
};

} // namespace detail

template <typename ContainerOfTypes>
char my_function(Param const & param, Base const & base)
{
typedef typename boost::mpl::begin<ContainerOfTypes>::type Begin;
typedef typename boost::mpl::end<ContainerOfTypes>::type End;
return detail::my_function_impl<Begin, End>::apply(param, base);
}

И звонок:

std::cout << my_function<boost::mpl::set<A, B, so::Base> >(x, C(y)) << std::endl;

Любое предложение по упрощению или улучшению приветствуется.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector