1

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:

  • Able to test a.hpp and b.hpp without including both headers in both test-files (a could easily be made to have its dependency injected too)
  • Able to substitute the injected parser with another parser to ease testing

I'm okay if I have to have a bunch of boilerplate every time I inject a different parser.

Justin
  • 24,288
  • 12
  • 92
  • 142

1 Answers1

0

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.

Justin
  • 24,288
  • 12
  • 92
  • 142