Boost Spirit accept rule dynamically when a keyword is used [duplicate] - boost

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

boost::hana::is_valid fails to compile with gcc8 (and more) and --std=c++14

I use this code with std=c++14 and gcc7.3:
#include <iostream>
#include <string>
#include <type_traits>
#include <boost/hana/assert.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/type.hpp>
namespace hana = boost::hana;
template<class T>
bool foo(T elem)
{
constexpr auto has_overload_to_string = hana::is_valid([](auto t) -> decltype(to_string(t)) {});
constexpr bool hasOverloadTo_string = has_overload_to_string(elem);
return hasOverloadTo_string;
}
int main()
{
std::string elem;
std::cin >> elem;
foo(elem);
}
And it works fine : demo
If now I use gcc10.1, I got this error: demo fail
prog.cc: In instantiation of 'bool foo(T) [with T = std::__cxx11::basic_string<char>]':
prog.cc:41:13: required from here
prog.cc:27:38: error: temporary of non-literal type 'foo<std::__cxx11::basic_string<char> >::<lambda(auto:1)>' in a constant expression
27 | [[maybe_unused]] constexpr auto has_overload_to_string =
| ^~~~~~~~~~~~~~~~~~~~~~
prog.cc:28:21: note: 'foo<std::__cxx11::basic_string<char> >::<lambda(auto:1)>' is not literal because:
28 | hana::is_valid([](auto t) -> decltype(to_string(t)) {});
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: note: 'foo<std::__cxx11::basic_string<char> >::<lambda(auto:1)>' is a closure type, which is only literal in C++17 and later
My question is : Is gcc7.3 too permissive with C++14 and is_valid works when it shouldn't or gcc8 and more add a bug with C++14 ?
The error has nothing to do with hana::is_valid, but the lambda is not valid in a constant expression in C++14.
There is already a good language lawyer answer for this here:
https://stackoverflow.com/a/32697323/800347
Clang also consistently provides an error, so it's clear that previous versions of gcc were incorrect in allowing this.
To workaround this, simply remove the constexpr qualifier to your variable declaration.

Issue with basename

