Перегруженный оператор вывода не найден для выражения Boost.Spirit

Это продолжение этот вопрос&. Теперь у меня есть несколько структур данных в namespace ast, разделенный на два подпространства имен (algebraic а также numeric), которые соответствуют двум различным форматам, которые распознает грамматика.

namespace ast {
namespace algebraic {
struct occupance
{
char pc;
char col;
int row;
};

using pieces = std::vector<occupance>;

struct placement
{
char c;
boost::optional<pieces> p;
};
}

namespace numeric {
struct occupance
{
char pc;
int sq;
};

struct range
{
occupance oc;
int sq;
};

using pieces = std::vector<boost::variant<range, occupance>>;

struct placement
{
char c;
boost::optional<pieces> p;
};
}

struct fen
{
char c;
std::vector<boost::variant<numeric::placement, algebraic::placement>> p;
};
}

Рабочий парсер Жить на Колиру

Проблема начинается, когда я пытаюсь определить потоковые операторы для различных типов. С общим operator<< принимая vector<T> в том же пространстве имен, что и различные ast структуры (как в связанном Q&А) все хорошо. Но однажды у меня есть два подпространства имен algebraic а также numeric и определите различные операторы в этих пространствах имен:

namespace ast {
template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> const& v)
{
os << "{";
for (auto const& el : v)
os << el << " ";
return os << "}";
}

namespace algebraic {
std::ostream& operator<<(std::ostream& os, occupance const& oc)
{
return os << oc.pc << oc.col << oc.row;
}

std::ostream& operator<<(std::ostream& os, placement const& p)
{
return os << p.c << " " << p.p;
}
}   // algebriac

namespace numeric {
std::ostream& operator<<(std::ostream& os, occupance const& oc)
{
return os << oc.pc << oc.sq;
}

std::ostream& operator<<(std::ostream& os, range const& r)
{
for (auto sq = r.oc.sq; sq <= r.sq; ++sq)
os << r.oc.pc << sq << " ";
return os;
}

std::ostream& operator<<(std::ostream& os, placement const& p)
{
return os << p.c << " " << p.p;
}
}   // numeric
}   // ast

Жить на Колиру соответствующие операторы больше не найдены.

In file included from main.cpp:4:
/usr/local/include/boost/optional/optional_io.hpp:47:21: error: invalid operands to binary expression ('basic_ostream<char, std::__1::char_traits<char> >' and 'const std::__1::vector<ast::algebraic::occupance, std::__1::allocator<ast::algebraic::occupance> >')
else out << ' ' << *v ;
~~~~~~~~~~ ^  ~~
main.cpp:79:37: note: in instantiation of function template specialization 'boost::operator<<<char, std::__1::char_traits<char>, std::__1::vector<ast::algebraic::occupance, std::__1::allocator<ast::algebraic::occupance> > >' requested here
return os << p.c << " " << p.p;

Вопрос: как определить различные операторы потоковой передачи для правильной печати соответствующего AST?

2

Решение

Это заурядный ADL. (Что такое "Аргумент-зависимый поиск" (он же ADL, или "Поиск Кенига")?)

Либо дублировать operator<< для каждого подпространства или используйте «тег» ADL hook.

Дублирование

Жить на Колиру

ADL Hook

Обратите внимание adl_hook тип и использование:

Жить на Колиру

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <vector>

namespace x3 = boost::spirit::x3;

namespace ast {
struct adl_hook;

template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
os << "{"; for (auto const& el : v) os << el << " "; return os << "}";
}

namespace algebraic {

template <typename tag = adl_hook>
struct occupance_t
{
char pc;
char col;
int row;
};

using occupance = occupance_t<>;

using pieces = std::vector<occupance>;

template <typename tag = adl_hook>
struct placement_t
{
char c;
boost::optional<pieces> p;
};

using placement = placement_t<>;
}

namespace numeric {

template <typename tag = adl_hook>
struct occupance_t
{
char pc;
int sq;
};

using occupance = occupance_t<>;

struct range
{
occupance oc;
int sq;
};

using piece  = boost::variant<range, occupance>;
using pieces = std::vector<piece>;

template <typename tag = adl_hook>
struct placement_t
{
char c;
boost::optional<pieces> p;
};

using placement = placement_t<>;
}

struct fen
{
char c;
using placement = boost::variant<numeric::placement, algebraic::placement>;
std::vector<placement> p;
};

namespace algebraic {
std::ostream& operator<<(std::ostream& os, occupance const& oc)
{
return os << oc.pc << oc.col << oc.row;
}

std::ostream& operator<<(std::ostream& os, placement const& p)
{
return os << p.c << " " << p.p;
}
}   // algebriac

