Skip to content

Latest commit

 

History

History
180 lines (146 loc) · 5.74 KB

259.md

File metadata and controls

180 lines (146 loc) · 5.74 KB
Info

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
}

https://compiler-explorer.com/z/xzTeWEYKc

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());
  };
}

https://godbolt.org/z/bar7zax88

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)>>{});
  }
};

https://godbolt.org/z/jMW75r7nP

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>{});
  }
};

https://godbolt.org/z/96Tzqexr3

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;
};

https://godbolt.org/z/TWrWjTaof