Add to a spirit qi symbol table in a semantic action - c++

Going by the opening paragraph of the boost::spirit::qi::symbols documentation, I assumed that it wouldn't be too hard to add symbols to a qi::symbols from a semantic action. Unfortunately it appears to be not as straightforward as I would have assumed.
The following bit of test code exhibits the problem:
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
namespace qi = boost::spirit::qi;
typedef qi::symbols<char, unsigned int> constants_dictionary;
template <typename Iter> struct parser : public qi::grammar<Iter, qi::space_type> {
parser(constants_dictionary &dict) : parser::base_type(start) {
start = qi::lit("#") >> ((+qi::char_) >> qi::uint_)[dict.add(qi::_1, qi::_2)];
}
qi::rule<Iter> start;
};
int main() {
constants_dictionary dict;
parser<std::string::const_iterator> prsr(dict);
std::string test = "#foo 3";
parse(test.begin(), test.end(), prsr, qi::space);
}
Gives type errors related to qi::_2 from VS2010:
C:\Users\k\Coding\dashCompiler\spirit_test.cpp(12) : error C2664: 'const boost::
spirit::qi::symbols<Char,T>::adder &boost::spirit::qi::symbols<Char,T>::adder::o
perator ()<boost::spirit::_1_type>(const Str &,const T &) const' : cannot conver
t parameter 2 from 'const boost::spirit::_2_type' to 'const unsigned int &'
with
[
Char=char,
T=unsigned int,
Str=boost::spirit::_1_type
]
Reason: cannot convert from 'const boost::spirit::_2_type' to 'const uns
igned int'
No user-defined-conversion operator available that can perform this conv
ersion, or the operator cannot be called
C:\Users\k\Coding\dashCompiler\spirit_test.cpp(10) : while compiling cla
ss template member function 'parser<Iter>::parser(constants_dictionary &)'
with
[
Iter=std::_String_const_iterator<char,std::char_traits<char>,std::al
locator<char>>
]
C:\Users\k\Coding\dashCompiler\spirit_test.cpp(21) : see reference to cl
ass template instantiation 'parser<Iter>' being compiled
with
[
Iter=std::_String_const_iterator<char,std::char_traits<char>,std::al
locator<char>>
]
(Apologies for the nasty VS2010 error-style)
What syntax am I supposed to be using to add (and later on, remove) symbols from this table?

This question has been answered before. However, there is quite a range of problems with your posted code, so I'll fix them up one by one to spare you unnecessary staring at pages of error messages.
The working code (plus verification of output) is here on liveworkspace.org.
Notes:
the semantic action must be a Phoenix actor, i.e. you need
boost::bind, phoenix::bind, std::bind
phoenix::lambda<> or phoenix::function<>
a function pointer or polymorphic calleable object (as per the documentation)
I'd recommend phoenix::bind (in this particular case), which I show below
There was a mismatch between the parser's skipper and the start rule
qi::char_ eats all characters. Combined with the skipper, this resulted
in parse failure, because (obviously) the digits in the value were also being
eaten by +qi::char_. I show you one of many solutions, based on qi::lexeme[+qi::graph]
use qi::lexeme to 'bypass' the skipper (i.e. to prevent +qi::graph to cut
across whitespace because the skipper, well, skipped it)
qi::parse doesn't take a skipper; use qi::phrase_parse for that (the
reason it appeared to work is that any trailing 'variadic' arguments are
bound to the exposed attributes of the parser, which in this case are
unspecified, and therefore qi::unused_type).
if you want to pass test.begin() and test.end() directly to
qi::phrase_parse, you need to make it clear that you want const iterators. The
more typical solution would be to introduce explicitely typed variables
(first and last, e.g.)
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
typedef qi::symbols<char, unsigned int> constants_dictionary;
template <typename Iter> struct parser : qi::grammar<Iter, qi::space_type>
{
parser(constants_dictionary &dict) : parser::base_type(start)
{
start = qi::lit("#") >> (qi::lexeme [+qi::graph] >> qi::uint_)
[ phx::bind(dict.add, qi::_1, qi::_2) ]
;
}
qi::rule<Iter, qi::space_type> start;
};
int main() {
constants_dictionary dict;
parser<std::string::const_iterator> prsr(dict);
const std::string test = "#foo 3";
if (qi::phrase_parse(test.begin(), test.end(), prsr, qi::space))
{
std::cout << "check: " << dict.at("foo") << "\n";
}
}