namespace numeric {
std::ostream& operator<<(std::ostream& os, occupance const& oc)
{
return os << oc.pc << oc.sq;
}

std::ostream& operator<<(std::ostream& os, range const& r)
{
for (auto sq = r.oc.sq; sq <= r.sq; ++sq)
os << r.oc.pc << sq << " ";
return os;
}

std::ostream& operator<<(std::ostream& os, placement const& p)
{
return os << p.c << " " << p.p;
}
}   // numeric
}   // ast

BOOST_FUSION_ADAPT_STRUCT(ast::algebraic::occupance, pc, col, row)
BOOST_FUSION_ADAPT_STRUCT(ast::algebraic::placement, c,  p )

BOOST_FUSION_ADAPT_STRUCT(ast::numeric::occupance, pc, sq)
BOOST_FUSION_ADAPT_STRUCT(ast::numeric::range,     oc, sq)
BOOST_FUSION_ADAPT_STRUCT(ast::numeric::placement, c,  p )

BOOST_FUSION_ADAPT_STRUCT(ast::fen, c,  p )

namespace grammar {
auto const colon = x3::lit(':');
auto const comma = x3::lit(',');
auto const dash  = x3::lit('-');
auto const dot   = x3::lit('.');

template<typename T>
auto as_rule = [](auto p) { return x3::rule<struct _, T>{} = x3::as_parser(p); };

auto const piece_type = x3::char_('K') | x3::attr('M');
auto const color      = x3::char_("BW");

namespace algebraic {
auto const square     = x3::lower >> x3::uint_;
auto const occupance  = as_rule<ast::algebraic::occupance> ( piece_type >> square      );
auto const pieces     = as_rule<ast::algebraic::pieces>    ( occupance % comma         );
auto const placement  = as_rule<ast::algebraic::placement> ( colon >> color >> -pieces );
}   // algebraic

namespace numeric {
auto const square     = x3::uint_;
auto const occupance  = as_rule<ast::numeric::occupance> ( piece_type >> square        );
auto const range      = as_rule<ast::numeric::range>     ( occupance >> dash >> square );
auto const pieces     = as_rule<ast::numeric::pieces>    ( (range | occupance) % comma );
auto const placement  = as_rule<ast::numeric::placement> ( colon >> color >> -pieces   );
}   // numeric

auto const fen = as_rule<ast::fen> ( color >> (x3::repeat(2)[numeric::placement] | x3::repeat(2)[algebraic::placement]) >> -dot );
}   // grammar

int main() {
for (std::string const t : {
"W:Wa1,c1,e1,g1,b2,d2,f2,h2,a3,c3,e3,g3:Bb8,d8,f8,h8,a7,c7,e7,g7,b6,d6,f6,h6",
"W:BKa1,Ka3:WKb8,Kd8",
"B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29",
"B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12",
"W:B1-20:W31-50",   // initial position
"W:B:W",            // empty board
"W:B1:W",           // only black pieces
"W:B:W50"           // only white pieces
}) {
auto b = t.begin(), e = t.end();
ast::fen data;
bool ok = phrase_parse(b, e, grammar::fen, x3::space, data);

std::cout << t << "\n";
if (ok) {
std::cout << "\t Parsed: \n" << boost::fusion::as_vector(data) << "\n";
} else {
std::cout << "Parse failed:\n";
std::cout << "\t on input: " << t << "\n";
}
if (b != e)
std::cout << "\t Remaining unparsed: '" << std::string(b, e) << '\n';
}
}
3

Другие решения

В дополнение к прекрасному ответу @sehe, я решил обойти все проблемы ADL и следовать Spirit X3 лекционные заметки и написать выделенный printer класс, который зацикливается на vector элементы от руки.

struct printer
{
std::ostream& out;

printer(std::ostream& os) : out(os) {}

auto operator()(algebraic::occupance const& oc) const
{
out << oc.piece_ << oc.column_ << oc.row_ << " ";
}

auto operator()(std::vector<algebraic::color_placement> const& cps) const
{
for (auto const& cp : cps) {
out << " { " << cp.color_ << " : ";
for (auto const& elem : cp.placement_)
(*this)(elem);
out << " } ";
}
}

auto operator()(numeric::occupance const& oc) const
{
out << oc.piece_ << oc.square_ << " ";
}

auto operator()(numeric::range const& r) const
{
auto const occupance = r.occupance_;
for (auto square = occupance.square_; square <= r.square_; ++square)
out << occupance.piece_ << square << " ";
}

auto operator()(std::vector<numeric::color_placement> const& cps) const
{
for (auto const& cp : cps) {
out << " { " << cp.color_ << " : ";
for (auto const& elem : cp.placement_)
boost::apply_visitor(*this, elem);
out << " } ";
}
}

auto operator()(fen const& f) const
{
out << f.color_;
boost::apply_visitor(*this, f.color_placements_);
}
};

Жить на Колиру

Кроме того, я немного очистил грамматику, уменьшив boost::optional<std::vector<T>> к совместимому атрибуту std::vector<T>так, чтобы больше не зависело от boost::optional остаются.

2