【问题标题】:How to use std::tuple types with boost::mpl algorithms?如何将 std::tuple 类型与 boost::mpl 算法一起使用?
【发布时间】:2011-02-24 01:34:18
【问题描述】:

boost::mpl 算法似乎无法在开箱即用的 std::tuple 类型上工作,例如,以下无法编译(boost-1.46.0,g++ 快照 2011-02-19):

#include <tuple>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>

namespace mpl=boost::mpl;

typedef mpl::vector<int,float,bool> types;
static_assert(mpl::contains<types, float>::value, "vector contains bool");

typedef std::tuple<int,float,bool> types2;
// the following does not compile:
// error: no class template named ‘apply’ in ‘struct boost::mpl::contains_impl<boost::mpl::non_sequence_tag>’
static_assert(mpl::contains<types2, float>::value, "tuple contains bool");

boost::mpl 算法在std::tuple 上工作的最简单方法是什么?

  • 是否 evtl. boost::fusion 提供此功能(就像 boost::tuple 提供的一样)?
  • 如果没有,是否可以轻松地将boost::tuple 的融合实现转移到std::tuple
  • 如果两者都不是,我是否真的必须实现all the intrinsic metafunctions listed in the MPL documentation 或者哪些就足够了? (文档只说“许多内在元函数提供了一个默认实现,在大多数情况下都可以工作”,但不清楚具体是哪些。一些只提供 begin 和 end 的测试没有带我去任何地方)。

【问题讨论】:

  • 我能建议的最简单的答案就是使用 boost's。
  • 但是,如果您有返回std::tuples 的现有代码或期望std::tuples 的代码,则这是不可能的——您必须在两个方向上转换类型,这有点尴尬。我还认为boost::tuple 类似于“早期原型”,之后std::tuple 被建模,因此从长远来看std::tuple 将取代boost::tuple
  • 提升类型很容易,返回可能很难......
  • 查看我的更新。我有一个完整的 to_boostto_std 模板,允许在任意 boost mpl 序列之间进行转换

标签: c++ c++11 tuples boost-mpl


【解决方案1】:

如果你不想将 std::tuple 转换为 mpl 类型,你可以重载标签调度 boost mpl 使用:

#include <tuple>
#include <boost/mpl/sequence_tag.hpp>
#include <boost/mpl/pop_front_fwd.hpp>
#include <boost/mpl/push_front_fwd.hpp>
#include <boost/mpl/push_back_fwd.hpp>
#include <boost/mpl/front_fwd.hpp>
#include <boost/mpl/empty_fwd.hpp>
#include <boost/mpl/size_fwd.hpp>
#include <boost/mpl/at_fwd.hpp>
#include <boost/mpl/back_fwd.hpp>
#include <boost/mpl/clear_fwd.hpp>
#include <boost/mpl/pop_back_fwd.hpp>
#include <boost/mpl/iterator_tags.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/begin_end_fwd.hpp>


namespace boost { namespace mpl {
  namespace aux { struct std_tuple; }

  template<class ... Args>
  struct sequence_tag<std::tuple<Args...> >
  {
    typedef aux::std_tuple type;
  };

  template<>
  struct front_impl< aux::std_tuple >
  {
    template< typename Tuple > struct apply
        : std::tuple_element<0, Tuple>
    {
    };
  };

  template<>
  struct empty_impl< aux::std_tuple >
  {
      template< typename Tuple > struct apply
          : std::integral_constant<bool, std::tuple_size<Tuple>::value == 0>
      {
      };
  };

  template<>
  struct pop_front_impl< aux::std_tuple >
  {
    template< typename Tuple > struct apply;

    template< class First, class ... Types > struct apply<std::tuple<First, Types...>>
    {
      typedef std::tuple<Types...> type;
    };
  };

  template<>
  struct push_front_impl< aux::std_tuple >
  {
    template< typename Tuple, typename T > struct apply;

