Info
-
Did you know that concept can be passed via lambda expression?
Example
static_assert([]<class T>{ return std::integral<T>; }.operator()<int>());
struct f { auto foo() -> void; };
static_assert([](auto t){ return requires { t.foo(); }; }(f{}));
Puzzle
- Can you implement concept
fooable
which is satisfied if its parameters satisfies given concepts?
template<class T, auto... Ts>
concept fooable; // TODO
struct bar { };
static_assert(not fooable<bar>);
struct foo1 { void foo(int); };
static_assert(fooable<foo1, []<class T> { return std::integral<T>; }>);
struct foo2 { void foo(int, short); };
static_assert(fooable<foo2, []<class T> { return std::integral<T>; },
[]<class T> { return std::same_as<short, T>; }>);
Solutions
template <class T, auto... ConceptCheckers>
concept fooable = requires(T t) { &T::foo; };
namespace detail {
template <typename...>
struct type_list {};
template <auto...>
struct value_list {};
template <typename T>
concept has_foo = requires(T t) { &T::foo; };
template <typename>
struct foo_function_traits;
template <typename TBase, typename TReturn, typename... TArgs>
struct foo_function_traits<TReturn (TBase::*)(TArgs...)> {
using arg_types = type_list<TArgs...>;
};
template <typename... TArgs, auto... ConceptCheckers>
[[nodiscard]] consteval auto check_foo_args(type_list<TArgs...>,
value_list<ConceptCheckers...>) {
if constexpr (sizeof...(TArgs) != sizeof...(ConceptCheckers)) {
return false;
} else {
return (... and ConceptCheckers.template operator()<TArgs>());
}
}
} // namespace detail
template <class T, auto... ConceptCheckers>
concept fooable =
detail::has_foo<T> and
detail::check_foo_args(
typename detail::foo_function_traits<decltype(&T::foo)>::arg_types{},
detail::value_list<ConceptCheckers...>{});
template <auto Constraint>
struct ArgConstraint {
template <typename T>
requires (Constraint.template operator()<T>())
operator T();
};
template<class T, auto... Ts>
concept fooable = requires (T x) { x.foo(ArgConstraint<Ts>{}...); }
template<typename T, typename R, typename ... Args>
consteval auto get_arguments(R(T::*)(Args...))
{
return std::tuple<Args...>();
}
template <class ArgumentTuple, class FunctionTuple>
consteval decltype(auto) for_each(ArgumentTuple && tuple, FunctionTuple && f)
{
if constexpr (std::tuple_size_v<ArgumentTuple> != std::tuple_size_v<FunctionTuple>)
return false;
return [] <std::size_t... I>
(ArgumentTuple && tuple, FunctionTuple&& f, std::index_sequence<I...>)
{
return (std::get<I>(f).template operator()<std::remove_reference_t<decltype(std::get<I>(tuple))>>() && ...);
}
(std::forward<ArgumentTuple>(tuple), std::forward<FunctionTuple>(f),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<ArgumentTuple>>>{});
}
template<class T, auto... Ts>
concept fooable = for_each(get_arguments(&T::foo), std::tuple(Ts...));
template <class T, class C>
using satifies = boost::mp11::mp_bool<C{}.template operator()<T>()>;
template <class T>
struct indexed_arg {
template <class N>
using fn = typename boost::mpl::at<T, N>::type;
};
template <class F>
using arg_list = boost::mp11::mp_pop_front<boost::mp11::mp_transform_q<
indexed_arg<boost::function_types::parameter_types<F>>,
boost::mp11::mp_iota_c<boost::function_types::function_arity<F>::value>>>;
template <class T, auto... Ts>
concept fooable = boost::mp11::mp_apply<boost::mp11::mp_all,
boost::mp11::mp_transform<
satifies,
arg_list<decltype(&T::foo)>,
boost::mp11::mp_list<decltype(Ts)...>>>::value;
template<class...> struct type_list{};
template<class> struct function_traits;
template<class R, class B, class... Ts>
struct function_traits<R(B::*)(Ts...)> {
using args_t = type_list<Ts...>;
};
template<class T, auto... Ts>
concept fooable = []<class... TArgs>(type_list<TArgs...>) {
return (decltype(Ts){}.template operator()<TArgs>() and ...);
}(typename function_traits<decltype(&T::foo)>::args_t{});
template <class T, auto... Vs>
concept fooable = []<class R, class... Args>(R (T::*)(Args...), auto... tests) {
return (... and tests.template operator()<Args>());
}(&T::foo, Vs...);
template<auto Validate>
struct AnyType {
template<class T>
operator T() {
static_assert( Validate.template operator()<T>() );
return T{}; }
};
template<class T, auto... Ts>
concept fooable = requires(T v ) {
v.foo(AnyType<Ts>{} ...);
};
template <auto Constraint> struct satisfies {
template <class T> requires (Constraint.template operator()<T>()) operator T();
};
template<class T, auto... Ts>
concept fooable = requires (T t) { t.foo(satisfies<Ts>{}...); };