Related

How does including gtest.h break template argument deduction for a std algorithm?

I upgraded to the latest release of Google Test, and several of my tests no longer compiled. I've reduced it to this:
#include <gtest/gtest.h>
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
int main () {
const std::string foo = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const auto uppers = std::count_if(foo.begin(), foo.end(), std::isupper);
std::cout << "There are " << uppers << " capitals." << std::endl;
return 0;
}
The Visual Studio 2019 (16.10.4) compiler with /std:c++latest complains:
1>Source.cpp(10,30): error C2672: 'std::count_if': no matching overloaded function found
1>Source.cpp(10,75): error C2780: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty2>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>::difference_type std::count_if(_ExPo &&,const _FwdIt,const _FwdIt,_Pr) noexcept': expects 4 arguments - 3 provided
1>algorithm(570): message : see declaration of 'std::count_if'
1>Source.cpp(10,30): error C2783: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>::difference_type std::count_if(_InIt,_InIt,_Pr)': could not deduce template argument for '_Pr'
1>algorithm(553): message : see declaration of 'std::count_if'
If I comment out the inclusion of gtest.h, the code builds and executes correctly.
What could gtest.h be doing that messes up template argument deduction for a call that depends only on std-defined types and functions?
[Note, my question is not how to workaround the problem, but to understand the specific underlying cause. I have a workaround: Replace the std::isupper with a lambda.]
It appears that <gtest/gtest.h> is now including <locale>, which introduces
template< class charT > bool isupper( charT ch, const locale& loc )
into the scope. That means that std::isupper now has two possible functions it could point to and without you specifying which one to use, you get an ambiguity which causes template argument deduction to fail.
If you do go the lambda route to fix this, make sure you cast the input to std::isupper to an unsigned char like
const auto uppers = std::count_if(foo.begin(),
foo.end(),
[]()(auto ch){ return std::isupper(static_cast<unsigned char>(ch));} )

Wrong attribute of sequence parser

I tried the following example from the spirit x3 docs
#include <string>
#include <iostream>
#include "boost/spirit/home/x3.hpp"
namespace x3 = boost::spirit::x3;
int main()
{
std::pair<double, double> p;
std::string input("1.0 2.0");
std::string::iterator input_pos = input.begin();
x3::phrase_parse(input_pos, input.end(),
x3::double_ >> x3::double_,
x3::space, p);
}
The error i get is
[...]/boost/boost-1.61.0/include/boost/spirit/home/x3/support/traits/move_to.hpp:62:18: error: no match for ‘operator=’ (operand types are ‘std::pair<double, double>’ and ‘std::remove_reference<double&>::type {aka double}’)
dest = std::move(src);
If i change p to be of type double it compiles and matches 2.0. This is obviously not my intention. I tried this with multiple version of gcc (4.9, 6.2, trunk) and boost version 1.61.0. I feel like this should be a configuration problem unless someone spots an error in the code.
Did someone experienced something similar and know where the problem lies?

error_invalid_expression compile error in a simple boost.spirit example

