BOOST_FUSION_ADAPT_STRUCT的局限性

9

我尝试使用BOOST_FUSION_ADAPT_STRUCT宏,并尝试了一些天真的事情,比如使用Fusion来打印任意结构。

从这个文档中给出的示例代码开始,我无法对我的适配结构执行一些与Fusion序列允许的操作。

#include <boost/fusion/adapted.hpp>
#include <boost/fusion/sequence/io/out.hpp>
#include <boost/fusion/sequence/intrinsic.hpp>
#include <boost/fusion/view.hpp>
#include <iostream>

namespace fuz = boost::fusion;

namespace demo
{
    struct employee
    {
        std::string name;
        int age;
    };
}

// demo::employee is now a Fusion sequence
BOOST_FUSION_ADAPT_STRUCT(
    demo::employee,
    (std::string, name)
    (int, age))

int main()
{
    // tried to initialize an employee like a fusion sequence
    // but it didnt work
    // demo::employee e("bob", 42);

    demo::employee e;
    e.name = "bob";
    e.age = 42;

    // Access struct members with fusion random access functions
    // ok
    std::cout << fuz::at_c<0>(e) << std::endl; 

    // tried to print the struct like any othe fusion sequence
    // didnt work
    // std::cout << e << std::endl;

    // I made it work by using a fusion view
    // is it the right way?
    std::cout << fuz::as_nview<0, 1>(e) << std::endl;
}

这让我想到了以下问题:
  • 为什么融合魔法在这里不起作用?

  • 使用视图打印适配的结构是正确的方式吗?

  • 一个适配的结构可以被用作融合序列的多远?

2个回答

12

来自 boost::fusion 文档:

输入输出操作符在命名空间 boost::fusion 中被重载。

这意味着,如果您想要隐式地使用这些 operator<<,您需要将命名空间 boost::fusion 注入到当前的命名空间中(在这里是 ::),或者显式地使用它们。

总之,添加以下代码:

using namespace boost::fusion;

应该可以在你的情况下工作。 或者为了明确使用,你将不得不编写:

boost::fusion::operator<<(std::cout, e) << std::endl;

--- 编辑 ---

经过一些阅读 boost::fusion 的代码,似乎你对于 Koenig's lookupboost::fusion::operators::operator<< 的使用会感到困惑。在参数为一个真正的 boost::fusion::sequence 时,就会选择这种方法。

这就是为什么你不需要引入 boost::fusion 命名空间,也不需要显式地调用 boost::fusion::operator<< 来处理在 boost::fusion 命名空间中定义的类型。

一些解释:

我不会在这里解释整个 Koenig lookup 概念(也称为 Argument Dependent Lookup - ADL),因为那不是重点。但基本上它表示,如果你正在使用一个变量,其类型位于一个命名空间中,则函数查找将扩展至该参数的命名空间。

在这种特殊情况下,包含 boost/fusion/sequence/io/out.hpp 将定义 boost::fusion::operator::operator<<,然后将注入到 boost::fusion 命名空间中。

$ cat /usr/local/include/boost/fusion/sequence/io/out.hpp
[...]
namespace boost { namespace fusion
{
    [...]
    namespace operators
    {
        template <typename Sequence>
        inline typename
            boost::enable_if<
               fusion::traits::is_sequence<Sequence>
              , std::ostream&
            >::type // this is just a SFINAE trick to ensure
                    // the function will only be selected for
                    // actual boost::fusion::sequence
        operator<<(std::ostream& os, Sequence const& seq)
        {
            return fusion::out(os, seq); // this will print out the sequence
        }
    }
    using operators::operator<<; // here the operator<< is injected
                                 // in boost::fusion
}}

这意味着使用 operator<< 并且参数类型属于 boost::fusion 命名空间的调用将会找到正确的重载函数。

使用不属于该命名空间的参数调用将无法解析正确的operator<<重载函数(例如在您的示例中)。

您可以通过将自定义类型定义在 boost::fusion 命名空间中来检查。

namespace boost { namespace fusion {
struct employee
{
  std::string name;
  int age;
};
}}

BOOST_FUSION_ADAPT_STRUCT(
    boost::fusion::employee,
    (std::string, name)
    (int, age))

[...]
boost::fusion::employee e;
std::cout << e << std::endl; // ADL will work here

顺便提一下:如果你想调试这种名称查找问题,你应该使用 gdb 。这样你就可以始终知道选择了哪个重载函数。在这种情况下:

$ cat fusion.cpp
#include <iostream>
#include <cstdlib>

#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/sequence/io.hpp>

int main(int, char**)
{
  boost::fusion::vector<int, char> foo(42, '?');
  std::cout << foo << std::endl;

  return EXIT_SUCCESS;
}

$ gdb -q ./fusion
Reading symbols for shared libraries ... done
(gdb) b 10
Breakpoint 1 at 0x1000012f7: file fusion.cpp, line 10.
(gdb) r
Starting program: /Users/avallee/Projects/tmp/fusion
Reading symbols for shared libraries ++............................. done

Breakpoint 1, main (unnamed_arg=0x7fff5fbffb60, unnamed_arg=0x7fff5fbffb60) at fusion.cpp:10
10    std::cout << foo << std::endl;
(gdb) s
boost::fusion::operators::operator<< <boost::fusion::vector<int, char, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > (os=@0x7fff762b5f10, seq=@0x7fff5fbffb18) at out.hpp:38
38              return fusion::out(os, seq);

5
非常感谢您,Aurelien,为我们提供了详细的解释。我还找到了这篇Google群组上的文章。正如您所解释的那样,让事情运行起来的最简单方法是将以下内容放入演示名称空间中:
namespace demo{
    using boost::fusion::operators::operator<<;
    ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接