Skip to content

Latest commit

 

History

History
147 lines (114 loc) · 3.57 KB

317.md

File metadata and controls

147 lines (114 loc) · 3.57 KB
Info

Example

template<class T>
concept foo_like = requires(T t) { t.foo; };

template<auto Concept>
struct foo {
  auto fn(auto f) {
    static_assert(requires { Concept(f); });
  }
};

int main() {
  foo<[](foo_like auto){}> f{};

  struct { int foo{}; } foo;
  struct { } bar;

  f.fn(foo); // ok
  f.fn(bar); // error: contrain not satisfied
}

https://godbolt.org/z/dE9nWdETs

Puzzle

Can you implement simple dependency injection framework based on concepts?

template<class T>
constexpr auto create(auto&& injector); // TODO

template<class T> concept foo_like = requires(T t) { t.f; };
template<class T> concept bar_like = requires(T t) { t.b; };

auto foo_like_v = [](foo_like auto){};
auto bar_like_v = [](bar_like auto){};

struct foo { int f{}; };
struct bar { int b{}; };

struct app {
  static constexpr auto ctor_traits = injector{foo_like_v, bar_like_v}; // reflection
  app(foo_like auto f, bar_like auto b) { }
};

int main() {
  auto i = injector{
    bind<bar_like_v, bar>{},
    bind<foo_like_v, foo>{},
  };

  auto a = create<app>(i);
  static_assert(sizeof(a));
}

https://godbolt.org/z/8T9P174M5

Solutions

template <typename Base, typename Tuple, std::size_t I = 0>
struct tuple_ref_index;

template <typename Base, typename Head, typename... Tail, std::size_t I>
struct tuple_ref_index<Base, std::tuple<Head, Tail...>, I>
   : std::conditional<std::is_base_of<Base, Head>::value
                    , std::integral_constant<std::size_t, I>
                    , tuple_ref_index<Base, std::tuple<Tail...>, I+1>
                    >::type
{
};

template <typename Base, typename Tuple>
auto tuple_ref_by_inheritance(Tuple&& tuple)
   -> decltype(std::get<tuple_ref_index<Base, typename std::decay<Tuple>::type>::value>(std::forward<Tuple>(tuple)))
{
   return std::get<tuple_ref_index<Base, typename std::decay<Tuple>::type>::value>(std::forward<Tuple>(tuple));
}

template<typename Bind>
auto get_bind_type(Bind)
{
   return typename Bind::type{};
}

template<typename...BindTypes, typename...CtorTraits>
auto get_bind_types(std::tuple<BindTypes...> bts, std::tuple<CtorTraits...>)
{
   return std::tuple{get_bind_type(tuple_ref_by_inheritance<CtorTraits>(bts))...};
}

template<typename...Ts>
auto get_types_as_tuple(injector<Ts...>)
{
   return std::tuple<Ts...>{};
}

template<class T>
constexpr auto create(auto&& injector)
{
   auto bind_types = get_types_as_tuple(injector);
   auto ctor_traits_types = get_types_as_tuple(T::ctor_traits);

   return std::make_from_tuple<T>(get_bind_types(bind_types, ctor_traits_types));
}

https://godbolt.org/z/zoYjGs3xE

template <class T>
constexpr auto create(auto injector) {
  if constexpr (requires { T::ctor_traits; }) {
    return [=]<class... Ts>(::injector<Ts...>) {
      return T{[=]<auto C, class U>(bind<C, U>) {
        return create<U>(injector);
      }.template operator()<Ts{}>(injector)...};
    }(T::ctor_traits);
  } else {
    return T{};
  }
}

https://godbolt.org/z/beTnhh31r

template<class T>
constexpr auto create(auto&& injector) {
    return [&]<class... Ts>(::injector<Ts...>) {
        return T{ (Ts{}, []<class _>(bind<Ts{}, _>) { return _{}; }(injector))... };
    }(T::ctor_traits);
}

https://godbolt.org/z/Yq7odx86e