Info
-
Did you know that with C++20 you can pass concepts?
- Although
template<template<class...> concept C>
is not valid - https://eel.is/c++draft/#concepts
- Although
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
}
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));
}
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));
}
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{};
}
}
template<class T>
constexpr auto create(auto&& injector) {
return [&]<class... Ts>(::injector<Ts...>) {
return T{ (Ts{}, []<class _>(bind<Ts{}, _>) { return _{}; }(injector))... };
}(T::ctor_traits);
}