Boost spirit X3: Unable to create an AST that has a optional list - c++14

I am trying to parse a optional list of things followed by a semicolon (this is a simplified example). Below is a example program:
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
namespace x3=boost::spirit::x3;
namespace ast
{
struct doubleOrString : x3::variant<double,std::string>{
using base_type::base_type;
using base_type::operator=;
};
struct pass {
std::vector<doubleOrString> dOrs;
};
}//ast namespace
BOOST_FUSION_ADAPT_STRUCT(ast::pass,dOrs)
x3::rule<class doubleOrString_class, ast::doubleOrString> const doubleOrString = "doubleOrString";
x3::rule<class pass_class, ast::pass> const pass = "pass";
x3::rule<class es_class, std::optional<ast::pass>> const es = "es";
auto const doubleOrString_def =x3::double_ | x3::string("AString");
auto const pass_def = doubleOrString % x3::lit(",");
auto const es_def = (pass > x3::lit(";")) | x3::lit(";");
BOOST_SPIRIT_DEFINE(doubleOrString,pass,es);
int main()
{
using boost::spirit::x3::ascii::space;
typedef std::string::const_iterator iterator_type;
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::optional<ast::pass> esdOrs;
iterator_type iter = str.begin();
iterator_type const end = str.end();
bool r = phrase_parse(iter, end, es, space, esdOrs);
if (r && iter == end)
{
std::cout << "\nGood!\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
However it fails to compile with the error:
usr/include/boost/spirit/home/x3/support/ast/variant.hpp:184:17: error: no match for ‘operator=’ (operand types are ‘boost::spirit::x3::variant<double, std::__cxx11::basic_string<char, std::char_traits, std::allocator<cha
r> > >::variant_type’ {aka ‘boost::variant<double, std::__cxx11::basic_string<char, std::char_traits, std::allocator > >’} and ‘std::optionalclient::ast::pass’)
26 184 | var = std::forward(rhs);
What am I doing wrong? The error is not very informative. It seems to work fine if I just do a optional of things.
I am using the following command (the file name is r1.cpp):
g++ --std=c++17 r1.cpp -o r1

With the following MRE(Minimal Reproducible Example):
#include <vector>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
namespace x3=boost::spirit::x3;
namespace ast
{
struct doubleOrString : x3::variant<double,std::string>{
using base_type::base_type;
using base_type::operator=;
};
struct pass {
std::vector<doubleOrString> dOrs;
};
}//ast namespace
BOOST_FUSION_ADAPT_STRUCT(ast::pass,dOrs)
x3::rule<class doubleOrString_class, ast::doubleOrString> const doubleOrString = "doubleOrString";
x3::rule<class pass_class, ast::pass> const pass = "pass";
x3::rule<class es_class, std::optional<ast::pass>> const es = "es";
auto const doubleOrString_def =x3::double_ | x3::string("AString");
auto const pass_def = doubleOrString % x3::lit(",");
auto const es_def = (pass > x3::lit(";")) | x3::lit(";");
BOOST_SPIRIT_DEFINE(doubleOrString,pass,es);
I get no compiler errors.
Please provide an MRE showing the error.

Related

How to pass a char to `boost::char_separator<char>`

Based on How do I tokenize a string in C++?
explicit char_separator(const Char* dropped_delims,
const Char* kept_delims = "",
empty_token_policy empty_tokens = drop_empty_tokens)
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;
int main(int, char**)
{
string text = "token, test string";
char_separator<char> sep(", ");
tokenizer< char_separator<char> > tokens(text, sep);
BOOST_FOREACH (const string& t, tokens) {
cout << t << "." << endl;
}
}
I need to pass in a char as the separator as follows:
void splitString(const string& text, char sepChar)
{
char_separator<char> sep(&sepChar);
tokenizer< char_separator<char> > tokens(text, sep);
BOOST_FOREACH (const string& t, tokens) {
cout << t << "." << endl;
}
}
Question> Is this right way to convert char to const char* for boost::char_separator?
Thank you

Error including boost/spirit/include/qi.hpp

I'm having the same error as occurs here. See 'compilation info' at the bottom for the long error message. Basically if I #include <boost/spirit/include/qi.hpp> I can't compile. Totally stumped by this one... I am compiling with g++ 4.9.3 on Debian using C++11. For reference the first part of the error message is:
In file included from /usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:35:0,
from /usr/include/boost/spirit/home/qi/nonterminal.hpp:14,
from /usr/include/boost/spirit/home/qi.hpp:20,
from /usr/include/boost/spirit/include/qi.hpp:16,
from prog.cpp:9:
/usr/include/boost/spirit/home/qi/reference.hpp: In instantiation of 'bool boost::spirit::qi::reference<Subject>::parse(Iterator&, const Iterator&, Context&, const Skipper&, Attribute&) const [with Iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >; Context = boost::spirit::context<boost::fusion::cons<std::basic_string<char>&, boost::fusion::nil_>, boost::spirit::locals<> >; Skipper = boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >; Attribute = std::basic_string<char>; Subject = const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>(), boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0l>, boost::spirit::unused_type, boost::spirit::unused_type>]':
My code is actually a little bit different to the above example:
My::Dictionary Parser::parse( const char * s ) {
std::string input(s); // input to parse
qi_my_protocol grammar; // create instance of parser
My::Dictionary msg; // map to receive results
bool result = qi::phrase_parse(
input.begin(),
input.end(),
grammar,
qi::space,
msg
); // returns true if successful
return msg;
}
Your string isn't const, so the iterators first and last aren't const iterators.
Either make the my_Parse function not a template (after all, you hardcode the iterator type on the grammar to be std::string::const_iterator anyways), or instantiate the grammar with the actual iterator type:
my_grammar<Iterator> g;
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <stdio.h>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
template <typename Iterator>
struct my_grammar : boost::spirit::qi::grammar<Iterator, std::string(), ascii::space_type>
{
my_grammar() : my_grammar::base_type(start)
{
using qi::alpha;
using qi::alnum;
start %=
(+alpha | +alnum)
;
}
qi::rule<Iterator, std::string(), ascii::space_type> start;
};
template <typename Iterator>
bool my_Parse(Iterator first, Iterator last, std::string& result)
{
using boost::spirit::ascii::space;
my_grammar<Iterator> g;
bool r = phrase_parse(first, last, g, space, result);
if (!r || first != last) // fail if we did not get a full match
return false;
return r;
}
int main() {
std::string str;
while (getline(std::cin, str))
{
std::string result = "";
if (my_Parse(str.begin(), str.end(), result))
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "got: " << result << std::endl;
std::cout << "\n-------------------------\n";
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
}

variadic template argument for std::function

Recently, I've been working on a little project alongside my c++ game-dev engine : it's a programming language, written in C++, in one header, named kickC. Here is what I have done so far : (See question below)
#ifndef KICK_C_INCLUDED_H
#define KICK_C_INCLUDED_H
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <exception>
#include <functional>
#include <unordered_map>
#include <vector>
#define LOG(x) std::cout << x << std::endl;
namespace strutil
{
inline unsigned CountWords(const std::string& value){
std::string temp = value;
std::replace_if(temp.begin(), temp.end(), std::ptr_fun<int, int>(std::isspace), ' ');
temp.erase(0, temp.find_first_not_of(" "));
if(temp.empty())
return 0;
return std::count(temp.begin(), std::unique(temp.begin(), temp.end()), ' ') + !std::isspace(*value.rbegin());
}
}
class KickCException : std::exception
{
public:
explicit KickCException(const char* msg, bool fatal = false)
: msg_(msg){}
explicit KickCException(const std::string& msg)
: msg_(msg){}
virtual ~KickCException() throw(){}
virtual const char* what() const throw(){
return std::string("[error :] [")
.append(msg_)
.append("]")
.c_str();
}
protected:
std::string msg_;
};
class KickCFileException : KickCException
{
public:
explicit KickCFileException(const char* msg)
: KickCException(msg){}
explicit KickCFileException(const std::string& msg)
: KickCException(msg){}
virtual ~KickCFileException() throw(){}
const char* what() const throw() override{
return std::string("[file error :] [")
.append(msg_)
.append("]")
.c_str();
}
};
class KickCEmptyStringException : KickCException
{
public:
explicit KickCEmptyStringException(const char* msg)
: KickCException(msg){}
explicit KickCEmptyStringException(const std::string& msg)
: KickCException(msg){}
virtual ~KickCEmptyStringException() throw(){}
const char* what() const throw() override{
return std::string("[empty string error :] [")
.append(msg_)
.append("]")
.c_str();
}
};
class KickCAPIBehaviourImplementation
{
public:
KickCAPIBehaviourImplementation(){}
~KickCAPIBehaviourImplementation(){}
void AddDefined(const std::string& str, std::function<void(void)> func){
m_values[str] = func;
}
void ParseAndApplyLine(const std::string& line){
std::istringstream iss(line);
for(unsigned i = 0; i < strutil::CountWords(line); ++i){
static std::string word = "";
iss >> word;
for(auto it_map = m_values.begin(); it_map != m_values.end(); ++it_map){
if(it_map->first == word)
{
(it_map->second)(/*HERE ! GIVE SOME ARGUMENTS ! */);
}
}
}
}
private:
std::unordered_map<std::string, std::function<void(void)>> ///so far, args is void... m_values;
};
#endif //KICK_C_INCLUDED_H
///src
int main(int argc, const char** args){
std::ifstream file("script.kick");
KickCAPIBehaviourImplementation kickCApiBehaviour;
try{
if(!file.is_open())
throw KickCFileException("unvalid fileName taken at input");
kickCApiBehaviour.AddDefined("print", [&](void){std::cout << "print found !" << std::endl;});
while(!file.eof()){
std::string line;
std::getline(file, line);
kickCApiBehaviour.ParseAndApplyLine(line);
}
}catch(KickCException& e){
LOG(e.what());
}
file.close();
std::cin.get();
}
So here is the Question : I would like to pass std::function (see class KickCAPIBehaviourImplementation ) a variable argument of types : I need to use variatic templates, of course, but the question how can I implement it so i end up calling my functions like this ?
kickCApiBehaviour.AddDefined("print", [&](int arg1, char * arg2, int arg3){std::cout << arg1 << arg2 << arg3 << std::endl;});
Move the parser into the std::function.
Where you add the function, include a signature:
// helper type:
template<class T>struct tag{using type=T;};
kickCApiBehaviour.AddDefined(
"print", // the name
tag<void(int,char*,int)>{}, // the signature
[&](int arg1, char * arg2, int arg3){
std::cout << arg1 << arg2 << arg3 << std::endl;
} // the operation
);
store a std::function< error_code(ParserState*) >. Inside AddDefined, store a lambda that includes a call to the code that parses arguments and calls the passed in lambda:
template<class R, class...Args, class F>
void AddDefined(std::string name, tag<R(Args...)>, F f) {
std::function< error_code(ParserState*) > r =
[f](ParserState* self)->error_code {
// here, parse each of Args... out of `self`
// then call `f`. Store its return value,
// back in `self`. If there is a parse error (argument mismatch, etc),
// return an error code, otherwise return no_error
};
m_values[name] = r;
};
then m_values contains the operation "take a parser state, and parse the arguments, and call the function in question on them".

Clang OS X compiler balks on this code but it works fine on Linux

The following sample code works fine under linux using g++4.8.2, using boost1_56. However, I get a strange linker error under MacOS X (Yosemite) using clang:
ld: internal error: atom not found in symbolIndex(__ZNSt3__112__hash_tableINS_17__hash_value_typeIKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEN15FRUIT_TUPLES4dataEEENS_22__unordered_map_hasherIS8_SB_NS9_8key_hashELb1EEENS_21__unordered_map_equalIS8_SB_NS9_9key_equalELb1EEENS5_ISB_EEE15__insert_uniqueIRKNS_4pairIS8_SA_EEEENSL_INS_15__hash_iteratorIPNS_11__hash_nodeISB_PvEEEEbEEOT_) for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Proces
The main.cpp file
#include "TupleFruits.hpp"
int main()
{
map_t fruitHash = InitializeFruitHash();
std::string fruit = "BANANA";
auto itr = fruitHash(fruit);
if (fruitHash.end() == itr)
{
std::cout << fruit << " not found in hash" << std::endl;
exit(1);
}
}
The FruitHash.cpp file:
#include "TupleFruits.hpp"
map_t InitializeFruitHash()
{
static map_t m;
data dBANANA = {0, 0, 6, false};
data dGRAPEFRUIT = {1, 1, 6, false};
data dSTRAWBERRY = {2, 2, 6, false};
m[BANANA] = dBANANA;
m[GRAPEFRUIT] = dGRAPEFRUIT;
m[STRAWBERRY] = dSTRAWBERRY;
return m;
}
The include file "HashData.hpp
#ifndef HASH_DATA_HPP
#define HASH_DATA_HPP
#include <string>
#include <unordered_map>
#include <cstring>
#include <iostream>
#include <tuple>
#include <boost/functional/hash.hpp>
typedef std::string fruit_key_t;
namespace HASH_TUPLES
{
struct key_hash : public std::unary_function<fruit_key_t, std::size_t>
{
std::size_t operator()(const fruit_key_t& k) const
{
std::hash<std::string> hash_fn;
return hash_fn(k);
}
};
struct key_equal : public std::binary_function<fruit_key_t, fruit_key_t, bool>
{
bool operator()(const fruit_key_t& v0, const fruit_key_t& v1) const
{
return (v0 == v1);
}
};
struct data
{
int row;
int column;
int precision;
bool isRipe;
inline bool operator ==(data d)
{
if (d.row == row && d.column == column)
return true;
else
return false;
}
friend std::ostream& operator << (std::ostream& os, const data& rhs) //Overloaded operator for '<<'
{ //for struct output
os << rhs.row << ", "
<< rhs.column;
return os;
}
};
typedef std::unordered_map<const fruit_key_t, data, key_hash, key_equal> map_t;
// ^ this is our custom hash
}
template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type,
typename T::mapped_type,
bool>
{
public:
bool operator() (typename T::value_type &pair,
typename T::mapped_type i) const
{
return pair.second == i;
}
};
#endif
The include file "TupleFruits.hpp"
#ifndef TUPLESFRUITS_HPP
#define TUPLESFRUITS_HPP
#include <boost/interprocess/containers/string.hpp>
#include "HashData.hpp"
using namespace HASH_TUPLES;
map_t InitializeFruitHash();
static std::string BANANA = "banana";
static std::string GRAPEFRUIT = "grapefruit";
static std::string STRAWBERRY = "strawberry";
#endif
I figured it out. Somehow -s (strip all symbols from binary) snuck in my Makefile

boost::program_options overloaded validate is ambiguous

I am trying to parse a list input from the command line.
My class is derived from vector
The compiler complains about a overloaded validate being ambiguous.
I can see why, but do not know how to solve this issue.
Please help.
Below is a minimal example that generates the error. If the type of ch_list is changed to a vector, this minimal example compiles.
// g++ -std=c++11 -Wall -Wextra -pedantic test.cpp -o test -lboost_program_options -lboost_system
#include <vector>
#include <boost/program_options.hpp>
#include <iostream>
#include <vector>
using namespace std;
class mylist : public vector<int> {
public:
friend istream &operator>>(istream &is, mylist& l) {
int val;
is >> val;
l.push_back(val);
return is;
}
friend ostream& operator<<(ostream& os, const mylist& l) {
return os << l[0];
}
};
int main (int argc, char* argv[])
{
//vector<int> ch_list; // This works
mylist ch_list; // This doesn't
namespace po = boost::program_options;
po::options_description desc("Allowed options");
desc.add_options()
("ch", po::value<decltype(ch_list)>(&ch_list), "Set channel numbers")
;
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
}
catch (po::error& e) {
cerr << "ERROR: " << e.what() << "\n";
return EXIT_FAILURE;
}
if (!ch_list.empty())
cout << ch_list[0] << "\n";
return EXIT_SUCCESS;
}
I get these errors
/usr/include/boost/program_options/detail/value_semantic.hpp: In instantiation of ‘void boost::program_options::typed_value<T, charT>::xparse(boost::any&, const std::vector<std::basic_string<charT> >&) const [with T = mylist; charT = char]’:
test.cpp:47:5: required from here
/usr/include/boost/program_options/detail/value_semantic.hpp:169:13: error: call of overloaded ‘validate(boost::any&, const std::vector<std::basic_string<char> >&, mylist*, int)’ is ambiguous
/usr/include/boost/program_options/detail/value_semantic.hpp:169:13: note: candidates are:
/usr/include/boost/program_options/detail/value_semantic.hpp:81:10: note: void boost::program_options::validate(boost::any&, const std::vector<std::basic_string<charT> >&, T*, long int) [with T = mylist; charT = char]
/usr/include/boost/program_options/detail/value_semantic.hpp:129:10: note: void boost::program_options::validate(boost::any&, const std::vector<std::basic_string<charT> >&, std::vector<_RealType>*, int) [with T = int; charT = char]
You can use custom validator. Your code would be:
#include <vector>
#include <boost/program_options.hpp>
#include <iostream>
#include <vector>
using namespace std;
namespace po = boost::program_options;
class mylist : public vector<int> {
public:
};
void validate(boost::any& v,
const vector<string>& values,
mylist*, int) {
mylist dvalues;
for(vector<string>::const_iterator it = values.begin();
it != values.end();
++it) {
stringstream ss(*it);
copy(istream_iterator<int>(ss), istream_iterator<int>(),
back_inserter(dvalues));
if(!ss.eof()) {
throw ("Invalid coordinate specification");
}
}
v = mylist(dvalues);
}
int main (int argc, char* argv[])
{
//vector<int> ch_list; // This works
mylist ch_list; // This doesn't
po::options_description desc("Allowed options");
desc.add_options()
("ch", po::value< mylist >(&ch_list)->multitoken(), "Set channel numbers")
;
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
}
catch (po::error& e) {
cerr << "ERROR: " << e.what() << "\n";
return EXIT_FAILURE;
}
for (auto cc : ch_list)
cout << cc << endl;
return EXIT_SUCCESS;
}
Reference: boost::program_options config file option with multiple tokens

Resources