问题
I'm trying to parse a key-value string into a structure. Some key-values may be absent or may be in different order, so I wanted to use boost::fusion
to adapt the structure and then parse into it with at_key<>
directive.
#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/sequence.hpp>
using namespace std;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phx = boost::phoenix;
using boost::fusion::at_key;
typedef string::const_iterator iter_type;
struct Couple {
int a;
int b;
Couple() : a(0), b(0) {}
};
namespace keys {
struct first;
struct second;
}
BOOST_FUSION_ADAPT_ASSOC_STRUCT(
Couple,
(int, a, keys::first)
(int, b, keys::second)
)
struct G: qi::grammar< iter_type, Couple(), ascii::space_type >
{
G() : G::base_type( start_rule ) {
using qi::_val;
using qi::_1;
using qi::_2;
start_rule =
( "first" >> qi::int_
[ at_key<keys::first>(_val) = _1 ]
)
^
( "second" >> qi::int_
[ at_key<keys::second>(_val) = _1 ]
);
}
qi::rule< iter_type, Couple(), ascii::space_type > start_rule;
};
int main() {
Couple couple;
string example = "second 2 first 1";
iter_type begin( example.begin() );
iter_type end( example.end() );
// test at_key -- compiles with no error
at_key<keys::second>(couple) = 5;
bool ok = qi::phrase_parse( begin, end, G(), ascii::space, couple );
if ( ok )
cout << couple.a << " " << couple.b << endl;
else
cout << "Parse failed" << endl;
return 0;
}
The problem is that the code does not compile (Boost 1.50.0, g++ 4.5.0, MinGW), apparently fails at the at_key<>
rules:
In file included from D:\projects\workspace\boost/boost/fusion/support/category_of.hpp:10:0,
from D:\projects\workspace\boost/boost/fusion/include/category_of.hpp:10,
from D:\projects\workspace\boost/boost/proto/fusion.hpp:20,
from D:\projects\workspace\boost/boost/proto/core.hpp:21,
from D:\projects\workspace\boost/boost/proto/proto.hpp:12,
from D:\projects\workspace\boost/boost/spirit/home/support/meta_compiler.hpp:19,
from D:\projects\workspace\boost/boost/spirit/home/qi/meta_compiler.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi.hpp:14,
from D:\projects\workspace\boost/boost/spirit/include/qi.hpp:16,
from ..\src\spirit02_test.cpp:11:
D:\projects\workspace\boost/boost/fusion/support/detail/category_of.hpp: In instantiation of 'boost::fusion::detail::fusion_category_of<const boost::phoenix::actor<boost::spirit::attribute<0> > >':
D:\projects\workspace\boost/boost/fusion/support/category_of.hpp:44:58: instantiated from 'boost::fusion::extension::category_of_impl<boost::fusion::non_fusion_tag>::apply<const boost::phoenix::actor<boost::spirit::attribute<0> > >'
D:\projects\workspace\boost/boost/fusion/support/category_of.hpp:66:9: instantiated from 'boost::fusion::traits::category_of<const boost::phoenix::actor<boost::spirit::attribute<0> > >'
D:\projects\workspace\boost/boost/fusion/support/category_of.hpp:73:9: instantiated from 'boost::fusion::traits::is_associative<const boost::phoenix::actor<boost::spirit::attribute<0> > >'
D:\projects\workspace\boost/boost/mpl/if.hpp:67:11: instantiated from 'boost::mpl::if_<boost::fusion::traits::is_associative<const boost::phoenix::actor<boost::spirit::attribute<0> > >, boost::fusion::result_of::key_of<mpl_::arg<1> >, boost::fusion::result_of::value_of<mpl_::arg<1> > >'
D:\projects\workspace\boost/boost/fusion/algorithm/query/find.hpp:45:9: instantiated from 'boost::fusion::result_of::find<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:38:17: instantiated from 'boost::fusion::extension::at_key_impl<boost::fusion::non_fusion_tag>::apply<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:71:9: instantiated from 'boost::fusion::result_of::at_key<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
..\src\spirit02_test.cpp:54:35: instantiated from here
D:\projects\workspace\boost/boost/fusion/support/detail/category_of.hpp:15:38: error: no type named 'category' in 'const struct boost::phoenix::actor<boost::spirit::attribute<0> >'
In file included from D:\projects\workspace\boost/boost/proto/args.hpp:21:0,
from D:\projects\workspace\boost/boost/proto/core.hpp:14,
from D:\projects\workspace\boost/boost/proto/proto.hpp:12,
from D:\projects\workspace\boost/boost/spirit/home/support/meta_compiler.hpp:19,
from D:\projects\workspace\boost/boost/spirit/home/qi/meta_compiler.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi.hpp:14,
from D:\projects\workspace\boost/boost/spirit/include/qi.hpp:16,
from ..\src\spirit02_test.cpp:11:
D:\projects\workspace\boost/boost/mpl/if.hpp: In instantiation of 'boost::mpl::if_<boost::fusion::traits::is_associative<const boost::phoenix::actor<boost::spirit::attribute<0> > >, boost::fusion::result_of::key_of<mpl_::arg<1> >, boost::fusion::result_of::value_of<mpl_::arg<1> > >':
D:\projects\workspace\boost/boost/fusion/algorithm/query/find.hpp:45:9: instantiated from 'boost::fusion::result_of::find<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:38:17: instantiated from 'boost::fusion::extension::at_key_impl<boost::fusion::non_fusion_tag>::apply<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:71:9: instantiated from 'boost::fusion::result_of::at_key<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
..\src\spirit02_test.cpp:54:35: instantiated from here
D:\projects\workspace\boost/boost/mpl/if.hpp:67:11: error: 'value' is not a member of 'boost::fusion::traits::is_associative<const boost::phoenix::actor<boost::spirit::attribute<0> > >'
D:\projects\workspace\boost/boost/mpl/if.hpp:70:41: error: 'value' is not a member of 'boost::fusion::traits::is_associative<const boost::phoenix::actor<boost::spirit::attribute<0> > >'
In file included from D:\projects\workspace\boost/boost/fusion/sequence/intrinsic.hpp:20:0,
from D:\projects\workspace\boost/boost/fusion/include/intrinsic.hpp:10,
from D:\projects\workspace\boost/boost/proto/fusion.hpp:22,
from D:\projects\workspace\boost/boost/proto/core.hpp:21,
from D:\projects\workspace\boost/boost/proto/proto.hpp:12,
from D:\projects\workspace\boost/boost/spirit/home/support/meta_compiler.hpp:19,
from D:\projects\workspace\boost/boost/spirit/home/qi/meta_compiler.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi.hpp:14,
from D:\projects\workspace\boost/boost/spirit/include/qi.hpp:16,
from ..\src\spirit02_test.cpp:11:
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp: In instantiation of 'boost::fusion::extension::at_key_impl<boost::fusion::non_fusion_tag>::apply<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>':
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:71:9: instantiated from 'boost::fusion::result_of::at_key<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
..\src\spirit02_test.cpp:54:35: instantiated from here
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:38:17: error: no type named 'type' in 'struct boost::fusion::result_of::find<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::first>'
..\src\spirit02_test.cpp: In constructor 'G::G()':
..\src\spirit02_test.cpp:54:35: error: no matching function for call to 'at_key(const boost::spirit::_val_type&)'
In file included from D:\projects\workspace\boost/boost/fusion/sequence/intrinsic.hpp:20:0,
from D:\projects\workspace\boost/boost/fusion/include/intrinsic.hpp:10,
from D:\projects\workspace\boost/boost/proto/fusion.hpp:22,
from D:\projects\workspace\boost/boost/proto/core.hpp:21,
from D:\projects\workspace\boost/boost/proto/proto.hpp:12,
from D:\projects\workspace\boost/boost/spirit/home/support/meta_compiler.hpp:19,
from D:\projects\workspace\boost/boost/spirit/home/qi/meta_compiler.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi/action.hpp:14,
from D:\projects\workspace\boost/boost/spirit/home/qi.hpp:14,
from D:\projects\workspace\boost/boost/spirit/include/qi.hpp:16,
from ..\src\spirit02_test.cpp:11:
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp: At global scope:
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp: In instantiation of 'boost::fusion::extension::at_key_impl<boost::fusion::non_fusion_tag>::apply<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::second>':
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:71:9: instantiated from 'boost::fusion::result_of::at_key<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::second>'
..\src\spirit02_test.cpp:58:36: instantiated from here
D:\projects\workspace\boost/boost/fusion/sequence/intrinsic/at_key.hpp:38:17: error: no type named 'type' in 'struct boost::fusion::result_of::find<const boost::phoenix::actor<boost::spirit::attribute<0> >, keys::second>'
..\src\spirit02_test.cpp: In constructor 'G::G()':
..\src\spirit02_test.cpp:58:36: error: no matching function for call to 'at_key(const boost::spirit::_val_type&)'
If I use a simpler rule (without associativity), everything compiles and works fine, but this solution is somewhat fragile:
// A non-associative solution
//start_rule %= ( ("first" >> qi::int_) ^ ("second" >> qi::int_) );
Why cannot I use at_key
in the semantic action? Is there a better way to do an "associative" parse into a non-associative structure?
回答1:
You need a lazy version of at_key. The patch for this sadly never made it into Spirit (yet):
/*=============================================================================
Copyright (c) 2005-2008 Hartmut Kaiser
Copyright (c) 2005-2007 Joel de Guzman
Copyright (c) 2011 Michael Caisse
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#ifndef PHOENIX_SEQUENCE_AT_KEY_HPP
#define PHOENIX_SEQUENCE_AT_KEY_HPP
#include <boost/fusion/include/at_key.hpp>
#include <boost/spirit/home/phoenix/core/actor.hpp>
#include <boost/spirit/home/phoenix/core/compose.hpp>
#include <boost/type_traits/remove_reference.hpp>
namespace boost { namespace phoenix
{
template <typename Key>
struct at_key_eval
{
template <typename Env, typename Tuple>
struct result
{
typedef typename Tuple::template result<Env>::type tuple;
typedef typename
fusion::result_of::at_key<
typename remove_reference<tuple>::type, Key
>::type
type;
};
template <typename RT, typename Env, typename Tuple>
static RT
eval(Env const& env, Tuple const& t)
{
return fusion::at_key<Key>(t.eval(env));
}
};
template <typename Key, typename Tuple>
inline actor<typename as_composite<at_key_eval<Key>, Tuple>::type>
at_key(Tuple const& tup)
{
return compose<at_key_eval<Key> >(tup);
}
}}
#endif
If you add it, you can use boost::phoenix::at_key
:
using boost::phoenix::at_key;
Things will compile and work as expected. The linked patch also modifies boost/spirit/home/phoenix/fusion.hpp
to include this new header, of course.
来源:https://stackoverflow.com/questions/11717509/parsing-a-structure-in-an-associative-manner-with-boost-spirit-and-fusion