Many hints apply here.
I cannot do a second BOOST_FUSION_ADAPT_STRUCT
Sure you can, see BOOST_FUSION_ADAPT_STRUCT_NAMED
In Qi this general form seem to apply:
[ qi::_val = phxfunction(qi::_0) ]
You could further simplify that by making your own actor type so you could just supply your "factory action": [ factory(&Foo::makeBar) ]
.
If you throw in an implementation of fusion::apply
¹ you can avoid ever dealing with the Fusion sequence manuallt
However, you might want to learn about this - very well hidden - attribute compatibility mode for semantic actions: BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
. Buried in that change log:
Semantic actions now support attribute compatibility. This is a breaking change but #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
must be defined in order for the new behavior to kick in. By default, the old behavior is still in place.
You might get the behaviour you desire with much less tweaking.
X3 is really malleable. We can have that factory
helper described above in as little as:
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
I'll throw in a quick draft of my_apply
(for the boost::fusion::apply
described earlier):
namespace detail {
template <class F, class Sequence, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Sequence&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), boost::fusion::at_c<I>(std::forward<Sequence>(t))...);
}
}
template <class F, class Sequence>
constexpr decltype(auto) my_apply(F&& f, Sequence&& t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Sequence>(t),
std::make_index_sequence<typename boost::fusion::result_of::size<std::remove_reference_t<Sequence> >::type{}>{});
}
Now we can have the parser:
namespace parser {
using namespace x3;
constexpr auto uint8 = uint_parser<std::uint8_t>{};
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
const auto color_rgb = rule<struct rgb, ast::color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> x3::attr(255u) >> ')';
const auto color_rgba = rule<struct rgba, ast::color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
const auto color_hsl = rule<struct hsl, ast::color>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> attr(255u) >> ')') [factory(ast::color::hsl)];
const auto color = skip(space) [ color_rgba | color_rgb | color_hsl ];
}
And test it with:
int main() {
for (std::string const input : {
"rgb(1,2,3)",
"rgba(4,5,6,7)",
"hsl(8,9,10)" })
{
std::cout << " ----- Parsng " << std::quoted(input) << " --------\n";
auto begin = input.begin(), end = input.end();
ast::color result;
bool success = parse(begin, end, parser::color, result);
if (success) {
std::cout << "parsed: ";
std::cout << result << "\n";
} else {
std::cout << "failed\n";
}
if (begin != end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
}
}
Prints:
Live On Coliru
----- Parsng "rgb(1,2,3)" --------
parsed: rgba(1,2,3,255)
----- Parsng "rgba(4,5,6,7)" --------
parsed: rgba(4,5,6,7)
----- Parsng "hsl(8,9,10)" --------
TODO: implement static ast::color ast::color::hsl(uint8_t, uint8_t, uint8_t, uint8_t)(8,9,10,255)
parsed: rgba(8,9,10,255)
Full Listing
Live On Coliru
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/include/size.hpp>
namespace x3 = boost::spirit::x3;
namespace ast {
using std::uint8_t;
struct color {
uint8_t red, green, blue, alpha;
color(uint8_t r=0, uint8_t g=0, uint8_t b=0, uint8_t a=255) : red{r}, green{g}, blue{b}, alpha{a} {}
static color hsl(uint8_t h, uint8_t s, uint8_t l, uint8_t a) {
std::cerr << "TODO: implement " << __PRETTY_FUNCTION__ << "(" << 1*h << "," << 1*s << "," << 1*l << "," << 1*a << ")\n";
return {h,s,l,a}; }
};
static inline std::ostream& operator<<(std::ostream& os, color const& c) {
return os << "rgba(" << 1*c.red << "," << 1*c.green << "," << 1*c.blue << "," << 1*c.alpha << ")";
}
}
BOOST_FUSION_ADAPT_STRUCT(ast::color, red, green, blue, alpha);
namespace {
namespace detail {
template <class F, class Sequence, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Sequence&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), boost::fusion::at_c<I>(std::forward<Sequence>(t))...);
}
}
template <class F, class Sequence>
constexpr decltype(auto) my_apply(F&& f, Sequence&& t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Sequence>(t),
std::make_index_sequence<typename boost::fusion::result_of::size<std::remove_reference_t<Sequence> >::type{}>{});
}
}
namespace parser {
using namespace x3;
constexpr auto uint8 = uint_parser<std::uint8_t>{};
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
const auto color_rgb = rule<struct rgb, ast::color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> x3::attr(255u) >> ')';
const auto color_rgba = rule<struct rgba, ast::color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
const auto color_hsl = rule<struct hsl, ast::color>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> attr(255u) >> ')') [factory(ast::color::hsl)];
const auto color = skip(space) [ color_rgba | color_rgb | color_hsl ];
}
int main() {
for (std::string const input : {
"rgb(1,2,3)",
"rgba(4,5,6,7)",
"hsl(8,9,10)" })
{
std::cout << " ----- Parsng " << std::quoted(input) << " --------\n";
auto begin = input.begin(), end = input.end();
ast::color result;
bool success = parse(begin, end, parser::color, result);
if (success) {
std::cout << "parsed: ";
std::cout << result << "\n";
} else {
std::cout << "failed\n";
}
if (begin != end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
}
}
¹ it seems not to exist. Of course you could copy to a std::tuple
and use std::apply
(also experimental)