0

I have the following test that works fine in a test app but in my big code it doesn't compile. What happens is that the parser is trying to hand me Char instead of a std::string. I've looked for any possible reason I would get different result, but, to no avail. Here is the code that works. Thanks to sehe for the std::variant adapter. here and here

#include <iostream>
#include <unordered_map>
#include <variant>
#include <string>
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/adapted/std_pair.hpp>

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

using rpn_val = std::variant<std::monostate, int64_t, std::wstring, double>;

std::ostream& operator << (std::ostream& os, const std::monostate& rp) { return os << "NULL"; }

struct rpn_val_print_ostream {
    std::ostream& os;
    rpn_val_print_ostream(std::ostream& os) : os(os) {}
    void operator()(const std::monostate& item) { os << " NULL "; }
    void operator()(const int64_t& item) { os << " int: " << std::dec << item; }
    void operator()(const std::wstring& item) { os << " str: "; }
    void operator()(const double& item) { os << " double: " << item; }
};

std::ostream& operator << (std::ostream& os, const rpn_val& rp) { std::visit(rpn_val_print_ostream(os), rp); return os; }

namespace boost::spirit::x3::traits {
    template<typename... T>
    struct is_variant<std::variant<T...> >
        : mpl::true_ {};

    template <typename Attribute, typename... T>
    struct variant_has_substitute_impl<std::variant<T...>, Attribute>
    {
        typedef std::variant<T...> variant_type;
        typedef typename mpl::transform<
            mpl::list<T...>
            , unwrap_recursive<mpl::_1>
        >::type types;
        typedef typename mpl::end<types>::type end;
        typedef typename mpl::find<types, Attribute>::type iter_1;
        typedef typename
            mpl::eval_if<
            is_same<iter_1, end>,
            mpl::find_if<types, traits::is_substitute<mpl::_1, Attribute>>,
            mpl::identity<iter_1>
            >::type
            iter;
        typedef mpl::not_<is_same<iter, end>> type;
    };

    template <typename Attribute, typename... T>
    struct variant_find_substitute<std::variant<T...>, Attribute>
    {
        typedef std::variant<T...> variant_type;
        typedef typename mpl::transform<
            mpl::list<T...>
            , unwrap_recursive<mpl::_1>
        >::type types;
        typedef typename mpl::end<types>::type end;
        typedef typename mpl::find<types, Attribute>::type iter_1;
        typedef typename
            mpl::eval_if<
            is_same<iter_1, end>,
            mpl::find_if<types, traits::is_substitute<mpl::_1, Attribute> >,
            mpl::identity<iter_1>
            >::type
            iter;
        typedef typename
            mpl::eval_if<
            is_same<iter, end>,
            mpl::identity<Attribute>,
            mpl::deref<iter>
            >::type
            type;
    };
    template <typename... T>
    struct variant_find_substitute<std::variant<T...>, std::variant<T...> >
        : mpl::identity<std::variant<T...> > {};

#if defined(BOOST_SPIRIT_X3_DEBUG)
        template <typename Out, typename... Ts>
        struct print_attribute_debug<Out, std::variant<Ts...>, void> {
            template <typename V>
            static void call(Out& out, V const& value) {
                std::visit([&out](auto const& v) {
                    x3::traits::print_attribute(out, v);
                    }, value);
            }
        };
#endif
}

namespace x3 = boost::spirit::x3;

template <typename T>
struct as_type {
    template <typename Expr>
    auto operator[](Expr&& expr) const {
        return x3::rule<class _, T>{"as"} = x3::as_parser(std::forward<Expr>(expr));
    }
};
template <typename T> static const as_type<T> as = {};

template <>
void x3::traits::detail::move_to<std::string, rpn_val>(std::string& src, rpn_val& dest, variant_attribute, mpl::false_)
{
    //TODO move from string to wstring;
}

const auto quoted_string = x3::lit('"') > x3::lexeme[*((x3::alnum | '_') - '"')] > '"';
const auto int_rule = x3::int64 >> !x3::lit(".");
const auto variant_str_parser = as<std::string>[quoted_string];
//const auto variant_parser = int_rule | x3::double_ | variant_str_parser;
const auto variant_parser = int_rule | x3::double_ | quoted_string;
const auto variable_parser = (x3::lexeme[+x3::char_("a-zA-Z0-9")] >> variant_parser) % ";";

