【问题标题】:What's the idiomatic way to traverse a boost::mpl::list?遍历 boost::mpl::list 的惯用方式是什么?
【发布时间】:2010-11-14 04:51:14
【问题描述】:

编辑:我已经编辑了示例以更好地类似于我遇到的问题,现在该函数依赖于一个常规参数(而不仅仅是模板参数),这意味着计算 无法在编译时生成


我用手写的typelist 编写了一些代码,现在我们已经开始使用boost,我正在尝试将其移至mpl 库。

我似乎找不到任何适合mpl::list 的文档,我什至无法将代码移植到boost::mpl。我有一种感觉,即使(如果?)我确实成功地移植了代码,它仍然不会是惯用的。能否请您告诉我应该如何使用boost 编写以下代码(请注意,这不是实际代码,这是人为的简化)。

原码 (codepad.org paste)

class nil {};

template <class Head, class Tail = nil>
struct type_list {
    typedef Head head;
    typedef Tail tail;
};

template <class List>
struct foo;

template <class Head, class Tail>
struct foo<type_list<Head, Tail> >{
    template <class T>
    static void* bar(T* obj, size_t size)
    {
        if (sizeof(Head) == size) 
            return reinterpret_cast<Head*>(obj);

        // Otherwise check the rest of the list
        return foo<Tail>::bar(obj, size);
    }
};


template <>
struct foo<nil>
{
    template <class T>
    static void* bar(T*, size_t)  { return NULL; }
};

#include <iostream>

int main()
{
    int n = 3;
    void *p = foo<type_list<char, type_list<bool, 
                      type_list<double, type_list<long> > > >
                 >::bar(&n, 4); 
    std::cout<< p << std::endl;
}

尝试使用 Boost 失败 (codepad.org paste)

#include <boost/mpl/list.hpp>

template <class List>
struct foo{
    template <class T>
    static void* bar(T* obj, size_t size)
    {
        typedef typename boost::mpl::front<List>::type type;
        if (sizeof(type) == size) 
            return reinterpret_cast<type*>(obj);

        // Otherwise check the rest of the list
    return foo<typename List::next>::bar(obj, size);
  }
};

template <>
struct foo<boost::mpl::list0<boost::mpl::na> >
{
    template <class T>
    static void* bar(T*)
    {
        return NULL;
    }
};

#include <iostream>

int main()
{
    int n = 3;
    void *p = foo<boost::mpl::list<char, bool, double, long> >::bar(&n, 4);
    std::cout << p << std::endl;
}

【问题讨论】:

    标签: c++ boost metaprogramming boost-mpl


    【解决方案1】:

    MPL 不适合混合编译时/运行时操作。

    在运行时允许对 MPL 序列进行的唯一操作是“for_each”。对于所有其他情况,您应该自己滚动。

    因此,您应该有效地考虑 MPL 类型并不是要实例化的。

    但是,Boost 中还有其他设施可以做这样的事情。

    旧的:Boost.Tuple

    新:Boost.Fusion > http://spirit.sourceforge.net/dl_more/fusion_v2/libs/fusion/doc/html/index.html

    Boost.Fusion 有点复杂(例如,它集成了视图的概念),但更适合您的示例。

    这并不意味着您不应该使用 MPL。相反,在 Boost.Fusion 参考文档中明确指出,应该使用 MPL 算法进行编译时计算,然后仅在跨越编译时/运行时边界时构建 Boost.Fusion 容器(甚至虽然 Boost.Fusion 容器应该适用于 MPL 算法)。

    所以,保持您的 mpl 实现将结果列表转换为 Boost.Fusion 序列。 然后实例化序列并使用所有 Boost.Fusion 工具。

    【讨论】:

      【解决方案2】:

      像这样使用boost::mpl::fold

      #include <boost/mpl/list.hpp>
      #include <boost/mpl/fold.hpp>
      
      #include <iostream>
      
      using namespace boost::mpl;
      
      // Initial state:
      struct foo_start {
          template <typename T>
          static void * bar( T *, size_t ) { return 0; }
      };
      
      // Folding Step: add This to Prev
      template <typename Prev, typename This>
      struct foo_iteration {
          struct type {
              template <typename T>
              static void * bar( T * obj, size_t size ) {
                  if ( sizeof(This) == size )
                      return reinterpret_cast<This*>(obj);
                  else
                      return Prev::bar( obj, size );
              }
          };
      };
      
      // foo is just calling mpl::fold now:
      template <typename List>
      struct foo : fold< List, foo_start, foo_iteration<_,_> >::type {};
      
      int main() {
          int n = 3;
          void * p = foo< list<char, bool, double, long> >::bar( &n, 4 );
          std::cout << p << std::endl;
      }
      

      在这里打印0,但后来我在amd64上,所以我需要将4更改为8,并得到非零值。

      HTH

      【讨论】:

      • 这发生在编译时,我更新了问题以表明我需要在运行时进行计算。
      • 折叠正是您正在做的。您只需要正确的初始状态和正确的元函数...
      • 我不明白,您能否发布一下您将如何实现我使用 fold 编写的内容?
      【解决方案3】:

      如果我理解正确,您是在运行时传入一个 T 并希望您的 {A、B、C、D} 的 MPL 列表将选择该集合中的正确 T 并对其采取行动?

      我可能错了,但这听起来适合 Boost.Fusion,它是为编译时序列的运行时迭代而设计的。

      【讨论】:

        【解决方案4】:

        警告:有一段时间没有做过任何 C++(与此相关的元编程),所以我可能错了。

        如果我理解正确,您的原始代码会找到第一个类型,它与传递的参数大小相同,并将参数转换为该类型。假设用mpl 实现它基本上可以归结为使用find_if 算法和自定义的书面谓词来测试类型大小(参见上面链接中的示例)。只需typedef 的结果,投射它就完成了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-18
          • 1970-01-01
          • 2020-09-28
          • 2022-08-09
          • 2011-02-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多