#include #include #include #include #include namespace { [[maybe_unused]] auto func_no_args() -> void {} [[maybe_unused]] auto func_one_arg(int) -> int { return 0; } [[maybe_unused]] auto func_ref_arg(int &) -> void {} } // namespace TEST_CASE("function return type", "[function_traits]") { STATIC_REQUIRE(std::is_void_v::return_type>); STATIC_REQUIRE(std::is_void_v>); STATIC_REQUIRE( std::is_same_v< typename stdx::function_traits::return_type, int>); STATIC_REQUIRE(std::is_same_v, int>); } TEST_CASE("function pointer return type", "[function_traits]") { STATIC_REQUIRE( std::is_void_v>::return_type>); STATIC_REQUIRE(std::is_void_v< stdx::return_t>>); STATIC_REQUIRE( std::is_same_v>::return_type, int>); STATIC_REQUIRE( std::is_same_v< stdx::return_t>, int>); } TEST_CASE("qualified function pointer return type", "[function_traits]") { STATIC_REQUIRE( std::is_void_v< stdx::return_t const>>); STATIC_REQUIRE(std::is_void_v volatile>>); STATIC_REQUIRE( std::is_void_v const volatile>>); } TEST_CASE("lambda return type", "[function_traits]") { [[maybe_unused]] auto const x = []() {}; [[maybe_unused]] auto const y = []() mutable {}; STATIC_REQUIRE(std::is_void_v>); STATIC_REQUIRE(std::is_void_v>); } TEST_CASE("function args", "[function_traits]") { STATIC_REQUIRE( std::is_same_v::template args, std::tuple<>>); STATIC_REQUIRE( std::is_same_v, std::tuple<>>); STATIC_REQUIRE( std::is_same_v::template args, std::tuple>); STATIC_REQUIRE( std::is_same_v, std::tuple>); } TEST_CASE("function decayed args", "[function_traits]") { STATIC_REQUIRE( std::is_same_v:: template decayed_args, std::tuple>); STATIC_REQUIRE( std::is_same_v, std::tuple>); } TEST_CASE("function nth arg", "[function_traits]") { STATIC_REQUIRE( std::is_same_v::template nth_arg<0>, int>); STATIC_REQUIRE( std::is_same_v, int>); } TEST_CASE("function decayed nth arg", "[function_traits]") { STATIC_REQUIRE( std::is_same_v::template decayed_nth_arg<0>, int>); STATIC_REQUIRE( std::is_same_v, int>); } TEST_CASE("lambda args", "[function_traits]") { [[maybe_unused]] auto const x = [](int) {}; [[maybe_unused]] auto const y = [](int) mutable {}; STATIC_REQUIRE(std::is_same_v::template args, std::tuple>); STATIC_REQUIRE( std::is_same_v, std::tuple>); STATIC_REQUIRE(std::is_same_v::template args, std::tuple>); STATIC_REQUIRE( std::is_same_v, std::tuple>); } TEST_CASE("lambda decayed args", "[function_traits]") { [[maybe_unused]] auto const x = [](int &) {}; [[maybe_unused]] auto const y = [](int &) mutable {}; STATIC_REQUIRE( std::is_same_v::template decayed_args, std::tuple>); STATIC_REQUIRE(std::is_same_v, std::tuple>); STATIC_REQUIRE( std::is_same_v::template decayed_args, std::tuple>); STATIC_REQUIRE(std::is_same_v, std::tuple>); } TEST_CASE("lambda nth arg", "[function_traits]") { [[maybe_unused]] auto const x = [](int) {}; STATIC_REQUIRE( std::is_same_v< typename stdx::function_traits::template nth_arg<0>, int>); STATIC_REQUIRE(std::is_same_v, int>); } TEST_CASE("lambda decayed nth arg", "[function_traits]") { [[maybe_unused]] auto const x = [](int &) {}; STATIC_REQUIRE(std::is_same_v::template decayed_nth_arg<0>, int>); STATIC_REQUIRE( std::is_same_v, int>); } TEST_CASE("function arity", "[function_traits]") { STATIC_REQUIRE( stdx::function_traits::arity::value == 0u); STATIC_REQUIRE( stdx::function_traits::arity::value == 1u); STATIC_REQUIRE(stdx::arity_t::value == 0u); STATIC_REQUIRE(stdx::arity_t::value == 1u); STATIC_REQUIRE(stdx::arity_v == 0u); STATIC_REQUIRE(stdx::arity_v == 1u); } TEST_CASE("lambda arity", "[function_traits]") { [[maybe_unused]] auto const x = []() {}; [[maybe_unused]] auto const y = [](int) {}; STATIC_REQUIRE(stdx::function_traits::arity::value == 0u); STATIC_REQUIRE(stdx::function_traits::arity::value == 1u); STATIC_REQUIRE(stdx::arity_t::value == 0u); STATIC_REQUIRE(stdx::arity_t::value == 1u); STATIC_REQUIRE(stdx::arity_v == 0u); STATIC_REQUIRE(stdx::arity_v == 1u); } TEST_CASE("generic lambda arity", "[function_traits]") { [[maybe_unused]] auto const x = [](auto) {}; [[maybe_unused]] auto const y = [](auto, auto) {}; STATIC_REQUIRE(stdx::arity_t::value == 1u); STATIC_REQUIRE(stdx::arity_t::value == 2u); STATIC_REQUIRE(stdx::arity_v == 1u); STATIC_REQUIRE(stdx::arity_v == 2u); } namespace { int called_1{}; int called_2{}; template constexpr auto call_f(F f, stdx::priority_t<1>) -> stdx::return_t { ++called_1; return f(); } template constexpr auto call_f(F f, stdx::priority_t<0>) -> void { ++called_2; f(0); } } // namespace TEST_CASE("SFINAE friendly", "[function_traits]") { called_1 = 0; called_2 = 0; auto f1 = []() -> int { return 1; }; auto f2 = [](auto) -> void {}; call_f(f1, stdx::priority<1>); CHECK(called_1 == 1); CHECK(called_2 == 0); call_f(f2, stdx::priority<1>); CHECK(called_2 == 1); } namespace { struct S { [[maybe_unused]] auto f() -> void {} }; struct S_C { [[maybe_unused]] auto f() const -> void {} }; struct S_V { [[maybe_unused]] auto f() volatile -> void {} }; struct S_CV { [[maybe_unused]] auto f() const volatile -> void {} }; struct S_LV { [[maybe_unused]] auto f() & -> void {} }; struct S_RV { [[maybe_unused]] auto f() && -> void {} }; struct S_CLV { [[maybe_unused]] auto f() const & -> void {} }; struct S_CRV { [[maybe_unused]] auto f() const && -> void {} }; struct S_VLV { [[maybe_unused]] auto f() volatile & -> void {} }; struct S_VRV { [[maybe_unused]] auto f() volatile && -> void {} }; struct S_CVLV { [[maybe_unused]] auto f() const volatile & -> void {} }; struct S_CVRV { [[maybe_unused]] auto f() const volatile && -> void {} }; } // namespace TEST_CASE("member function return type", "[function_traits]") { STATIC_REQUIRE(std::is_void_v>); STATIC_REQUIRE(std::is_void_v>); STATIC_REQUIRE(std::is_void_v>); STATIC_REQUIRE(std::is_void_v>); } TEST_CASE("member function arity", "[function_traits]") { STATIC_REQUIRE(stdx::arity_v == 0); STATIC_REQUIRE(stdx::arity_v == 0); STATIC_REQUIRE(stdx::arity_v == 0); STATIC_REQUIRE(stdx::arity_v == 0); } TEST_CASE("object argument types (const/volatile)", "[function_traits]") { STATIC_REQUIRE(std::is_void_v>); STATIC_REQUIRE(std::is_same_v>); STATIC_REQUIRE( std::is_same_v>); STATIC_REQUIRE( std::is_same_v>); STATIC_REQUIRE(std::is_same_v>); } TEST_CASE("object argument types (value categories)", "[function_traits]") { STATIC_REQUIRE(std::is_same_v>); STATIC_REQUIRE( std::is_same_v>); STATIC_REQUIRE( std::is_same_v>); STATIC_REQUIRE( std::is_same_v>); STATIC_REQUIRE( std::is_same_v>); STATIC_REQUIRE(std::is_same_v>); STATIC_REQUIRE(std::is_same_v>); STATIC_REQUIRE(std::is_same_v>); } TEST_CASE("object argument type (lambda)", "[function_traits]") { [[maybe_unused]] auto const x = [](int) {}; STATIC_REQUIRE(std::is_same_v>); } namespace { struct TS { template auto operator()(T) -> void {} }; struct TS_C { template auto operator()(T) const -> void {} }; struct TS_V { template auto operator()(T) volatile -> void {} }; struct TS_CV { template auto operator()(T) const volatile -> void {} }; struct TS_LV { template auto operator()(T) & -> void {} }; struct TS_RV { template auto operator()(T) && -> void {} }; struct TS_CLV { template auto operator()(T) const & -> void {} }; struct TS_CRV { template auto operator()(T) const && -> void {} }; struct TS_VLV { template auto operator()(T) volatile & -> void {} }; struct TS_VRV { template auto operator()(T) volatile && -> void {} }; struct TS_CVLV { template auto operator()(T) const volatile & -> void {} }; struct TS_CVRV { template auto operator()(T) const volatile && -> void {} }; } // namespace TEST_CASE("member function template arity", "[function_traits]") { STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); STATIC_REQUIRE(stdx::arity_v == 1); }