From the documentation, it said says that when I use the expect operator, I should get an expectation_failure when the operator fails to match. I want to catch the exception to instruct the user where the erroneous input is. But it seems I get some kind of wrapped exception instead:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::spirit::x3::expectation_failure<__gnu_cxx::__normal_iterator<char*, std::string> > > >'
what(): boost::spirit::x3::expectation_failure
Aborted
My catch statements are:
try {
r = parse(iter, end, wctl_parser::entry, root);
} catch (x3::expectation_failure<char const*> const& x) {
std::cout << "Never runs,";
} catch (x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "me neither" << std::endl;
}
Update: Here is a small program exhibiting the behaviour:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace wccs_parser {
namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;
namespace qi = boost::spirit::qi;
struct AstNullProcess;
struct AstChoiceProcess;
using AstAnyProcess = x3::variant<
x3::forward_ast<AstNullProcess>,
x3::forward_ast<AstChoiceProcess>
>;
struct AstNullProcess {};
struct AstChoiceProcess {
AstAnyProcess left;
AstAnyProcess right;
};
} // End namespace
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChoiceProcess, left, right)
namespace wccs_parser {
template <typename T> auto rule = [](const char* name = typeid(T).name()) {
struct _{};
return x3::rule<_, T> {name};
};
template <typename T> auto as = [](auto p) { return rule<T>() = p; };
auto nullProcess = as<AstNullProcess>(x3::omit['0']);
auto const choiceActual = as<AstChoiceProcess> (nullProcess > '+' > nullProcess);
auto const choice = rule<AstAnyProcess> ("choice")
= nullProcess >> !x3::lit('+')
| choiceActual;
auto const entry = x3::skip(ascii::space) [choice];
} //End namespace
namespace x3 = boost::spirit::x3;
int main() {
std::string str("0 + ");
wccs_parser::AstAnyProcess root;
auto iter = str.begin();
auto end = str.end();
bool r = false;
try {
r = parse(iter, end, wccs_parser::entry, root);
} catch (x3::expectation_failure<char const*> const& x) {
std::cout << "Never runs," << std::endl;
} catch (x3::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "me neither" << std::endl;
}
if (r) {
std::cout << str << std::endl << std::endl << " Parses OK: " << std::endl;
std::cout << "\n-------------------------\n";
} else {
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
if (iter != end) std::cout << "Partial match" << std::endl;
return 0;
}
If you catch by const_iterator, you will want to make sure const_iterator is what you pass to parse:
std::string const str("0 + ");
See it Live On Coliru
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <vector>
namespace wccs_parser {
using boost::spirit::x3::variant;
using boost::spirit::x3::forward_ast;
struct AstNullProcess;
struct AstChoiceProcess;
using AstAnyProcess = variant<
forward_ast<AstNullProcess>,
forward_ast<AstChoiceProcess>
>;
struct AstNullProcess {};
struct AstChoiceProcess {
AstAnyProcess left;
AstAnyProcess right;
};
} // End namespace
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChoiceProcess, left, right)
namespace wccs_parser {
namespace x3 = boost::spirit::x3;
using x3::expectation_failure;
template <typename T> auto rule = [](const char* name = typeid(T).name()) {
struct _{};
return x3::rule<_, T> {name};
};
template <typename T> auto as = [](auto p, const char* name = typeid(T).name()) { return rule<T>(name) = p; };
auto nullProcess = as<AstNullProcess>(x3::omit['0'], "nullProcess");
auto const choiceActual = as<AstChoiceProcess> (nullProcess > '+' > nullProcess, "choiceActual");
auto const choice = rule<AstAnyProcess> ("choice")
= nullProcess >> !x3::lit('+')
| choiceActual;
auto const entry = x3::skip(x3::ascii::space) [choice];
}
int main() {
std::string const str("0 + ");
try {
auto iter = str.cbegin();
auto end = str.cend();
wccs_parser::AstAnyProcess root;
bool const r = parse(iter, end, wccs_parser::entry, root);
if (r) {
std::cout << str << "\n\n Parses OK: \n";
std::cout << "\n-------------------------\n";
} else {
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
if (iter != end) {
std::cout << "Partial match, leaving '" << std::string(iter,end) << "'\n";
}
} catch (wccs_parser::expectation_failure<std::string::const_iterator> const& e) {
std::cout << "Expected: " << e.which() << " at '" << std::string(e.where(), str.end()) << "'\n";
return 1;
}
}
Prints
Expected: nullProcess at ' '
Related
In the following programm std::cout << ex.what() << std::endl; does not print anything.
What's the problem with this code?
#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <cstring>
class Interval_OutOfRange : public std::out_of_range {
public:
std::stringstream ss;
Interval_OutOfRange(int min, int max, int value)
: std::out_of_range{"interval value out of range"}
{
ss << value << " is not inside intervall [" << min << ", " << max << "]";
}
char const * what() const noexcept override {
return ss.str().c_str();
}
};
template<int Min, int Max>
class Interval {
public:
Interval(int value) : value_{value} {
throw Interval_OutOfRange{Min, Max, value};
}
auto get() const { return value_; }
auto set(int value) { value_ = value; }
private:
int value_;
};
int main() {
try {
Interval<7, 17> obj(19);
}
catch (Interval_OutOfRange& ex) {
std::cout << ex.what() << std::endl;
}
}
I need to parse a key value pair, where key itself is a fixed string lke 'cmd' in the example. Unfortunately qi::lit has no synthesized attribute and qi::char_ parses no fixed string.
Following code does not compile. I would need that result.name == cmd after execution.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType
{
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type> rule = qi::lit("cmd") >> "=" >> qi::int_;
for (std::string const s : {"cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
qi::lit does not expose an attribute. qi::string does:
rule = qi::string("cmd") >> "=" >> qi::int_;
Live On Coliru
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <string>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
struct CommandRuleType {
std::string name;
int arg;
};
BOOST_FUSION_ADAPT_STRUCT(CommandRuleType, name, arg)
int main() {
qi::rule<std::string::const_iterator, CommandRuleType(), qi::space_type>
rule = qi::string("cmd") >> "=" >> qi::int_;
for (std::string const s : { "cmd = 1" }) {
std::cout << std::quoted(s) << " -> ";
CommandRuleType result;
if (qi::phrase_parse(s.begin(), s.end(), rule, qi::space, result)) {
std::cout << "result: " << result.name << "=" << result.arg << "\n";
} else {
std::cout << "parse failed\n";
}
}
}
Prints
"cmd = 1" -> result: cmd=1
I want to catch boost::lexicat_cast overflows the same way I can catch boost::numeric_cast overflows. Is it possible?
The first try block below throws a boost::numeric::negative_overflow.
The second block does not throw an exception (isn't this a lexical_cast bug?)
Though unsigned int is used in the example below, I am looking for a method that would work for any integer type.
#include <boost/numeric/conversion/cast.hpp>
#include <boost/lexical_cast.hpp>
int main()
{
unsigned int i;
try
{
int d =-23;
i = boost::numeric_cast<unsigned int>(d);
}
catch (const boost::numeric::bad_numeric_cast& e)
{
std::cout << e.what() << std::endl;
}
std::cout << i << std::endl; // 4294967273
try
{
char c[] = "-23";
i = boost::lexical_cast<unsigned int>(c);
}
catch (const boost::bad_lexical_cast& e)
{
std::cout << e.what() << std::endl;
}
std::cout << i << std::endl; // 4294967273
return 0;
}
You could write what you want using a modicum of Spirit:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <iostream>
template <typename Out, typename In> Out numeric_lexical_cast(In const& range) {
Out value;
{
using namespace boost::spirit::qi;
using std::begin;
using std::end;
if (!parse(begin(range), end(range), auto_ >> eoi, value)) {
struct bad_numeric_lexical_cast : std::domain_error {
bad_numeric_lexical_cast() : std::domain_error("bad_numeric_lexical_cast") {}
};
throw bad_numeric_lexical_cast();
}
}
return value;
}
int main()
{
for (std::string const& input : { "23", "-23" }) try {
std::cout << " == input: " << input << " -> ";
auto i = numeric_lexical_cast<unsigned int>(input);
std::cout << i << std::endl;
} catch (std::exception const& e) {
std::cout << e.what() << std::endl;
}
}
Prints
== input: 23 -> 23
== input: -23 -> bad_numeric_lexical_cast
The code that works is the following:
#include <boost/variant.hpp>
#include <string>
#include <map>
#include <iostream>
int main(int argc, char** argv) {
std::map<std::string, boost::variant<int, std::string> > values;
values["a"] = 10;
values["b"] = "bstring";
values["c"] = "cstring";
for (const auto &p : values) {
std::cout << p.first << " = ";
if (p.second.type() == typeid(std::string)) {
std::cout << boost::get<std::string>( p.second ) << " (found string)" << std::endl;
} else if ( p.second.type() == typeid(int)) {
std::cout << boost::get<int>( p.second ) << " (found int)" << std::endl;
} else if ( p.second.type() == typeid(bool)) {
std::cout << boost::get<bool>( p.second ) << " (found bool)" << std::endl;
} else {
std::cout << " not supported type " << std::endl;
}
}
}
The output (g++ test.cpp -std=c++11):
a = 10
b = bstring
c = cstring
The code that does not work is exactly the same, except the line that defines the std::map
modifying the line of the map definition to:
std::map<std::string, boost::variant<int, std::string, bool> > values;
the output is different:
a = 10
b = c =
The if statement that refers to std::string comparison does not succeed. What is the problem?
In your code, values["b"] = "bstring"; creates a bool value, when both std::string and bool are in the variant type.
A fix is values["b"] = std::string("bstring");.
Or, in C++14:
using namespace std::string_literals;
values["b"] = "bstring"s;
It is a well-known annoyance that string literals better convert to bool than to std::string:
#include <iostream>
#include <string>
void f(std::string) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
void f(std::string const&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
void f(bool) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
int main() {
f("hello");
}
Outputs:
void f(bool)
I have the following code:
#include <boost/any.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
template <typename Iterator>
struct parser : boost::spirit::qi::grammar<Iterator, boost::any(), boost::spirit::qi::ascii::space_type>
{
parser() : parser::base_type(start)
{
start %= boost::spirit::qi::int_ | boost::spirit::qi::lexeme['"' >> +(boost::spirit::qi::char_ - '"') >> '"']; // example: 0 or "str"
}
boost::spirit::qi::rule<Iterator, boost::any(), boost::spirit::qi::ascii::space_type> start;
};
int main()
{
const std::string input_data("\"str\"");
boost::any var = 0;
auto itr = input_data.begin();
auto end = input_data.end();
parser<decltype(itr)> g;
bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, var);
if (res && itr == end)
{
std::cout << "Parsing succeeded \n";
try
{
std::cout << boost::any_cast<std::string>(var) << '\n';
}
catch (const boost::bad_any_cast& ex)
{
std::cerr << ex.what() << '\n';
}
}
else
{
std::cout << "Parsing failed \n";
}
}
It compiles fine until I add
boost::spirit::qi::lexeme['"' >> +(boost::spirit::qi::char_ - '"') >> '"']
I can't post the errors here and on pastie.org because of the characters amount limitations.
What am I doing wrong? How can I fix it?
Again, why would you want to complicate matters and make it slow by using boost::any?
Anyways, +char_ exposes std::vector<char> and apparently the attribute compatibility rules don't want to decide what to transform this to.
Make it explicit with as_string: Live On Coliru
#include <boost/any.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
template <typename Iterator>
struct parser : boost::spirit::qi::grammar<Iterator, boost::any(), boost::spirit::qi::ascii::space_type>
{
parser() : parser::base_type(start)
{
using namespace boost::spirit::qi;
start %= int_ | as_string [ lexeme['"' >> +(char_ - '"') >> '"'] ]; // example: 0 or "str"
}
boost::spirit::qi::rule<Iterator, boost::any(), boost::spirit::qi::ascii::space_type> start;
};
int main()
{
// const std::string input_data("\"str\"");
const std::string input_data("123");
for (std::string const& input_data : { "\"str\"", "123" })
{
boost::any var = boost::none;
auto itr = input_data.begin();
auto end = input_data.end();
parser<decltype(itr)> g;
bool res = boost::spirit::qi::phrase_parse(itr, end, g, boost::spirit::ascii::space, var);
if (res && itr == end)
{
std::cout << "Parsing succeeded \n";
try { std::cout << boost::any_cast<int> (var) << '\n'; } catch (const boost::bad_any_cast& ex) { std::cerr << ex.what() << '\n'; }
try { std::cout << boost::any_cast<std::string>(var) << '\n'; } catch (const boost::bad_any_cast& ex) { std::cerr << ex.what() << '\n'; }
}
else
{
std::cout << "Parsing failed \n";
}
}
}