如何使用Boost MPL在函数中有多个返回点?

时间:2014-01-10 16:36:56

标签: c++ boost foreach return 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可以动态发送的集合中的第一个类型,但实际上这个算法与大多数MPL算法一样,静态地生成其结果所以它显然不能与运行时参数(base)一起使用。

唯一被归类为“运行时”的MPL算法是for_each,但我无法弄清楚如何使用它来产生与我的多个return语句相同的效果(我甚至不会知道是否有可能)。 感谢任何可以帮助我的MPL演讲者。

PS:不要告诉我,我应该避免动态演员,或者我可以简单地拥有

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

与现实生活中的问题相比,我简化了代码示例,但我无法改变现有设计。

2 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:1)

这是我找到的解决方案。

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;

欢迎任何简化或改进建议。