    template< typename T, typename ... Args >
    struct apply<std::tuple<Args...>, T>
    {
        typedef std::tuple<T, Args...> type;
    };
  };

  template<>
  struct push_back_impl< aux::std_tuple >
  {
    template< typename Tuple, typename T > struct apply;

    template< typename T, typename ... Args  >
    struct apply<std::tuple<Args...>, T>
    {
      typedef std::tuple<Args..., T> type;
    };
  };


  template<>
  struct size_impl< aux::std_tuple >
  {
    template< typename Tuple > struct apply
        : std::tuple_size<Tuple>
    {
    };
  };

  template<>
  struct at_impl< aux::std_tuple >
  {
    template< typename Tuple, typename N > struct apply
        : std::tuple_element<N::value, Tuple>
    {
    };
  };

  template<>
  struct back_impl< aux::std_tuple >
  {
    template< typename Tuple > struct apply
        : std::tuple_element<std::tuple_size<Tuple>::value - 1, Tuple>
    {
    };
  };

  template<>
  struct clear_impl< aux::std_tuple >
  {
    template< typename Tuple > struct apply
    {
      typedef std::tuple<> type;
    };
  };

  template<>
  struct pop_back_impl< aux::std_tuple >
  {
    template<int ...> struct tuple_seq {};
    template<int N, int ...S> struct tuple_gens : tuple_gens<N-1, N-1, S...> {};
    template<int ...S> struct tuple_gens<0, S...>{ typedef tuple_seq<S...> type; };

    template < class Tuple, class Index> struct apply_impl;
    template < class Tuple, int ... S> struct apply_impl<Tuple, tuple_seq<S...>>
    {
      typedef std::tuple<typename std::tuple_element<S, Tuple>::type...> type;
    };

    template< typename Tuple > struct apply : apply_impl<Tuple, typename tuple_gens<std::tuple_size<Tuple>::value - 1>::type> { };
  };

template< class ... Args >
struct tuple_iter;

  template< class ... Args >
  struct tuple_iter<std::tuple<Args...>>
  {
    typedef aux::std_tuple tag;
    typedef forward_iterator_tag category;
  };

template<>
struct begin_impl< aux::std_tuple >
{
  template< class Tuple > struct apply
  {
    typedef tuple_iter<Tuple> type;
  };
};

template<>
struct end_impl< aux::std_tuple >
{
  template< typename > struct apply
  {
    typedef tuple_iter<std::tuple<>> type;
  };
};

template< typename First, class ... Args >
struct deref< tuple_iter<std::tuple<First, Args...> > >
{
  typedef First type;
};

template< typename First, class ... Args >
struct next< tuple_iter<std::tuple<First, Args...>> >
{
  typedef tuple_iter< std::tuple<Args...> > type;
};

} }

以及相关的测试:

#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/empty.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/back.hpp>
#include <boost/mpl/clear.hpp>
#include <boost/mpl/pop_back.hpp>
#include <boost/mpl/contains.hpp>


#include <boost/mpl/aux_/test.hpp>
MPL_TEST_CASE()
{
  typedef std::tuple<int, char, bool> Tuple;
  MPL_ASSERT((is_same<front<Tuple>::type, int>));
  MPL_ASSERT_RELATION( size<Tuple>::type::value, ==, 3 );
  MPL_ASSERT(( is_same< pop_front<Tuple>::type, std::tuple<char, bool> > ));
  MPL_ASSERT(( is_same< push_front<Tuple, unsigned>::type, std::tuple<unsigned, int, char, bool> > ));
  MPL_ASSERT(( is_same< push_back<Tuple, unsigned>::type, std::tuple<int, char, bool, unsigned> > ));
  MPL_ASSERT_RELATION( empty<Tuple>::type::value, ==, false );
  MPL_ASSERT(( is_same< at_c<Tuple, 0>::type, int > ));
  MPL_ASSERT(( is_same< at_c<Tuple, 1>::type, char > ));
  MPL_ASSERT(( is_same< back<Tuple>::type, bool > ));
  MPL_ASSERT(( is_same< clear<Tuple>::type, std::tuple<> > ));
  MPL_ASSERT(( is_same< pop_back<Tuple>::type, std::tuple<int, char> > ));
  MPL_ASSERT(( contains<Tuple, int> ));
  MPL_ASSERT(( contains<Tuple, char> ));
  MPL_ASSERT(( contains<Tuple, bool> ));
  MPL_ASSERT_NOT(( contains<Tuple, unsigned> ));

}

