Last active
April 24, 2021 14:04
-
-
Save cleoold/df31c3789f2b2b8fe2cfa5022957ee06 to your computer and use it in GitHub Desktop.
detect return, param type of a C++ functor (or lambda)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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...> {}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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