Where is the fault? - c++11

complier error
Error C2440 '=': cannot convert from 'const std::basic_string,std::allocator>' to 'const char *'
int main(void)
{
/*
complier error
Error C2440 '=': cannot convert from 'const std::basic_string<char,std::char_traits<char>,std::allocator<char>>' to 'const char *'
primer C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\VC\Tools\MSVC\14.22.27706\include\xutility 1745
*/
list<string> names = {"A","B"};
vector<const char*> vnames;
vnames.assign(names.cbegin(), names.cend());//OK
/*for (auto it = names.cbegin(); it != names.cend(); ++it)
{
cout << *it << endl;
}*/
for (auto it = vnames.cbegin(); it != vnames.cend(); ++it)
{
cout << *it << endl;
}
cout << "hello world" << endl;
return 0;
}

This:
vnames.assign(names.cbegin(), names.cend());//OK
Your comment says "OK", but it's not OK. You can't assign std::string objects to const char* ones. If you want to get the C string pointers of each std::string and put it into vnames, then you have to:
for (const auto& i : names) {
vnames.push_back(i.c_str());
}
After that, you need to be very careful not to modify the names vector in any way that would cause a reallocation. Doing so will invalidate the pointers stored in vnames.

Related

Q: Boost Program Options using std::filesystem::path as option fails when the given path contains spaces

