Info
-
Did you know that static reflection supports introspecting constructors?
Example
struct foo {
foo(int i) { }
};
auto main() -> int {
namespace meta = std::experimental::reflect;
std::cout << meta::get_size_v<meta::get_constructors_t<reflexpr(foo)>>; // prints 1
std::cout << meta::get_name_v<meta::get_element_t<0, meta::get_constructors_t<reflexpr(foo)>>>; // prints foo
std::cout << meta::get_name_v<meta::get_element_t<0, meta::get_parameters_t<meta::get_element_t<0, meta::get_constructors_t<reflexpr(foo)>>>>>; // prints i
}
Puzzle
- Can you implement a simple interface dependency injection framework which injects interface accodringly to given wiring?
template <class... TWirings>
struct injector; // TODO
struct interface {
virtual ~interface() noexcept = default;
virtual auto get() const -> int = 0;
};
template<auto N>
struct implementation : interface {
auto get() const -> int override { return N; }
};
int main() {
using namespace boost::ut;
"dependency injection"_test = [] {
struct dependency_injection {
explicit(true) dependency_injection(const interface& i1) : i1{i1} { }
auto run() { return i1.get(); }
private:
const interface& i1;
};
const auto wiring = injector{
bind<interface, implementation<42>>(),
};
auto di = wiring.create<dependency_injection>();
expect(42_i == di.run());
};
}
Solutions
template <class... TWirings>
struct injector : TWirings... {
explicit(true) constexpr injector(TWirings...) {}
template <class T>
constexpr auto create() const {
return [this]<class... Ts>(std::tuple<Ts...>) {
const auto wiring = [this]<class TName, class TParam>(
TName, TParam) -> decltype(auto) {
constexpr auto has_type_wiring = []<class TWiring>(TWiring) {
return std::is_same_v<std::remove_cvref_t<meta::get_reflected_type_t<
meta::get_type_t<TParam>>>,
typename TWiring::if_t>;
};
constexpr auto has_named_wiring = []<class TWiring>(TWiring) {
return std::string_view{TWiring::name}.substr(
0, std::size(std::string_view{meta::get_name_v<TName>})) ==
meta::get_name_v<TName> and
std::string_view{TWiring::name}.substr(
std::size(std::string_view{meta::get_name_v<TName>}) +
1) == meta::get_name_v<TParam>;
};
static_assert(
((has_type_wiring(TWirings{}) or has_named_wiring(TWirings{})) or
...),
"Missing wiring!");
static std::shared_ptr<std::remove_cvref_t<
meta::get_reflected_type_t<meta::get_type_t<TParam>>>>
param{};
(
[&] {
if constexpr (has_named_wiring(TWirings{})) {
param = std::make_shared<typename TWirings::impl_t>();
} else if constexpr (has_type_wiring(TWirings{})) {
param = std::make_shared<typename TWirings::impl_t>();
}
}(),
...);
return *param;
};
return (
[&]<class TName, class... TParams>(TName, std::tuple<TParams...>) {
return T{wiring(TName{}, TParams{})...};
}(Ts{},
meta::unpack_sequence_t<std::tuple, meta::get_parameters_t<Ts>>{}),
...);
}
(meta::unpack_sequence_t<std::tuple,
meta::get_constructors_t<reflexpr(T)>>{});
}
};
template <class... TWirings>
struct injector : TWirings... {
constexpr injector(TWirings...) {}
template <class T>
auto create() const {
using constructors_t = meta::get_constructors_t<reflexpr(T)>;
using constructor_t = meta::get_element_t<0, constructors_t>;
using parameters_t = meta::get_parameters_t<constructor_t>;
return [&] <class... Params> (std::tuple<Params...>) {
return T([&] <class Param> {
using if_t = std::remove_cvref_t<meta::get_reflected_type_t<meta::get_type_t<Param>>>;
return [] <auto Name, class TImpl> (const detail::bind<Name, if_t, TImpl> *) {
return TImpl{};
}(this);
}.template operator()<Params>()...);
}(meta::unpack_sequence_t<std::tuple, parameters_t>{});
}
};
template <class... TWirings>
struct injector : TWirings... {
constexpr explicit(true) injector(TWirings...) noexcept {};
template <class T>
constexpr auto create() const {
return [] <class TFirstConstructor, class... TConstructors> (std::tuple<TFirstConstructor, TConstructors...>) {
const auto implementation_for = [] <class TInterface> (TInterface) {
using interface_t = std::remove_cvref_t<meta::get_reflected_type_t<meta::get_type_t<TInterface>>>;
using impl_t = decltype(lookup_impl<interface_t>(std::declval<injector>()));
return impl_t{};
};
const auto make_T = [&] <class... TParams> (std::tuple<TParams...>) {
return T{implementation_for(TParams{})...};
};
return make_T(meta::unpack_sequence_t<std::tuple, meta::get_parameters_t<TFirstConstructor>>{});
}(meta::unpack_sequence_t<std::tuple, meta::get_constructors_t<reflexpr(T)>>{});
}
private:
template <class TInterface, auto TName, class TImpl>
constexpr static auto lookup_impl(const detail::bind<TName, TInterface, TImpl> &) -> TImpl;
};