Sometimes I have tight coupling / circular dependencies between parsers. I might have something like this:
parser.hpp
#pragma once
namespace parser {
using a_type = x3::rule<class a_class>;
a_type const a = "a";
using b_type = x3::rule<class b_class>;
b_type const b = "b";
auto const a_def = "(" >> b >> ")";
auto const b_def = "<" >> a >> ">";
BOOST_SPIRIT_DEFINE(a, b);
}
However, I want to separate these into different headers, as that will make for smaller compilation times when I unit test and for having multiple header files (rather than one monolithic file). In reality, I have much more than just 2 parsers.
I want to be able to do something like this:
a.hpp
#pragma once
#include "b.hpp"
namespace parser {
using a_type = x3::rule<class a_class>;
a_type const a = "a";
auto const a_def = "(" >> b_wrapper<a>::b >> ")";
BOOST_SPIRIT_DEFINE(a);
}
b.hpp
#pragma once
namespace parser {
template <auto Injected>
struct b_wrapper {
using b_type = x3::rule<class b_class>;
static b_type const b = "b";
static auto const b_def = "(" >> Injected >> ")";
BOOST_SPIRIT_DEFINE(b);
};
}
Naturally, this doesn't work. How can I achieve dependency injection with the parsers?
I don't care about the exact syntax for the injection. However, I really need these points:
a
could easily be made to have its dependency injected too)I'm okay if I have to have a bunch of boilerplate every time I inject a different parser.
You can produce your foo_def
objects via functions. That is, b.hpp would look like this:
#pragma once
namespace parser {
template <typename Injected>
auto b_impl(Injected const& injected) {
return "(" >> injected >> ")";
}
}
And where you want to inject the actual parser, you can do:
using b_type = x3::rule<class b_class>;
b_type const b = "b";
auto const b_def = b_impl(a);
BOOST_SPIRIT_DEFINE(b);
Note that there is still that bit of boilerplate when you want to create the parser.