With a simple Boost qi grammar, how can I make it rounding my number?
This is the parser:
factor =
float_ [_val = _1]
| ('-' >> factor [_val = -_1])
| ('+' >> factor [_val = _1])
;
This can parse a float and it can be also negative.
I want to round the float, so I would add something like this to the grammar:
| ('~' >> factor [_val = round(_1)])
But this results a compile-time error:
no type named ‘__type’ in ‘struct __gnu_cxx::__enable_if<false, double>’
This error is not too informative for me, can you please help? I want to be able to round a number, ie:
~1.8 -> 2
~1.2 -> 1
Note: I'm parsing with phrase_parse
.
Semantic actions require Phoenix Actors, which are deferred functions.
Options:
phoenix::function<>
phoenix::bind
Just parsing a number:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
int main() {
std::string s = "1.75";
double v;
if (qi::parse(begin(s), end(s), qi::double_, v)) {
std::cout << "Parsed: " << v << "\n";
}
}
Prints Live On Coliru:
Parsed: 1.75
Using the macros:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <cmath>
namespace qi = boost::spirit::qi;
int main() {
std::string s = "1.75";
double v;
if (qi::parse(begin(s), end(s), qi::double_, v)) {
std::cout << "Parsed: " << v << "\n";
std::cout << "Rounded: " << round(v) << "\n";
}
}
Prints Live On Coliru:
Parsed: 2
function<>
You can get away with hardcoding a signature here:
boost::phoenix::function<double(*)(double)> round_(::round);
However the real power comes with polymorphic calleables:
struct round_f {
template <typename T> auto operator()(T const& v) const {
using std::round; // activate ADL
return round(v);
}
};
boost::phoenix::function<round_f> round_{};
Now you can use the round_
actor on any type that has a free-function overload round
overload that is compatible. Handy if tomorrow you decide to parse long double
, float
or boost::multiprecision::cpp_dec_float
.
See it Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <cmath>
namespace qi = boost::spirit::qi;
struct round_f {
template <typename T> auto operator()(T const& v) const {
using std::round; // activate ADL
return round(v);
}
};
boost::phoenix::function<round_f> round_{};
int main() {
std::string s = "1.75";
double v;
using namespace qi::labels;
if (qi::parse(begin(s), end(s), qi::double_ [ _val = round_(_1) ], v)) {
std::cout << "Parsed: " << v << "\n";
}
}
Prints
Parsed: 2
phoenix::bind
As a lower-level building block, you can bind unwrapped calleables:
if (qi::parse(begin(s), end(s), qi::double_
[ _val = phoenix::bind(round_f{}, _1) ], v))
{
std::cout << "Parsed: " << v << "\n";
}
If you don't mind ugly:
if (qi::parse(begin(s), end(s), qi::double_
[ _val = phoenix::bind(static_cast<double(&)(double)>(std::round), _1) ], v))
{
std::cout << "Parsed: " << v << "\n";
}
See both Live On Coliru