I have a windows command line program using Boost.Program_Options. One option uses a std::filesystem::path variable.
namespace fs = std::filesystem;
namespace po = boost::program_options;
fs::path optionsFile;
po::options_description desc( "Options" );
desc.add_options()
("help,h", "Help screen")
("options,o", po::value<fs::path>( &optionsFile ), "file with options");
calling the program with -o c:\temp\options.txt or with -o "c:\temp\options.txt" works fine, but calling the program with -o "c:\temp\options 1.txt" fails with this error:
error: the argument( 'c:\temp\options 1.txt' ) for option '--options' is invalid
The content of argv in this case is:
argv[0] = Exepath
argv[1] = -o
argv[2] = c:\temp\options 1.txt
This is the full code:
#include <boost/program_options.hpp>
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
namespace po = boost::program_options;
int wmain( int argc, wchar_t * argv[] )
{
try
{
fs::path optionsFile;
po::options_description desc( "Options" );
desc.add_options()
("help,h", "Help screen")
("options,o", po::value<fs::path>( &optionsFile ), "File containing the command and the arguments");
po::wcommand_line_parser parser{ argc, argv };
parser.options( desc ).allow_unregistered().style(
po::command_line_style::default_style |
po::command_line_style::allow_slash_for_short );
po::wparsed_options parsed_options = parser.run();
po::variables_map vm;
store( parsed_options, vm );
notify( vm );
if( vm.count( "help" ) )
{
std::cout << desc << '\n';
return 0;
}
std::cout << "optionsFile = " << optionsFile << "\n";
}
catch( const std::exception & e )
{
std::cerr << "error: " << e.what() << "\n";
return 1;
}
return 0;
}
How can I handle paths containing whitespace correctly? Is that even possible using std::filesystem::path or do I have to use std::wstring?
Indeed I could reproduce this. Replacing fs::path with std::string fixed it.
Here's a side-by-side reproducer:
Live On Coliru
#include <boost/program_options.hpp>
#include <filesystem>
#include <iostream>
namespace po = boost::program_options;
template <typename Path> static constexpr auto Type = "[unknown]";
template <> constexpr auto Type<std::string> = "std::string";
template <> constexpr auto Type<std::filesystem::path> = "fs::path";
template <typename Path>
bool do_test(int argc, char const* argv[]) try {
Path optionsFile;
po::options_description desc("Options");
desc.add_options() //
("help,h", "Help screen") //
("options,o", po::value<Path>(&optionsFile),
"File containing the command and the arguments");
po::command_line_parser parser{argc, argv};
parser.options(desc).allow_unregistered().style(
po::command_line_style::default_style |
po::command_line_style::allow_slash_for_short);
auto parsed_options = parser.run();
po::variables_map vm;
store(parsed_options, vm);
notify(vm);
if (vm.count("help")) {
std::cout << desc << '\n';
return true;
}
std::cout << "Using " << Type<Path> << "\toptionsFile = " << optionsFile << "\n";
return true;
} catch (const std::exception& e) {
std::cout << "Using " << Type<Path> << "\terror: " << e.what() << "\n";
return false;
}
int main() {
for (auto args : {
std::vector{"Exepath", "-o", "c:\\temp\\options1.txt"},
std::vector{"Exepath", "-o", "c:\\temp\\options 1.txt"},
})
{
std::cout << "\n -- Input: ";
for (auto& arg : args) {
std::cout << " " << std::quoted(arg);
}
std::cout << "\n";
int argc = args.size();
args.push_back(nullptr);
do_test<std::string>(argc, args.data());
do_test<std::filesystem::path>(argc, args.data());
}
}
Prints
-- Input: "Exepath" "-o" "c:\\temp\\options1.txt"
Using std::string optionsFile = c:\temp\options1.txt
Using fs::path optionsFile = "c:\\temp\\options1.txt"
-- Input: "Exepath" "-o" "c:\\temp\\options 1.txt"
Using std::string optionsFile = c:\temp\options 1.txt
Using fs::path error: the argument ('c:\temp\options 1.txt') for option '--options' is invalid
The reason most likely is that extraction from the command line argument defaults to using operator>> on a stringstream¹. If that has skipws set (as all C++ istreams do by default), then whitespace stops the "parse" and the argument is rejected because it is not fully consumed.
However, modifying the code to include a validate overload that fires for paths, adding std::noskipws didn't help!
template <class CharT>
void validate(boost::any& v, std::vector<std::basic_string<CharT>> const& s,
std::filesystem::path* p, int)
{
assert(s.size() == 1);
std::basic_stringstream<CharT> ss;
for (auto& el : s)
ss << el;
path converted;
ss >> std::noskipws >> converted;
if (!ss.eof())
throw std::runtime_error("Invalid path format");
v = std::move(converted);
}
Apparently, operator>> for fs::path doesn't obey noskipws. A look at the docs confirms:
Performs stream input or output on the path p. std::quoted is used so that spaces do not cause truncation when later read by stream input operator.
This gives us the workaround:
Workaround
template <class CharT>
void validate(boost::any& v, std::vector<std::basic_string<CharT>> const& s,
std::filesystem::path* p, int)
{
assert(s.size() == 1);
std::basic_stringstream<CharT> ss;
for (auto& el : s)
ss << std::quoted(el);
path converted;
ss >> std::noskipws >> converted;
if (ss.peek(); !ss.eof())
throw std::runtime_error("excess path characters");
v = std::move(converted);
}
Here we balance the std::quoted quoting/escaping as required.
Live Demo
Proof Of Concept:
Live On Coliru
#include <boost/program_options.hpp>
#include <filesystem>
#include <iostream>
namespace std::filesystem {
template <class CharT>
void validate(boost::any& v, std::vector<std::basic_string<CharT>> const& s,
std::filesystem::path* p, int)
{
assert(s.size() == 1);
std::basic_stringstream<CharT> ss;
for (auto& el : s)
ss << std::quoted(el);
path converted;
ss >> std::noskipws >> converted;
if (ss.peek(); !ss.eof())
throw std::runtime_error("excess path characters");
v = std::move(converted);
}
}
namespace po = boost::program_options;
template <typename Path> static constexpr auto Type = "[unknown]";
template <> constexpr auto Type<std::string> = "std::string";
template <> constexpr auto Type<std::filesystem::path> = "fs::path";
template <typename Path>
bool do_test(int argc, char const* argv[]) try {
Path optionsFile;
po::options_description desc("Options");
desc.add_options() //
("help,h", "Help screen") //
("options,o", po::value<Path>(&optionsFile),
"File containing the command and the arguments");
po::command_line_parser parser{argc, argv};
parser.options(desc).allow_unregistered().style(
po::command_line_style::default_style |
po::command_line_style::allow_slash_for_short);
auto parsed_options = parser.run();
po::variables_map vm;
store(parsed_options, vm);
notify(vm);
if (vm.count("help")) {
std::cout << desc << '\n';
return true;
}
std::cout << "Using " << Type<Path> << "\toptionsFile = " << optionsFile << "\n";
return true;
} catch (const std::exception& e) {
std::cout << "Using " << Type<Path> << "\terror: " << e.what() << "\n";
return false;
}
int main() {
for (auto args : {
std::vector{"Exepath", "-o", "c:\\temp\\options1.txt"},
std::vector{"Exepath", "-o", "c:\\temp\\options 1.txt"},
})
{
std::cout << "\n -- Input: ";
for (auto& arg : args) {
std::cout << " " << std::quoted(arg);
}
std::cout << "\n";
int argc = args.size();
args.push_back(nullptr);
do_test<std::string>(argc, args.data());
do_test<std::filesystem::path>(argc, args.data());
}
}
Now prints
-- Input: "Exepath" "-o" "c:\\temp\\options1.txt"
Using std::string optionsFile = c:\temp\options1.txt
Using fs::path optionsFile = "c:\\temp\\options1.txt"
-- Input: "Exepath" "-o" "c:\\temp\\options 1.txt"
Using std::string optionsFile = c:\temp\options 1.txt
Using fs::path optionsFile = "c:\\temp\\options 1.txt"
¹ this actually happens inside boost::lexical_cast which comes from Boost Conversion