int main() {
    std::string input = R"(  name "dan";  double 4.5;   intx 9;
    nameb "mane";
)";
    auto first = input.begin();
    std::unordered_map<std::string, rpn_val> map;
    auto r = x3::phrase_parse(first, input.end(), variable_parser, x3::space, map);

    for (auto& item : map) {
        std::cout << item.first << "\t " << item.second << std::endl;
    }
    return 0;
}

With a breakpoint in the specialized move_to, I'm clearly being handed a std::string& in the test app. In my big app, I get a compiler error the likes of:

1>...\x3\support\traits\move_to.hpp(62,1): error C2679: binary '=': no operator found which takes a right-hand operand of type 'char' (or there is no acceptable conversion) (compiling source file rpt_parse.cpp)
1>...\x3\support\traits\move_to.hpp(62,1): message : while trying to match the argument list '(Dest, char)'
1>        with
1>        [
1>            Dest=value_type
1>        ] (compiling source file rpt_parse.cpp)
1>...\x3\support\traits\move_to.hpp(81): message : see reference to function template instantiation 'void boost::spirit::x3::traits::detail::move_to_plain<Source,Dest>(Source &,Dest &,boost::mpl::false_)' being compiled
1>        with
1>        [
1>            Source=char,
1>            Dest=value_type
1>        ] (compiling source file rpt_parse.cpp)
1>...\x3\support\traits\move_to.hpp(196): message : see reference to function template instantiation 'void boost::spirit::x3::traits::detail::move_to<char,Dest>(Source &,Dest &,boost::spirit::x3::traits::plain_attribute)' being compiled
1>        with
1>        [
1>            Dest=value_type,
1>            Source=char
1>        ] (compiling source file rpt_parse.cpp)
1>...\x3\char\char_parser.hpp(31): message : see reference to function template instantiation 'void boost::spirit::x3::traits::move_to<char&,Attribute>(Source,Dest &)' being compiled
1>        with
1>        [
1>            Attribute=value_type,
1>            Source=char &,
1>            Dest=value_type
1>        ] (compiling source file rpt_parse.cpp)
1>...\x3\core\detail\parse_into_container.hpp(97): message : see reference to function template instantiation 'bool boost::spirit::x3::char_parser<boost::spirit::x3::char_set<Encoding,boost::spirit::char_encoding::standard::char_type>>::parse<Iterator,Context,value_type>(Iterator &,const Iterator &,const Context &,boost::spirit::x3::unused_type,Attribute &) const' being compiled
1>        with
1>        [
1>            Encoding=boost::spirit::char_encoding::standard,
1>            Iterator=std::_String_iterator<std::_String_val<std::_Simple_types<char>>>,
1>            Context=boost::spirit::x3::context<boost::spirit::x3::skipper_tag,unused_skipper_type,boost::spirit::x3::context<boost::spirit::x3::skipper_tag,const boost::spirit::x3::standard::space_type,boost::spirit::x3::unused_type>>,
1>            Attribute=value_type
1>        ] (compiling source file rpt_parse.cpp)

I've even gone so far as to paste this code into a function, with the parsers, well outside of my parser namespaces and I still get the error. Something subtle, but I can't figure...

lakeweb
  • 1,859
  • 2
  • 16
  • 21

1 Answers1

1

Well. So I kept building up from just a single variant parser.

auto r = phrase_parse(first, input.end(), variant_parser, x3::space, var);

This told me I did not have a problem with parsing into the variant! I got to the point where if the attribute was:

std::tuple<std::string, rpn::rpn_val> pair;

it would work but:

std::pair<std::string, rpn::rpn_val> pair;

would not.

I had never #include <boost/fusion/adapted/std_pair.hpp> in the big app, never needed it. But std::unordered_map is a std::pair target. I've known about std_pair.hpp and spirit for some time but just didn't realize, in my case, it was the issue. Yes, I have spent some time on this. But pouring over the error output has helped in understanding it. The biggest lesson is not to assume an error is in one place when it may very well be somewhere else.

lakeweb
  • 1,859
  • 2
  • 16
  • 21