So I am having an issue using basename for one of my programming assignments for school
I have tried getting a simplier version of it working -- I got it working however, still confused exactly what I am supposed to do in this case
// $Id: util.cpp,v 1.1 2016-06-14 18:19:17-07 - - $
#include <libgen.h>
#include <cstring>
using namespace std;
#include "util.h"
ydc_exn::ydc_exn (const string& what): runtime_error (what) {
}
string exec::execname_; // Must be initialized from main().
int exec::status_ = EXIT_SUCCESS;
void exec::execname (const string& argv0) {
execname_ = basename (argv0.c_str());
cout << boolalpha;
cerr << boolalpha;
DEBUGF ('Y', "execname = " << execname_);
}
void exec::status (int new_status) {
new_status &= 0xFF;
if (status_ < new_status) status_ = new_status;
}
ostream& note() {
return cerr << exec::execname() << ": ";
}
ostream& error() {
exec::status (EXIT_FAILURE);
return note();
}
Just trying to get the code to compile --- my error message in c++ is:
'
util.cpp:15:16: error: no matching function for call to 'basename'
execname_ = basename (argv0.c_str());
^~~~~~~~
/usr/include/libgen.h:40:7: note: candidate function not viable: 1st argument ('const std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>
>::value_type *' (aka 'const char *')) would lose const qualifier
char *basename(char *);
^
1 error generated.
make: *** [util.o] Error 1'
basename takes char* as argument, which is not const, meaning the function is allowed to modify the value.
const string& argv0 is const, meaning the value of argv0 must not be modified.
Thus you are not allowed to call basename with argv0.c_str() as parameter, as that would violate the const qualifier.
The error message is clear: 1st argument ... would lose const qualifier.
So either change argv0 to not be const (probably not a good idea), or change basename to take a const char* parameter (probably the better idea), or change basename to work with std::string instead of char* like the rest of your code (probably the best idea).
So apparently basename is a unix function that you cannot change. (Thanks Nevin!) From the manpage:
Both dirname() and basename() may modify the contents of path, so it may be desirable to pass a copy when calling one of these functions.
In that case I recommend creating a copy of argv0. The simplest way of doing this would be to change the signature of execname to this:
void exec::execname(std::string argv0)
I think that your central problem is including libgen.h as well as cstring. If you look at the prototypes for basename in those files, they're different. If you're building this on linux, you'll see a comment in the libgen.h version which should be enlightening.
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html and
https://linux.die.net/man/3/basename are useful here too.
I suggest removing the #include <libgen.h> and trying again.

Problems after commenting out "using namespace std;"

I'm new to C++ and I read that "using namespace std;" is considered bad practice. I used the following code to test if my compiler was c++14 compliant:
#include <iostream>
#include <string>
using namespace std;
auto add([](auto a, auto b){ return a+b ;});
auto main() -> int {cout << add("We have C","++14!"s);}
No errors. Then I started to play around with the code – as you do... when you learn something new. So I commented out using namespace std; and replaced cout with std::cout. Now the code looked like this:
#include <iostream>
#include <string>
//using namespace std;
auto add([](auto a, auto b){ return a+b ;});
auto main() -> int {std::cout << add("We have C","++14!"s);}
Build Messages:
||=== Build: Release in c++14-64 (compiler: GNU GCC Compiler) ===|
C:\CBProjects\c++14-64\c++14-64-test.cpp||In function 'int main()':|
C:\CBProjects\c++14-64\c++14-64-test.cpp|5|error: unable to find string literal operator 'operator""s' with 'const char [6]', 'long long unsigned int' arguments|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
Questions:
What causes the error in the second program?
How to avoid using namespace std in this case?
clang++ gives a good error message:
error: no matching literal operator for call to 'operator""s' with arguments of types 'const char *' and 'unsigned long', and no matching literal operator template
auto main() -> int { std::cout << add("We have C", "++14!"s); }
^
You use string literals and more precisely operator""s.
By removing using namespace std; you have to specify the namespace where the operator is defined.
With an explicit call:
int main() {
std::cout << add("We have C", std::operator""s("++14!", 5));
// Note the length of the raw character array literal is required
}
or with a using declaration:
int main() {
using std::operator""s;
std::cout << add("We have C", "++14!"s);
}

Cannot convert from 'const std::initializer_list<_Elem>' to 'const std::allocator<_Ty>' where both templates are int

5> ...long path...\PredicateEquals.cpp(47): error C2664: 'std::vector<_Ty>::vector(const std::allocator<_Ty> &)' : cannot convert parameter 1 from 'const std::initializer_list<_Elem>' to 'const std::allocator<_Ty> &'
5> with
5> [
5> _Ty=int
5> ]
5> and
5> [
5> _Elem=int
5> ]
5> and
5> [
5> _Ty=int
5> ]
5> Reason: cannot convert from 'const std::initializer_list<_Elem>' to 'const std::allocator<_Ty>'
5> with
5> [
5> _Elem=int
5> ]
5> and
5> [
5> _Ty=int
5> ]
5> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
Note that both _Elem=int and _Ty=int so basically what fails is conversion from const std::initializer_list<int> to const std::vector<int>. I thought this was the very purpose of initializer lists - to convert them into arrays.
I based my code on this answer:
Header file
#include <initializer_list>
#include <vector>
class Indexes {
public:
Indexes(const std::initializer_list<int> uniqueIds);
protected:
const std::vector<int> uniqueIds_;
};
Source file:
Indexes::Indexes( const std::initializer_list<int> uniqueIds )
:
uniqueIds_(uniqueIds)
{}
The error comes from the source file, from the second constructor. How to properly use the initializer list for the constructor here? I'd prefer to keep std::vector a constant. But removing the const did not remove the problem.
I am using Microsoft Visual Studio 2010. The code compiles perfectly in GCC: http://ideone.com/HRC68a
You could try:
#include <initializer_list>
#include <vector>
class Indexes {
public:
#ifdef PRE_CXX11_COMPILER
template<typename... T>
Indexes(T... t) : uniqueIds_{t...} { }
#else
Indexes(const std::initializer_list<int> uniqueIds) : uniqueIds_{uniqueIds} { }
#endif
protected:
const std::vector<int> uniqueIds_;
};
But I have no idea if that will work with MSVC 2010 either.
If you can't change your toolchain then you need to avoid C++ 2011 features that a 2010 compiler doesn't support.
So far, only thing I have is a workaround that uses plain old C varargs feature. This is nothing like initializer lists and requires you pass -1 as last parameter. I do not recommend using this if you can upgrade your compiler. Failing to pass correct parameters results in segmentation fault. But in my case rot of repetitive unreadable code would ensue if instead filled vectors manually using std::vector::push_back.
Header file
#include <vector>
#ifdef CPP_11_COMPILER
#include <initializer_list>
#else
#include <stdarg.h>
#endif
class Indexes {
public:
// Constructor with standard vector
Indexes(const std::vector<int>& uniqueIds);
#ifdef CPP_11_COMPILER
Indexes(const std::initializer_list<int> uniqueIds);
#else
// Static method to construct the std::vector and then the class' instance
// ☠☠☠ WARNING: variadric arguments must be of type short! Last argument MUST BE -1!!!
static Indexes with_initializer_list(const int unused, ...);
#endif
protected:
const std::vector<int> uniqueIds_;
};
Source file
Indexes::Indexes( const std::vector<int>& uniqueIds )
: uniqueIds_(uniqueIds)
{}
#ifdef CPP_11_COMPILER
Indexes::Indexes( const std::initializer_list<int> uniqueIds )
: uniqueIds_(uniqueIds)
{}
#else
Indexes Indexes::with_initializer_list( const int unused, ... )
{
va_list ap;
va_start(ap, unused);
std::vector<int> items;
int entry = 0;
while((entry = va_arg(ap, short))>=0) {
items.push_back(entry);
}
va_end(ap);
return Indexes(items);
}
#endif
Usage
The solution isn't exactly reliable, but is best fit before an upgrade of compiler happens:
Indexes indexes = Indexes::with_initializer_list(0, (short)1,(short)2,(short)3,(short)-1);
// indexes' vector now contains [1, 2, 3]

How to parse escaped string using `c_escape_ch_p` from boost::spirit?

I'm trying to use c_escape_ch_p (see here) from boost::spirit to parse an escaped C++ string. But I'm getting a compiler error. Here is my code:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/home/classic/utility/escape_char.hpp>
#include <boost/spirit/home/classic/utility/confix.hpp>
#include <iostream>
#include <string>
namespace client {
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
bool parse(Iterator first, Iterator last) {
using qi::char_;
qi::rule< Iterator, std::string(), ascii::space_type > text;
using namespace boost::spirit::classic;
qi::rule<Iterator, std::string()> myword2 =
confix_p('"', *c_escape_ch_p, '"') ; // ERROR!
text = myword2;
bool r = qi::phrase_parse(first, last, text, ascii::space);
if (first != last)
return false;
return r;
}
}
int main () {
std::string s = "\"foo\"";
bool ok = client::parse(s.begin(), s.end());
std::cout << "OK? " << (ok ? "y" : "n") << std::endl;
return 0;
}
The compiler error is a failed static assert instantiated from the line with confix:
// 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.
BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
So, it says it's not a valid expression. How is it used correctly?
P.S.: I'm using Boost 1.45.
You are trying to combine classic (old, V1, ...) boost::spirit::classic and (new, V2) boost::spirit::qi.
This is not going to work. The newer stuff is a complete, and incompatible, rewrite. See the 'Porting from Spirit 1.8.x' notes in the documentation.
As for the question on how to parse escaped C/C++ strings using boost::spirit::qi, the following article will be helpful:
Parsing Escaped String Input Using Spirit.Qi

Resources