Selecting which overload is used in c++11

In the following code, as none of the arguments is const, i can't understand why the second overload is called in the 3 following cases.
#include <iostream>
#include <algorithm>
using namespace std;
void ToLower( std::string& ioValue )
{
std::transform( ioValue.begin(), ioValue.end(), ioValue.begin(), ::tolower );
}
std::string ToLower( const std::string& ioValue )
{
std::string aValue = ioValue;
ToLower(aValue);
return aValue;
}
int main()
{
string test = "test";
cout<<"Hello World" << endl;
// case 1
cout << ToLower("test") << endl;
// case 2
cout << ToLower(static_cast<string>(test)) << endl;
// case 3
cout << ToLower(string(test)) << endl;
}
In all 3 cases you are creating a temporary std::string, this is an unnamed object, an R-value. R-values aren't allowed to bind to non-const l-value references (T&) and so only the overload taking const std::string& ioValue is valid.
The reasoning is the return type is std::string for the second function but void for the first. std::cout << (void) << std::endl is not a valid set of operations. std::cout << (std::string) << std::endl is. If you return a std::string& from the first function you'd probably see #2 & #3 probably use your first function call.

How to correctly transfer the ownership of a shared_ptr?

I have the following code snipet:
// code snipet one:
#include <memory>
#include <iostream>
#include <queue>
struct A {
uint32_t val0 = 0xff;
~A() {
std::cout << "item gets freed" << std::endl;
}
};
typedef std::shared_ptr<A> A_PTR;
int main()
{
std::queue<A_PTR> Q;
Q.push(std::make_shared<A>());
auto && temp_PTR = Q.front();
std::cout << "first use count = " << temp_PTR.use_count() << std::endl;
Q.pop();
std::cout << "second use count = " << temp_PTR.use_count() <<std::endl;
return 0;
}
After running it, I got the result as following:
first use count = 1
item gets freed
second use count = 0
Q1: is anybody can explain what the type of temp_PTR after the third line of main function is called?
if I change that line as
A_PTR && temp_PTR = Q.front();
compiler complains that
main.cpp: In function 'int main()':
main.cpp:26:32: error: cannot bind '__gnu_cxx::__alloc_traits > >::value_type {aka std::shared_ptr}' lvalue to 'A_PTR&& {aka std::shared_ptr&&}'
A_PTR && temp_PTR = Q.front();
Q2: I remember that the return value of a function should be a r-value, but it seems here the compiler tell me: " hey, the return value of Queue.front() is a l-value", why is here?
For Q2, I just check the C++ docs, that the return value of Queue.front() is refernece, that means it return a l-value
reference& front();
const_reference& front() const;
For Q3, it works for A_PTR temp_PTR = std::move(Q.front());, it is what I want.