I'm trying to get started with boost.spirit on Visual Studio 2013 and I'm getting error_invalid_expression compilation errors. Here is the code:
#include <iostream>
#include <string>
#include <boost/spirit/home/qi.hpp>
void main()
{
using namespace boost::spirit::qi;
std::string in("123 234");
std::cout << parse(in.begin(), in.end(), int_) << '\n'; // works fine
std::cout << parse(in.begin(), in.end(), int_ << ' ' << int_) << '\n'; // error here
}
Here is the template barf:
C:\boost\boost/spirit/home/qi/detail/parse.hpp(32): error C2338: error_invalid_expression
C:\boost\boost/spirit/home/qi/parse.hpp(36) : see reference to class template instantiation 'boost::spirit::qi::detail::parse_impl<Expr,void>' being compiled
with
[
Expr=boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::spirit::terminal<boost::spirit::tag::int_> &,boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<const char &>,0>>,2> &,const boost::spirit::terminal<boost::spirit::tag::int_> &>,2>
]
C:\boost\boost/spirit/home/qi/parse.hpp(47) : see reference to function template instantiation 'bool boost::spirit::qi::parse<std::_String_iterator<std::_String_val<std::_Simple_types<char>>>,boost::proto::exprns_::expr<Tag,Args,2>>(Iterator &,Iterator,const Expr &)' being compiled
with
[
Tag=boost::proto::tagns_::tag::shift_left
, Args=boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::spirit::terminal<boost::spirit::tag::int_> &,boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<const char &>,0>>,2> &,const boost::spirit::terminal<boost::spirit::tag::int_> &>
, Iterator=std::_String_iterator<std::_String_val<std::_Simple_types<char>>>
, Expr=boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::spirit::terminal<boost::spirit::tag::int_> &,boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<const char &>,0>>,2> &,const boost::spirit::terminal<boost::spirit::tag::int_> &>,2>
]
Spirit.cpp(12) : see reference to function template instantiation 'bool boost::spirit::qi::parse<std::_String_iterator<std::_String_val<std::_Simple_types<char>>>,boost::proto::exprns_::expr<Tag,Args,2>>(const Iterator &,Iterator,const Expr &)' being compiled
with
[
Tag=boost::proto::tagns_::tag::shift_left
, Args=boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::spirit::terminal<boost::spirit::tag::int_> &,boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<const char &>,0>>,2> &,const boost::spirit::terminal<boost::spirit::tag::int_> &>
, Iterator=std::_String_iterator<std::_String_val<std::_Simple_types<char>>>
, Expr=boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::spirit::terminal<boost::spirit::tag::int_> &,boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<const char &>,0>>,2> &,const boost::spirit::terminal<boost::spirit::tag::int_> &>,2>
]
C:\boost\boost/spirit/home/qi/parse.hpp(36): error C2039: 'call' : is not a member of 'boost::spirit::qi::detail::parse_impl<Expr,void>'
with
[
Expr=boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_left,boost::proto::argsns_::list2<const boost::spirit::terminal<boost::spirit::tag::int_> &,boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<const char &>,0>>,2> &,const boost::spirit::terminal<boost::spirit::tag::int_> &>,2>
]
C:\boost\boost/spirit/home/qi/parse.hpp(36): error C3861: 'call': identifier not found
I tried adding various include files suggested in above lines, but it didn't help. Any ideas what is wrong with this code?
I used a wrong shift operator when copying an example: << instead of >>. It should be:
std::cout << parse(in.begin(), in.end(), int_ >> ' ' >> int_) << '\n';
Template error messages were helpful as usual :-(
#Paul You have all my permission to complain :)
I'm just pointing out that it could be far worse (and it frequently is) and that you could in fact thank the library writers to make this problem traceable at all.
Right now making the jump from "tag::shift_left" to operator<< is the only tricky part.
For the rest I'd like to point at the template barf evidence:
the very first line reports
C:\boost\boost/spirit/home/qi/detail/parse.hpp(32): error C2338: error_invalid_expression
Let's have a look:
error_invalid_expression tries to tell you you're trying to compile an expression that you shouldn't try to compile.
the error originates from qi/detail/parse.hpp(32). This immediately tells you it's in a Boost Spirit Qi expression (and not, e.g. in a Karma expression).
Double-clicking on that first line takes you to this location, which reads:
{
// Report invalid expression error as early as possible.
// If you got an error_invalid_expression error message here,
// then the expression (expr) is not a valid spirit qi expression.
// Did you intend to use the auto_ facilities while forgetting to
// #include <boost/spirit/include/qi_auto.hpp>?
BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr); // <-- LINE 32
};
I guess what I mean to say is: don't complain, instead just absorb the new ways in which you can "read" the messages. Of course, you're entitled to complain, much like you can complain when at first your nails end up crooked when starting to use a hammer for carpenting.
I just feel compelled to warn that if you can't stomach the message you got in this case, then probably Boost Spirit (or any other Boost Proto, Boost Mpl and perhaps Boost Fusion heavy library) is likely not your thing.
I've had very pleasurable experiences using Coco/R C++ for parser generating. There are others (flex/bison, ANTLR come to mind). All of the above are well suited for larger grammars and have additional benefits, e.g.
portability of grammar specifications across different development languages
tooling that helps in testing/transforming/diagnosing grammars
vastly reduced compile-times.
I'm saying this as one of the resident boost-spirit buffs (citation).
Pick your tools wisely. And don't cross out personal preference as a factor.

