I am writing player command handler for some game server. Handler is a some function which accepts arbitrary combination of arguments of allowed types. Allowed types are double
, int
, std::string
, bool
. I add new command handler dynamically using method template<typename F> void addCommand(string name, F handler);
. But I need to restrict template type F
to be:
I check that F
is a function using following concept:
// Helper concept to check if a type is one of the allowed types
template <typename T, typename... AllowedTypes>
concept IsAllowedType = (std::is_same_v<AllowedTypes, T> || ...);
// Concept to check if a function satisfies the requirements
template <typename F, typename... AllowedArgTypes>
concept IsFunctionAndAcceptsAllowedTypes = requires {
std::invocable<F>;
// TODO check arg types of F
};
So, the question is, how to infer argument types of provided type F
and check them against list of allowed types? I know I can use function_traits
helper:
template <typename T>
struct function_traits;
template <typename R, typename... Args>
struct function_traits<std::function<R(Args...)>>
{
static const size_t nargs = sizeof...(Args);
typedef R result_type;
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
};
But I can't get my head around this: how to iterate over tuple types to check them in context of templates?
I think this is only possible by defining a helper that has an own requires call:
#include <functional>
#include <type_traits>
template <typename T, typename... AllowedTypes>
concept allowed_type = (std::is_same_v<AllowedTypes, T> || ...);
template <class... Allowed> struct helper {
template <class R, allowed_type<Allowed...>... Args>
auto operator()(std::function<R(Args...)> t) -> void;
};
template <class T, class... Allowed>
concept function = requires(T t, helper<Allowed...> h) {
{ h(t) } -> std::same_as<void>;
};
int allowed(int, int, char);
void disallowed(int, long, char);
int main() {
function<int, char> auto foo = std::function{allowed};
// The following fails
function<int, char> auto bar = std::function{disallowed};
}