Is it a boost spirit regression?

I have a boost spirit parser which works very well with boost 1.46.1, and which doesn't work with boost 1.54.
This parser extract informations from the following sentence, which is a variable initialization in a DSEL : "Position #start=0;0;0".
Informations extracted from this sentence are stored in a structure :
Type of the variable will be stored (here position) ;
Name of variable (here start) ;
The "#" means that the variable is "static" ;
The value of the variable (0;0;0).
The code to extract these inforamtions is as follow :
#include <iostream>
#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
using namespace std;
using namespace boost::spirit;
struct VariableInitialization
{
std::string m_type;
bool m_is_static;
std::string m_name;
std::string m_value;
};
BOOST_FUSION_ADAPT_STRUCT(
VariableInitialization,
(std::string, m_type)
(bool, m_is_static)
(std::string, m_name)
(std::string, m_value)
)
template <typename Iterator>
struct VariableInitializationParser : qi::grammar<Iterator, VariableInitialization(), ascii::space_type> {
/*!
* IsStatic is a mapping betwen a char and a boolean
*/
struct IsStatic_ : qi::symbols<char, bool> {
IsStatic_()
{
add("#", false)("#", true);
}
}IsStatic;
VariableInitializationParser() :
VariableInitializationParser::base_type(start) {
using qi::lit;
using ascii::char_;
using qi::_val;
using qi::_1;
/*!
* For now, type is one of the three following litterals :
*/
var_type %= lit("Position")|lit("String")|lit("Numeric")|lit("Integer")|lit("Trajectory");
/*!
* identifier is how a variable can be named. Name of variable is an alpha (a-zA-Z) or an _,followed
* by any alpha numeric (a-zA-Z0-9) or a _. The followings are correct :
* _toto _T5ot_To t1oTo ...
* The following are incorrect
* 12toto -tiotp ...
*/
identifier %= ((ascii::alpha|char_('_')) >> *(ascii::alnum|char_('_')));
/*!
* var value can be anything because it's parsed by someone else.
*/
var_value %= qi::lexeme[*(char_)];
start = var_type >> IsStatic >> identifier >> '=' >> var_value;
}
qi::rule<Iterator, std::string(), ascii::space_type> var_type;
qi::rule<Iterator, std::string(), ascii::space_type> identifier;
qi::rule<Iterator, std::string(), ascii::space_type> var_value;
qi::rule<Iterator, VariableInitialization(), ascii::space_type> start;
};
int main()
{
VariableInitialization variable;
std::string input = "Position #toto=1;2;2";
std::string::const_iterator iter = input.begin();
std::string::const_iterator end = input.end();
// The phrase_parse call wil fill the structure "variable" with the good values if the syntax is correct.
// if the syntax is not correct, the method will return false.
// So if input = "Integer #toto= 6", variable.m_type == "Integer", variable.m_isStatic==true,
// variable.m_name=="toto" and variable.m_vale="6".
VariableInitializationParser<std::string::const_iterator> m_parser;
bool ok = phrase_parse(iter, end, m_parser, boost::spirit::ascii::space, variable);
if(!ok) return false;
std::cout << "Boost version : " << BOOST_VERSION << std::endl;
std::cout << "Type : " << variable.m_type << std::endl
<< "Is Static : " << variable.m_is_static << std::endl
<< "Name :" << variable.m_name << std::endl
<< "Value :" << variable.m_value << std::endl;
return 0;
}
Output of the following code is different in Boost 1.46.1 and in boost 1.53.
With boost 1.46.1 I have the following Output :
Boost version : 104601
Type : Position
Is Static : 1
Name :toto
Value :1;2;2
With boost 1.54 I have :
Boost version : 105400
Type :
Is Static : 1
Name :toto
Value :1;2;2
As you can see in boost 1.54 the Position output is not filled by the parser.
I read (maybe not carefully) the changelog of Fusion and Spirit, and I can't find why this happens.
Does someone have an explaination ?
No this is not a regression. Quite clearly you depended on undocumented (possibly undefined) behaviour.
Just change lit into string, or just slightly more compact:
var_type = qi::raw[lit("Position")|"String"|"Numeric"|"Integer"|"Trajectory"];
See it Live On Coliru
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
using namespace std;
using namespace boost::spirit;
struct VariableInitialization
{
std::string m_type;
bool m_is_static;
std::string m_name;
std::string m_value;
};
BOOST_FUSION_ADAPT_STRUCT(
VariableInitialization,
(std::string, m_type)
(bool, m_is_static)
(std::string, m_name)
(std::string, m_value)
)
template <typename Iterator>
struct VariableInitializationParser : qi::grammar<Iterator, VariableInitialization(), ascii::space_type> {
struct IsStatic_ : qi::symbols<char, bool> {
IsStatic_() {
add("#", false)("#", true);
}
} IsStatic;
VariableInitializationParser() :
VariableInitializationParser::base_type(start) {
using qi::lit;
using ascii::char_;
var_type = qi::raw[lit("Position")|"String"|"Numeric"|"Integer"|"Trajectory"];
identifier = (ascii::alpha|'_') >> *(ascii::alnum|'_');
var_value = qi::lexeme[*(char_)];
start = var_type >> IsStatic >> identifier >> '=' >> var_value;
}
qi::rule<Iterator, std::string(), ascii::space_type> var_type;
qi::rule<Iterator, std::string(), ascii::space_type> identifier;
qi::rule<Iterator, std::string(), ascii::space_type> var_value;
qi::rule<Iterator, VariableInitialization(), ascii::space_type> start;
};
int main()
{
VariableInitialization variable;
std::string input = "Position #toto=1;2;2";
std::string::const_iterator iter = input.begin();
std::string::const_iterator end = input.end();
VariableInitializationParser<std::string::const_iterator> m_parser;
bool ok = phrase_parse(iter, end, m_parser, boost::spirit::ascii::space, variable);
if(!ok) return false;
std::cout << "Boost version : " << BOOST_VERSION << "\n";
std::cout << "Type : " << variable.m_type << "\n"
<< "Is Static : " << variable.m_is_static << "\n"
<< "Name :" << variable.m_name << "\n"
<< "Value :" << variable.m_value << "\n";
}

