Skip to content

Instantly share code, notes, and snippets.

@cleoold
Last active April 24, 2021 14:04
Show Gist options
  • Save cleoold/df31c3789f2b2b8fe2cfa5022957ee06 to your computer and use it in GitHub Desktop.
Save cleoold/df31c3789f2b2b8fe2cfa5022957ee06 to your computer and use it in GitHub Desktop.
detect return, param type of a C++ functor (or lambda)
/* Lambda traits (c) cos Licensed under BSD 2-Clause */
#pragma once
#include <tuple>
namespace detail {
template<typename R, typename ...Args>
struct lambda_traits_base_noconst {
using result_type = R;
using args_type = std::tuple<Args...>;
template<size_t idx> using arg_type_at = std::tuple_element_t<idx, args_type>;
static constexpr size_t arity = sizeof...(Args);
};
template<typename T> struct lambda_traits_base;
template<typename R, typename Cls, typename ...Args>
struct lambda_traits_base<R(Cls::*)(Args...)> : lambda_traits_base_noconst<R, Args...> {
static constexpr bool is_const = false;
// noexcept is part of function signature since C++17
#if __cplusplus >= 201703L
static constexpr bool is_noexcept = false;
#endif
};
template<typename R, typename Cls, typename ...Args>
struct lambda_traits_base<R(Cls::*)(Args...) const> : lambda_traits_base_noconst<R, Args...> {
static constexpr bool is_const = true;
#if __cplusplus >= 201703L
static constexpr bool is_noexcept = false;
#endif
};
#if __cplusplus >= 201703L
template<typename R, typename Cls, typename ...Args>
struct lambda_traits_base<R(Cls::*)(Args...) noexcept> : lambda_traits_base_noconst<R, Args...> {
static constexpr bool is_const = false;
static constexpr bool is_noexcept = true;
};
template<typename R, typename Cls, typename ...Args>
struct lambda_traits_base<R(Cls::*)(Args...) const noexcept> : lambda_traits_base_noconst<R, Args...> {
static constexpr bool is_const = true;
static constexpr bool is_noexcept = true;
};
#endif
template<class T, class ...TArgs>
struct is_lambda_base {
private:
template<class F>
static auto test(decltype(&F::operator())) -> std::true_type;
template<class F, class ...FTArgs>
static auto test(decltype(&F::template operator()<FTArgs...>)) -> std::true_type;
template<class F>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T, TArgs...>(0))::value;
};
template<typename T> struct function_traits_base;
template<typename R, typename ...Args>
struct function_traits_base<R(Args...)> : lambda_traits_base_noconst<R, Args...> {
#if __cplusplus >= 201703L
static constexpr bool is_noexcept = false;
#endif
};
#if __cplusplus >= 201703L
template<typename R, typename ...Args>
struct function_traits_base<R(Args...) noexcept> : lambda_traits_base_noconst<R, Args...> {
static constexpr bool is_noexcept = true;
};
#endif
template<typename T>
struct is_function_base {
private:
template<typename F>
static auto test(typename function_traits_base<F>::result_type *) -> std::true_type;
template<typename F>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template<typename T, typename ...TArgs>
struct is_convertible_to_fp_base {
private:
template<typename F, typename R, typename ...Args>
static auto test0(decltype(static_cast<R(*)(Args...)>(std::declval<F>()))) -> std::true_type;
template<typename F, typename Trait, size_t ...i>
static auto test1(std::index_sequence<i...>)
-> decltype(test0<F, typename Trait::result_type, typename Trait::template arg_type_at<i>...>(0));
template<typename F, typename Trait = lambda_traits_base<decltype(&F::operator())>>
static auto test(long long)
-> decltype(test1<F, Trait>(std::make_index_sequence<Trait::arity>()));
template<typename F, typename ...FTArgs, typename Trait = lambda_traits_base<decltype(&F::template operator()<TArgs...>)>>
static auto test(unsigned long long)
-> decltype(test1<F, Trait>(std::make_index_sequence<Trait::arity>()));
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = is_function_base<std::remove_pointer_t<T>>::value
|| decltype(test<T, TArgs...>(0))::value;
};
}
template<typename...> struct lambda_traits;
template<typename F>
struct lambda_traits<F> : detail::lambda_traits_base<decltype(&F::operator())> {};
template<typename F, typename ...TArgs>
struct lambda_traits<F, TArgs...> : detail::lambda_traits_base<decltype(&F::template operator()<TArgs...>)> {};
template<typename F, typename ...TArgs>
struct is_lambda : detail::is_lambda_base<F, TArgs...> {};
template<typename F>
struct function_traits : detail::function_traits_base<F> {};
template<typename F>
struct is_function : detail::is_function_base<F> {};
template<typename F, typename ...TArgs>
struct is_convertible_to_fp: detail::is_convertible_to_fp_base<F, TArgs...> {};
#include "lambda_traits.hpp"
long func1(float) { return 0; }
template<typename T> void func2(T) {}
int main(void) {
auto lambda1 = [](int x, double y) { return x + y; };
using traits = lambda_traits<decltype(lambda1)>;
static_assert(std::is_same<traits::arg_type_at<0>, int>::value, "monky");
static_assert(std::is_same<traits::arg_type_at<1>, double>::value, "monky");
static_assert(traits::arity == 2, "monky");
static_assert(std::is_same<traits::result_type, double>::value, "monky");
static_assert(traits::is_const, "monky");
// template lambda
auto lambda2 = [](auto, auto) {};
using traits2 = lambda_traits<decltype(lambda2), int, char>;
static_assert(std::is_same<traits2::args_type, std::tuple<int, char>>::value, "monky");
// stateful lambda
auto lambda3 = [c = 0](int) mutable {};
using traits3 = lambda_traits<decltype(lambda3)>;
static_assert(!traits3::is_const, "monky");
// is lambda
static_assert(is_lambda<decltype(lambda1)>::value, "monky");
static_assert(is_lambda<decltype(lambda2), int, char>::value, "monky");
static_assert(is_lambda<decltype(lambda3)>::value, "monky");
static_assert(!is_lambda<decltype(lambda3) *>::value, "monky");
static_assert(!is_lambda<decltype(func1)>::value, "monky");
static_assert(!is_lambda<decltype(func2<int>)>::value, "monky");
// functions
using traits4 = function_traits<decltype(func1)>;
static_assert(std::is_same<traits4::result_type, long>::value, "monky");
using traits5 = function_traits<decltype(func2<int>)>;
static_assert(std::is_same<traits5::arg_type_at<0>, int>::value, "monky");
// is function
static_assert(is_function<decltype(func1)>::value, "monky");
static_assert(is_function<decltype(func2<int>)>::value, "monky");
static_assert(!is_function<decltype(func1) *>::value, "monky"); // function pointer is not function
static_assert(!is_function<decltype(lambda1)>::value, "monky");
// is convertible to fp
static_assert(is_convertible_to_fp<decltype(lambda1)>::value, "monky");
static_assert(is_convertible_to_fp<decltype(lambda2), int, char>::value, "monky");
static_assert(is_convertible_to_fp<decltype(func1)>::value, "monky");
static_assert(is_convertible_to_fp<decltype(func1) *>::value, "monky");
static_assert(!is_convertible_to_fp<decltype(lambda3)>::value, "monky");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment