Semantic actions require Phoenix Actors, which are deferred functions.
Options:
- Adaptation macros https://www.boost.org/doc/libs/1_73_0/libs/phoenix/doc/html/phoenix/modules/function/adapting_functions.html
phoenix::function<>
phoenix::bind
- write your own, see for more details https://www.boost.org/doc/libs/1_68_0/libs/spirit/doc/html/spirit/qi/tutorials/semantic_actions.html#spirit.qi.tutorials.semantic_actions.examples_of_semantic_actions
Simplified Test Bed
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
Adaptation
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
Using 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