Ограничения BOOST_FUSION_ADAPT_STRUCT

Я пробовал играть с макросом BOOST_FUSION_ADAPT_STRUCT и пробовал некоторые наивные вещи, такие как использование 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;
}

Это приводит меня к следующим вопросам:

  • Почему здесь не работает магия Fusion?

  • Использование представления - правильный способ печати адаптированной структуры?

  • Насколько адаптированная структура может использоваться в качестве последовательности Fusion?


person Laurent    schedule 27.11.2013    source источник


Ответы (2)


Из документации boost::fusion:

Операторы ввода-вывода перегружены в пространстве имен boost::fusion.

Это означает, что если вы хотите неявно интегрировать эти operator<<, вам нужно будет внедрить пространство имен boost::fusion в ваше текущее пространство имен (здесь ::) или использовать их явно.

Подводя итог, добавлю:

using namespace boost::fusion;

Должно работать в вашем случае. Или для явного использования вам придется написать:

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

--- ИЗМЕНИТЬ ---

После небольшого чтения кода boost::fusion кажется, что вы запутались из-за поиска Кенига boost::fusion::operators::operator<<, который выбирается в случае, если ваш аргумент является реальным boost::fusion::sequence.

Вот почему вам не нужно внедрять пространство имен boost::fusion или явно вызывать boost::fusion::operator<< для типов, определенных в пространстве имен boost::fusion.

Некоторые пояснения:

Я не буду объяснять всю концепцию поиска Кенига (также известного как Поиск, зависящий от аргумента). - 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);
person NewbiZ    schedule 27.11.2013

Большое спасибо Аурелиен за прекрасное подробное объяснение. Я также нашел это сообщение в группах Google. Как следует из вашего объяснения, самый простой способ заставить все работать - поместить это в пространство имен demo:

namespace demo{
    using boost::fusion::operators::operator<<;
    ...
person Laurent    schedule 29.11.2013