ensure that an iterator dereferences to a certain type

I have to implement a function that takes an iterator. The iterator must dereference to a certain type, say int:
template<typename iter>
void f(iter i) {
// do something here ...
int t = *i;
// do something here ...
}
The problem with this code is that if a user calls the function like this
vector<string> v;
v.push_back("aaa");
f(v.begin());
he will see an error pointing to some place in my code, not in his code (which will be confusing to him). I want the error to be in user's code to ease debugging.
GMan already pointed to a method to solve this via compile time assertions. There is another way to do this, which I prefer (it's my favorite C++ technique). You can put constraints on function arguments in a way that the function is ignored for overload resolution if the constraints don't fit. This is quite terrific, because you can fine tune your function overloads to arbitrary conditions. Here's how:
#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
#include <vector>
template<typename Iter> typename
boost::enable_if<
boost::is_same<typename Iter::value_type,int>,
void>::type
foo(Iter it) { }
int main() {
std::vector<int> v; // this is OK
foo(v.begin());
std::vector<double> v2; // this is an error
foo(v2.begin()); }
If you compile this, you will get
b.cc: In function 'int main()':
b.cc:19:16: error: no matching function for call to 'foo(std::vector<double>::iterator)'
This is because the compiler would consider foo() only, if it's argument has a value_type type inside, which is 'int' (This is what the enable_if part means). The second call of foo() can't satisfy this constraint.
enable_if is mentioned a couple of times in SO, just search for it: https://stackoverflow.com/search?q=enable_if
You could do something like this:
#include <boost/type_traits/is_convertible.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/mpl/assert.hpp>
template <typename Iter>
void foo(Iter pIter)
{
BOOST_MPL_ASSERT_MSG(
(boost::is_convertible<BOOST_TYPEOF(*pIter), int>::value),
DEREFERENCED_ITERATOR_MUST_BE_CONVERTIBLE_TO_INT,
(int));
// ...
}
#include <vector>
#include <string>
int main(void)
{
std::vector<std::string> v(5);
foo(v.begin());
}
Which makes the message quite visible:
error C2664: 'boost::mpl::assertion_failed' : cannot convert parameter 1 from 'boost::mpl::failed ************(__thiscall foo::DEREFERENCED_ITERATOR_MUST_BE_CONVERTIBLE_TO_INT::* ***********)(int)' to 'boost::mpl::assert::type'
But like James says, most compilers give plenty of information to find out what happened anyway.
Given the code in question, most compilers will refer to the point of instantiation in the diagnostic message. For the following, line 16 is the line f(v.begin());.
Microsoft Visual C++ reports:
> c:\example\main.cpp(16) : see reference to function template instantiation 'void f<std::_Vector_iterator<_Myvec>>(iter)' being compiled
1> with
1> [
1> _Myvec=std::_Vector_val<std::string,std::allocator<std::string>>,
1> iter=std::_Vector_iterator<std::_Vector_val<std::string,std::allocator<std::string>>>
1> ]
g++ reports:
main.cpp:16: instantiated from here
Intel C++ Compiler and Comeau both report:
detected during instantiation of
"void f(iter) [with iter=std::string *]" at line 16
You need to set a constraint on the generic type.

C++ Map can't insert with pair

Why can't I insert as shown below?
#include <map>
struct something {
} some_object;
typedef std::map<std::string, something*> list;
typedef std::pair<std::string, something*> pair;
int main()
{
list l;
pair p("abc", &some_object); // working fine!!!
l.insert(p); // 17 errors
return 0;
}
Visual studio gives me many errors and I don't understand anything of them. The first one is:
error C2784: 'bool std::operator <(const std::_Tree<_Traits> &,const std::_Tree<_Traits> &)' : could not deduce template argument for 'const std::_Tree<_Traits> &' from 'const std::string'
I can post more but I don't want to spam here. Thanks a lot for your help.
You need to
#include <string>
I would change this line:
typedef std::pair<std::string, something*> pair;
You are relaying on an implementation detail. Are you sure this wil always be true for all future version of the library? Tightly coupling your code like that is a bad idea.
Try this:
typedef list::value_type pair;
PS. 'list' would not be my first choice for the name of a type I put in the global namespace. Either put it in your own namespace or call it 'MyList'.