How would I implement a forth-style reverse-polish notation parser in boost spirit?

I'm trying to implement a parser for an old forth-based grammar where most of the functions take the form of: "num" "num" "command" where command is a string of some kind.
For example:
0 1 HSFF
41 SENSOR ON
1 12.0 BH 4 LNON
As you can see, the grammar is [mostly] reverse polish notation, with some string of arguments preceding the command. The grammar is pseudo white-space dependent, in that:
0 1 HSFF 41 SENSOR ON
Is as valid as:
0 1 HSFF
41 SENSOR ON
(In other words '\n' is treated just as a space)
Extra whitespace is also skipped, so:
0 1 HSFF 41 SENSOR ON
Is 2 valid commands with a lot of unnecessary whitespace.
All of this seemed simple enough, so I started chugging away at implementing the grammar. Of course, things are never as simple as they seem, and I found that my parser fails on the very first character (in this case an int). So, boiling things down, I tried implementing a single rule:
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
qi::rule<Iterator> Cmd_TARGETSENSPAIRCMD =
qi::int_ >> (lit("TARGET") | lit("SENSOR") | lit("PAIR") )
>> (lit("ON") | lit("OFF") | lit("ERASE") );
std::string in("0 TARGET ERASE\n");
Iterator = in.begin();
bool success = qi::parse(in.begin(), in.end(), Cmd_TARGETSENSPAIRCMD, ascii::space);
This code block always returns false, indicating that parsing has failed.
As you can see, the rule is that an int must be followed by two literals, in this case indicating whether the command is for a target, sensor, or pair, identified by the int, to be turned on, off, or erased.
If I look at the iterator to see where the parsing has stopped, it shows that it has failed immediately on the int. So I changed the rule to simply be +qi::int_, which succeeds in parsing the int, but fails on the literals. Shortening the rule to simply qi::int_ >> lit("TARGET") also fails.
I think the problem may be in the whitespace skipper I'm using, but I have been unable to determine what I'm doing wrong.
Is there a way to tell spirit that all tokens are separated by whitespace, with the exception of quoted strings (which turn into labels in my grammar)?
I have phantasized a little for you.
The first step I usually take is to come up with an AST model:
namespace Ast
{
enum Command { NO_CMD, TARGET, SENSOR, PAIR };
enum Modifier { NO_MODIFIER, ON, OFF, ERASE };
struct ModifiedCommand
{
Command cmd = NO_CMD;
Modifier mod = NO_MODIFIER;
};
struct OtherCommand
{
std::string token;
OtherCommand(std::string token = "") : token(std::move(token))
{ }
};
typedef boost::variant<int, double> Operand;
typedef boost::variant<Operand, ModifiedCommand, OtherCommand> RpnMachineInstruction;
typedef std::vector<RpnMachineInstruction> RpnMachineProgram;
}
As you can see I intend to distinguish integers and double for operand values, and I treat any "other" commands (like "HSSF") that wasn't actively described in your grammar as free-form tokens (uppercase alphabetical).
Now, we map the rule definitions onto this:
RpnGrammar() : RpnGrammar::base_type(_start)
{
_start = *_instruction;
_instruction = _operand | _mod_command | _other_command;
_operand = _strict_double | qi::int_;
_mod_command = _command >> _modifier;
_other_command = qi::as_string [ +qi::char_("A-Z") ];
// helpers
_command.add("TARGET", Ast::TARGET)("SENSOR", Ast::SENSOR)("PAIR", Ast::PAIR);
_modifier.add("ON", Ast::ON)("OFF", Ast::OFF)("ERASE", Ast::ERASE);
}
The grammar parses the result into a list of instructions (Ast::RpnMachineProgram), where each instruction is either an operand or an operation (a command with modifier, or any other free-form command like "HSSF"). Here are the rule declarations:
qi::rule<It, Ast::RpnMachineProgram(), Skipper> _start;
qi::rule<It, Ast::RpnMachineInstruction(), Skipper> _instruction;
qi::rule<It, Ast::ModifiedCommand(), Skipper> _mod_command;
qi::rule<It, Ast::Operand(), Skipper> _operand;
// note: omitting the Skipper has the same effect as wrapping with `qi::lexeme`
qi::rule<It, Ast::OtherCommand()> _other_command;
qi::real_parser<double, boost::spirit::qi::strict_real_policies<double> > _strict_double;
qi::symbols<char, Ast::Command> _command;
qi::symbols<char, Ast::Modifier> _modifier;
You can see it parse the sample from the question:
Parse succeeded, 10 stack instructions
int:0 int:1 'HSFF'
int:41 SENSOR [ON]
int:1 double:12 'BH'
int:4 'LNON'
The output is created with a sample visitor that you could use as inspiration for an interpreter/executor.
See it Live On Coliru
Full Listing
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <fstream>
namespace qi = boost::spirit::qi;
namespace Ast
{
enum Command { NO_CMD, TARGET, SENSOR, PAIR };
enum Modifier { NO_MODIFIER, ON, OFF, ERASE };
struct ModifiedCommand
{
Command cmd = NO_CMD;
Modifier mod = NO_MODIFIER;
};
struct OtherCommand
{
std::string token;
OtherCommand(std::string token = "") : token(std::move(token))
{ }
};
typedef boost::variant<int, double> Operand;
typedef boost::variant<Operand, ModifiedCommand, OtherCommand> RpnMachineInstruction;
typedef std::vector<RpnMachineInstruction> RpnMachineProgram;
// for printing, you can adapt this to execute the stack instead
struct Print : boost::static_visitor<std::ostream&>
{
Print(std::ostream& os) : os(os) {}
std::ostream& os;
std::ostream& operator()(Ast::Command cmd) const {
switch(cmd) {
case TARGET: return os << "TARGET" << " ";
case SENSOR: return os << "SENSOR" << " ";
case PAIR: return os << "PAIR" << " ";
case NO_CMD: return os << "NO_CMD" << " ";
default: return os << "#INVALID_COMMAND#" << " ";
}
}
std::ostream& operator()(Ast::Modifier mod) const {
switch(mod) {
case ON: return os << "[ON]" << " ";
case OFF: return os << "[OFF]" << " ";
case ERASE: return os << "[ERASE]" << " ";
case NO_MODIFIER: return os << "[NO_MODIFIER]" << " ";
default: return os << "#INVALID_MODIFIER#" << " ";
}
}
std::ostream& operator()(double d) const { return os << "double:" << d << " "; }
std::ostream& operator()(int i) const { return os << "int:" << i << " "; }
std::ostream& operator()(Ast::OtherCommand const& cmd) const {
return os << "'" << cmd.token << "'\n";
}
std::ostream& operator()(Ast::ModifiedCommand const& cmd) const {
(*this)(cmd.cmd);
(*this)(cmd.mod);
return os << "\n";
}
template <typename... TVariant>
std::ostream& operator()(boost::variant<TVariant...> const& v) const {
return boost::apply_visitor(*this, v);
}
};
}
BOOST_FUSION_ADAPT_STRUCT(Ast::ModifiedCommand, (Ast::Command, cmd)(Ast::Modifier, mod))
template <typename It, typename Skipper = qi::space_type>
struct RpnGrammar : qi::grammar<It, Ast::RpnMachineProgram(), Skipper>
{
RpnGrammar() : RpnGrammar::base_type(_start)
{
_command.add("TARGET", Ast::TARGET)("SENSOR", Ast::SENSOR)("PAIR", Ast::PAIR);
_modifier.add("ON", Ast::ON)("OFF", Ast::OFF)("ERASE", Ast::ERASE);
_start = *_instruction;
_instruction = _operand | _mod_command | _other_command;
_operand = _strict_double | qi::int_;
_mod_command = _command >> _modifier;
_other_command = qi::as_string [ +qi::char_("A-Z") ];
}
private:
qi::rule<It, Ast::RpnMachineProgram(), Skipper> _start;
qi::rule<It, Ast::RpnMachineInstruction(), Skipper> _instruction;
qi::rule<It, Ast::ModifiedCommand(), Skipper> _mod_command;
qi::rule<It, Ast::Operand(), Skipper> _operand;
// note: omitting the Skipper has the same effect as wrapping with `qi::lexeme`
qi::rule<It, Ast::OtherCommand()> _other_command;
qi::real_parser<double, boost::spirit::qi::strict_real_policies<double> > _strict_double;
qi::symbols<char, Ast::Command> _command;
qi::symbols<char, Ast::Modifier> _modifier;
};
int main()
{
std::ifstream ifs("input.txt");
typedef boost::spirit::istream_iterator It;
ifs.unsetf(std::ios::skipws);
RpnGrammar<It> grammar;
It f(ifs), l;
Ast::RpnMachineProgram program;
bool ok = qi::phrase_parse(f, l, grammar, qi::space, program);
if (ok)
{
std::cout << "Parse succeeded, " << program.size() << " stack instructions\n";
std::for_each(
program.begin(),
program.end(),
Ast::Print(std::cout));
}
else
{
std::cout << "Parse failed\n";
}
if (f != l)
{
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
}

Resources