I'm attempting to learn the boost spirit library. Working from the qi/karma XML example (http://www.boost.org/doc/libs/1_50_0/libs/spirit/repository/example/karma/mini_xml_karma_sr.cpp), I've attempted to change the children container in mini_xml to something other than std::vector (std::list in this example, look for the mini_xml_children typedef below). Unfortunately, doing so doesn't seem to compile.
The compile error seems to indicate that phoenix is attempting to assign std::list to std::vector, which I had a little trouble explaining because there aren't any references to std::vector in my code any longer. I did a little digging - looks like boost::spirit::karma::action::generate decides to use std::vector internally rather than detecting the container mini_xml uses.
If my assumption there is correct, I think I need a way of communicating more explicitly what the 'attribute' should be. Is there an easy way to do this? Ideally, I'd like this code to be container-agnostic.
The code:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
//[mini_xml_karma_sr_includes
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/repository/include/karma_subrule.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
//]
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <iostream>
#include <fstream>
#include <string>
//[mini_xml_karma_sr_using
using namespace boost::spirit;
using namespace boost::spirit::ascii;
namespace repo = boost::spirit::repository;
//]
namespace fusion = boost::fusion;
namespace phoenix = boost::phoenix;
using phoenix::at_c;
using phoenix::push_back;
///////////////////////////////////////////////////////////////////////////////
// Our mini XML tree representation
///////////////////////////////////////////////////////////////////////////////
struct mini_xml;
typedef
boost::variant<
boost::recursive_wrapper<mini_xml>
, std::string
>
mini_xml_node;
//typedef std::vector<mini_xml_node> mini_xml_children; // original
typedef std::list<mini_xml_node> mini_xml_children;
struct mini_xml
{
std::string name; // tag name
mini_xml_children children; // children
};
// We need to tell fusion about our mini_xml struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_STRUCT(
mini_xml,
(std::string, name)
(mini_xml_children, children)
)
///////////////////////////////////////////////////////////////////////////////
// Our mini XML grammar definition
///////////////////////////////////////////////////////////////////////////////
template <typename Iterator>
struct mini_xml_parser :
qi::grammar<Iterator, mini_xml(), space_type>
{
mini_xml_parser() : mini_xml_parser::base_type(xml)
{
text = lexeme[+(char_ - '<') [_val += _1]];
node = (xml | text) [_val = _1];
start_tag =
'<'
>> !lit('/')
>> lexeme[+(char_ - '>') [_val += _1]]
>> '>'
;
end_tag =
"</"
>> lit(_r1)
>> '>'
;
xml =
start_tag [at_c<0>(_val) = _1]
>> *node [push_back(at_c<1>(_val), _1)]
>> end_tag(at_c<0>(_val))
;
}
qi::rule<Iterator, mini_xml(), space_type> xml;
qi::rule<Iterator, mini_xml_node(), space_type> node;
qi::rule<Iterator, std::string(), space_type> text;
qi::rule<Iterator, std::string(), space_type> start_tag;
qi::rule<Iterator, void(std::string), space_type> end_tag;
};
///////////////////////////////////////////////////////////////////////////////
// A couple of phoenix functions helping to access the elements of the
// generated AST
///////////////////////////////////////////////////////////////////////////////
template <typename T>
struct get_element
{
template <typename T1>
struct result { typedef T const& type; };
T const& operator()(mini_xml_node const& node) const
{
return boost::get<T>(node);
}
};
phoenix::function<get_element<std::string> > _string;
phoenix::function<get_element<mini_xml> > _xml;
///////////////////////////////////////////////////////////////////////////////
// The output grammar defining the format of the generated data
///////////////////////////////////////////////////////////////////////////////
//[mini_xml_karma_sr_grammar
template <typename OutputIterator>
struct mini_xml_generator
: karma::grammar<OutputIterator, mini_xml()>
{
mini_xml_generator() : mini_xml_generator::base_type(xml)
{
node %= ascii::string | xml;
xml =
'<' << ascii::string[qi::_1 = phoenix::at_c<0>(qi::_val)] << '>'
<< (*node)[qi::_1 = phoenix::at_c<1>(qi::_val)]
<< "</" << ascii::string[qi::_1 = phoenix::at_c<0>(qi::_val)] << '>'
;
}
karma::rule<OutputIterator, mini_xml()> xml;
karma::rule<OutputIterator, mini_xml_node()> node;
};
//]
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
char const* filename;
if (argc > 1)
{
filename = argv[1];
}
else
{
std::cerr << "Error: No input file provided." << std::endl;
return 1;
}
std::ifstream in(filename, std::ios_base::in);
if (!in)
{
std::cerr << "Error: Could not open input file: "
<< filename << std::endl;
return 1;
}
std::string storage; // We will read the contents here.
in.unsetf(std::ios::skipws); // No white space skipping!
std::copy(
std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(storage));
typedef mini_xml_parser<std::string::const_iterator> mini_xml_parser;
mini_xml_parser xmlin; // Our grammar definition
mini_xml ast; // our tree
std::string::const_iterator iter = storage.begin();
std::string::const_iterator end = storage.end();
bool r = qi::phrase_parse(iter, end, xmlin, space, ast);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
typedef std::back_insert_iterator<std::string> outiter_type;
typedef mini_xml_generator<outiter_type> mini_xml_generator;
mini_xml_generator xmlout; // Our grammar definition
std::string generated;
outiter_type outit(generated);
bool r = karma::generate(outit, xmlout, ast);
if (r)
std::cout << generated << std::endl;
return 0;
}
else
{
std::string::const_iterator begin = storage.begin();
std::size_t dist = std::distance(begin, iter);
std::string::const_iterator some =
iter + (std::min)(storage.size()-dist, std::size_t(30));
std::string context(iter, some);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << context << "...\"\n";
std::cout << "-------------------------\n";
return 1;
}
}
Start of the error message:
1>------ Build started: Project: test_project, Configuration: Debug Win32 ------
1>Compiling...
1>test_project.cpp
1>c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/phoenix/operator/self.hpp(27) : error C2679: binary '=' : no operator found which takes a right-hand operand of type 'const std::list<_Ty>' (or there is no acceptable conversion)
1> with
1> [
1> _Ty=mini_xml_node
1> ]
1> c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\vector(562): could be 'std::vector<_Ty> &std::vector<_Ty>::operator =(const std::vector<_Ty> &)'
1> with
1> [
1> _Ty=boost::variant<boost::recursive_wrapper<mini_xml>,std::string>
1> ]
1> while trying to match the argument list '(std::vector<_Ty>, const std::list<_Ty>)'
1> with
1> [
1> _Ty=boost::variant<boost::recursive_wrapper<mini_xml>,std::string>
1> ]
1> and
1> [
1> _Ty=mini_xml_node
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/mpl/eval_if.hpp(41) : see reference to class template instantiation 'boost::phoenix::result_of_assign<X,Y>' being compiled
1> with
1> [
1> X=std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> ,
1> Y=const std::list<mini_xml_node>
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/phoenix/operator/self.hpp(69) : see reference to class template instantiation 'boost::mpl::eval_if<C,F1,F2>' being compiled
1> with
1> [
1> C=boost::mpl::or_<boost::phoenix::is_actor<std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> >,boost::phoenix::is_actor<const std::list<mini_xml_node> >>,
1> F1=boost::phoenix::re_curry<boost::phoenix::assign_eval,std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> ,const std::list<mini_xml_node> >,
1> F2=boost::phoenix::result_of_assign<std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> ,const std::list<mini_xml_node> >
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/phoenix/core/detail/composite_eval.hpp(88) : see reference to class template instantiation 'boost::phoenix::assign_eval::result<Env,A0,A1>' being compiled
1> with
1> [
1> Env=boost::phoenix::basic_environment<boost::fusion::vector1<std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> &>,boost::spirit::context<boost::fusion::cons<const mini_xml &,boost::fusion::nil>,boost::fusion::vector0<>>,bool>,
1> A0=boost::spirit::argument<0>,
1> A1=boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/phoenix/core/composite.hpp(60) : see reference to class template instantiation 'boost::phoenix::detail::composite_eval<2>::result<Composite,Env>' being compiled
1> with
1> [
1> Composite=boost::phoenix::composite<boost::phoenix::assign_eval,boost::fusion::vector<boost::spirit::argument<0>,boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,
1> Env=boost::phoenix::basic_environment<boost::fusion::vector1<std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> &>,boost::spirit::context<boost::fusion::cons<const mini_xml &,boost::fusion::nil>,boost::fusion::vector0<>>,bool>
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/phoenix/core/actor.hpp(56) : see reference to class template instantiation 'boost::phoenix::composite<EvalPolicy,EvalTuple>::result<Env>' being compiled
1> with
1> [
1> EvalPolicy=boost::phoenix::assign_eval,
1> EvalTuple=boost::fusion::vector<boost::spirit::argument<0>,boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>,
1> Env=boost::phoenix::basic_environment<boost::fusion::vector1<std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> &>,boost::spirit::context<boost::fusion::cons<const mini_xml &,boost::fusion::nil>,boost::fusion::vector0<>>,bool>
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/phoenix/core/detail/actor.hpp(48) : see reference to class template instantiation 'boost::phoenix::eval_result<Eval,Env>' being compiled
1> with
1> [
1> Eval=boost::phoenix::composite<boost::phoenix::assign_eval,boost::fusion::vector<boost::spirit::argument<0>,boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,
1> Env=boost::phoenix::basic_environment<boost::fusion::vector1<std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> &>,boost::spirit::context<boost::fusion::cons<const mini_xml &,boost::fusion::nil>,boost::fusion::vector0<>>,bool>
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/support/action_dispatch.hpp(178) : see reference to class template instantiation 'boost::phoenix::actor<Eval>::result<Sig>' being compiled
1> with
1> [
1> Eval=boost::phoenix::composite<boost::phoenix::assign_eval,boost::fusion::vector<boost::spirit::argument<0>,boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,
1> Sig=boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval,boost::fusion::vector<boost::spirit::argument<0>,boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>> (boost::fusion::vector1<std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>> &> &,boost::spirit::context<boost::fusion::cons<const mini_xml &,boost::fusion::nil>,boost::fusion::vector0<>> &,bool &)
1> ]
1> c:\rtgcode\thirdparty\thirdparty\boost/spirit/home/karma/action/action.hpp(69) : see reference to function template instantiation 'bool boost::spirit::traits::action_dispatch<Component>::operator ()<boost::phoenix::composite<EvalPolicy,EvalTuple>,std::vector<_Ty>,Context>(const boost::phoenix::actor<Eval> &,Attribute &,Context &)' being compiled
1> with
1> [
1> Component=boost::spirit::karma::kleene<boost::spirit::karma::reference<const boost::spirit::karma::rule<outiter_type,mini_xml_node (void)>>>,
1> EvalPolicy=boost::phoenix::assign_eval,
1> EvalTuple=boost::fusion::vector<boost::spirit::argument<0>,boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>,
1> _Ty=boost::variant<boost::recursive_wrapper<mini_xml>,std::string>,
1> Context=boost::spirit::context<boost::fusion::cons<const mini_xml &,boost::fusion::nil>,boost::fusion::vector0<>>,
1> Eval=boost::phoenix::composite<boost::phoenix::assign_eval,boost::fusion::vector<boost::spirit::argument<0>,boost::phoenix::composite<boost::phoenix::at_eval<1>,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>,
1> Attribute=std::vector<boost::variant<boost::recursive_wrapper<mini_xml>,std::string>>
1> ]
*snip*
Compiled with boost v1.50.0 in Visual Studio 2008.
Side note: I couldn't get the original example to compile without first changing karma::subrule to karma::rule. Is there an issue with subrules in 1.50.0? Is there a better workaround for the issue than what I've done?