I'm trying to write a boost::spirit::karma generator where some of the output depends on non-trivial properties of the input values.
The actual problem is part of a larger grammar, but this example has the same properties as several of the other troublesome rules and is actually one of the grammar rules that is causing me trouble.
I'll start with a minimal example that is almost what I want, and then work from there.
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/home/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <string>
#include <vector>
template<typename OutputIterator_T>
struct Test_Grammar :
boost::spirit::karma::grammar<OutputIterator_T, std::vector<double>()>
{
Test_Grammar() : Test_Grammar::base_type(start), start(), value()
{
namespace karma = boost::spirit::karma;
start
= *(value % karma::lit(", "))
;
value
= (karma::double_)
;
}
boost::spirit::karma::rule<OutputIterator_T, std::vector<double>()> start;
boost::spirit::karma::rule<OutputIterator_T, double()> value;
};
template <typename OutputIterator_T>
bool generate_output(OutputIterator_T& sink, std::vector<double> const& data)
{
Test_Grammar<OutputIterator_T> grammar;
return (boost::spirit::karma::generate(sink, grammar, data));
}
int main (int, char**)
{
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
std::vector<double> data{1.5, 0.0, -2.5,
std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::infinity()};
generate_output(sink, data);
std::cout << generated << std::endl;
return 0;
}
The above code defines a grammar that, when fed with the test data, produces the output
1.5, 0.0, -2.5, nan, inf
However, the output that I want is
1.5, 0.0, -2.5, special, special
If I replace the value
part of the grammar with
value
= (&karma::double_(std::numeric_limits<double>::quiet_NaN()) <<
karma::lit("special"))
| (&karma::double_(std::numeric_limits<double>::infinity()) <<
karma::lit("special"))
| (karma::double_)
;
I get the desired behavior for infinity. However, I do not get the desired result for NaN since NaN has the property that (NaN != NaN) in comparisons. So I need a way to use the fpclassify macros/functions such as isfinite().
I should be able to get what I want by replacing the value
part of the grammar with
value
= (karma::eps(...) << karma::lit("special"))
| (karma::double_)
;
However, every combination of function calls, function pointers, and bind incantations that I've tried for the ...
part has resulted in compiler errors.
Any help would be much appreciated.
UPDATE:
Sehe provided an excellent general solution (which I have accepted). Thank you!
For my particular use case, I was able to further simplify sehe's answer and wanted to document that here for others.
After changing all of the includes from <boost/spirit/home/*>
to <boost/spirit/include/*>
and defining BOOST_SPIRIT_USE_PHOENIX_V3
before those includes, I added the following line
BOOST_PHOENIX_ADAPT_FUNCTION(bool, isfinite_, std::isfinite, 1)
and changed the value
part of the grammar to this
value
%= karma::double_[karma::_pass = isfinite_(karma::_1)]
| karma::lit("special")
;