我使用 gcc 4.7.2 和 clang 3.2 对此进行了测试。它应该包含在 mpl 中使用任何东西所需的一切(实际上比它需要的多一点)。您可以将元组视为 mpl::list(前向迭代器编译类型)。所以,为了让它更好玩,你应该知道 boost::mpl::list 做了什么。快速查看 boost/mpl/list/aux_ 目录: begin_end.hpp empty.hpp iterator.hpp pop_front.hpp push_back.hpp size.hpp clear.hpp front.hpp push_front.hpp tag.hpp 是需要实现的相关文件。

【讨论】:

    【解决方案2】:

    从 std::tuple 转换为 boost 类型并返回似乎是最简单的方法

    #include <iostream>
    #include <tuple>
    #include <type_traits>
    #include <boost/mpl/if.hpp>
    #include <boost/mpl/not.hpp>
    #include <boost/mpl/vector.hpp>
    
    namespace mpl = boost::mpl;
    
    template<typename Sequence, typename T>
    struct push_front;
    
    template<template<typename...> class Sequence, typename T, typename ... Args>
    struct push_front< Sequence<Args...>,T> {
      typedef Sequence<T, Args...> type;
    };
    
    template<template<typename...> class To, typename From> struct tuple_change;
    
    template<template<typename...> class To, template<typename...> class From, typename ... Args>
    struct tuple_change<To, From<Args...>>
    {
      typedef To<Args...> type;
    };
    
    template<typename Sequence, size_t N>
    struct at : std::tuple_element<N,Sequence> { };
    
    template<typename Sequence>
    struct empty;
    
    template<template<typename...> class Sequence, typename ... Args>
    struct empty<Sequence<Args...>> {
      typedef Sequence<> type;
    };
    
    template<
      size_t N,
      typename Sequence,
      template<typename> class Pred,
      typename ... Args >
    struct while_impl
    {
      typedef typename mpl::if_c<
        Pred<
            typename at<Sequence, sizeof...(Args) - N -1>::type
        >::value,
        typename push_front<
            typename while_impl<N-1, Sequence, Pred, Args...>::type, 
                typename at<Sequence,sizeof...(Args)-N-1>::type
        >::type,
        typename empty< Sequence > ::type
      >::type type;
    };
    
    template<
      typename Sequence,
      template<typename> class Pred,
      typename ... Args >
    struct while_impl<-1, Sequence, Pred, Args...>
    : empty<Sequence> {
    };
    
    
    template<
      typename Sequence,
      template<typename> class Pred>
    struct while_;
    
    template<
      template<typename...> class Sequence,
      template<typename> class Pred,
      typename ... Args >
    struct while_< Sequence<Args...>, Pred >
    {
      typedef typename while_impl<sizeof...(Args)-1, Sequence<Args...>, Pred, Args...>::type type;
    };
    
    template<typename T>
    struct not_na : mpl::not_< std::is_same<mpl_::na, T> >
    { };
    
    template<template<typename...> class To, typename From>
    struct to_boost;
    
    template<template<typename...> class To, typename...Args >
    struct to_boost<To, std::tuple<Args...> > :
      tuple_change< mpl::vector, std::tuple<Args...> >
    { };
    
    template< typename From >
    struct to_std;
    
    template<template<typename...> class From, typename...Args >
    struct to_std< From<Args...> > :
       while_<typename tuple_change< std::tuple, From<Args...> >::type, not_na>
    { };
    
    static_assert(
    std::is_same<
        mpl::vector< char, int, bool>,
        typename to_boost<mpl::vector, std::tuple<char, int, bool> >::type
      >::value,
    "tuple_change to boost failed");
    
    static_assert(
      std::is_same<
        std::tuple< char, int, bool>,
        typename to_std< mpl::vector<char, int, bool> >::type
      >::value,
    "tuple_change from boost failed");
    
    int main(){ return 0;}
    

    *经过以下测试:
    MacOSx 上的 boost_1_46_0 和 g++-4.5
    Ubuntu 10.10 上的 boost_1_45_0 和 g++-4.5

    【讨论】:

    • @KitsuneYMG 非常感谢。不幸的是,我无法编译您的示例。我更正了 --> at_c, empty<...>::type--> empty<...>::value 并添加了缺少的包含,但仍然出现错误。你能不能 evtl.再看看? -- 对于转换,我有另一个更短的解决方案,我将在一分钟内发布。我的有什么问题吗? -- 最后,我认为转换很糟糕,因为它没有编译时效率。
    • @Lars。我不知道发生了什么。我想我没有得到我的整个测试文件。发布了一个新的。它编译为g++-4.5 -std=c++0x test.cpp
    • @Lars。我可能还应该提到我正在重新处理 boost.mpl 的部分以与std::tuples 一起工作。稍后我会尝试在网上发布一些代码,但我无法理解 MPL::bind。我确实知道上面的代码与我将要发布的代码不同(主要是 template&lt;typename&gt; class X -> typename X 带有一个名为 apply 的子模板结构,就像 boost 一样)。
    • @KitsuneYMG 非常感谢,新版本编译正常。 -- 如果你知道如何避免转换,我会非常感兴趣,因为我根本没有设法这样做......
    • @Lars 具体来说,你需要什么功能?
    【解决方案3】:

    这是我在 std::tuple 和 boost 类型之间转换的版本,但正如上面评论中所说,转换可能不是非常高效的编译时间,即会导致(不必要的)长编译时间。肯定会首选避免转换的解决方案...

    #include <tuple>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/size.hpp>
    #include <boost/mpl/at.hpp>
    
    namespace mpl=boost::mpl;
    
    //_ 1. vector_size and vector_at for std::tuple and mpl sequences
    template <typename SEQ> struct vector_size 
    : mpl::size<SEQ>
    {};
    
    template <typename... TYPES> struct vector_size<std::tuple<TYPES...>> 
    : std::tuple_size<std::tuple<TYPES...>>
    {};
    
    template <typename SEQ, size_t N> struct vector_at 
    : mpl::at_c<SEQ, N>
    {};
    
    template <typename... TYPES, size_t N> struct vector_at<std::tuple<TYPES...>, N> 
    : std::tuple_element<N, std::tuple<TYPES...>>
    {};
    
    //_ 2. convert
    template <template <typename...> class COLLECT,
          typename SEQ, size_t N, typename... ARGS>
    struct convert_helper 
      : convert_helper<COLLECT, SEQ, N-1, typename vector_at<SEQ, N-1>::type, ARGS...>
    {};
    
    template <template <typename...> class COLLECT, typename SEQ, typename... ARGS>
    struct convert_helper<COLLECT, SEQ, 0, ARGS...> {
      typedef COLLECT<ARGS...> type;
    };
    
    template <template <typename...> class COLLECT, typename SEQ>
    struct convert 
      : convert_helper<COLLECT, SEQ, vector_size<SEQ>::value>
    {};
    
    //_ 3. tests
    typedef std::tuple<int, float, bool> types;
    typedef mpl::vector<int, float, bool> types_v;
    
    static_assert(std::is_same<convert<std::tuple, types_v>::type, types>::value, "boost2std works");
    static_assert(std::is_same<convert<mpl::vector,types>::type, types_v>::value, "std2boost works");
    
    int main() {}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-04
      • 2022-01-07
      • 1970-01-01
      • 2011-05-05
      • 2021-06-06
      相关资源
      最近更新 更多