From d0ebdb239ca8830c8aa67f3ac1e0998190f1410d Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Wed, 14 Oct 2015 12:25:31 -0700 Subject: [PATCH] Added check_argument_types to simplify checking for a functor's arity and the types of its arguments --- .../rclcpp/any_subscription_callback.hpp | 66 +++---- rclcpp/include/rclcpp/function_traits.hpp | 34 +++- rclcpp/include/rclcpp/node.hpp | 71 ++------ rclcpp/test/test_function_traits.cpp | 168 ++++++++++++++---- 4 files changed, 201 insertions(+), 138 deletions(-) diff --git a/rclcpp/include/rclcpp/any_subscription_callback.hpp b/rclcpp/include/rclcpp/any_subscription_callback.hpp index 6949b7e368..ba4c51c520 100644 --- a/rclcpp/include/rclcpp/any_subscription_callback.hpp +++ b/rclcpp/include/rclcpp/any_subscription_callback.hpp @@ -59,80 +59,58 @@ struct AnySubscriptionCallback template< typename CallbackT, - std::size_t Arity = 1 - > - typename std::enable_if::value, void>::type - set( - CallbackT callback, typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<0>, + rclcpp::check_argument_types< + CallbackT, typename std::shared_ptr >::value - >::type * = nullptr) + >::type * = nullptr + > + void set(CallbackT callback) { shared_ptr_callback = callback; } template< typename CallbackT, - std::size_t Arity = 2 - > - typename std::enable_if::value, void>::type - set( - CallbackT callback, typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<0>, - typename std::shared_ptr - >::value - >::type * = nullptr, - typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<1>, + rclcpp::check_argument_types< + CallbackT, + typename std::shared_ptr, const rmw_message_info_t & >::value - >::type * = nullptr) + >::type * = nullptr + > + void set(CallbackT callback) { shared_ptr_with_info_callback = callback; } template< typename CallbackT, - std::size_t Arity = 1 - > - typename std::enable_if::value, void>::type - set( - CallbackT callback, typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<0>, + rclcpp::check_argument_types< + CallbackT, typename std::shared_ptr >::value - >::type * = nullptr) + >::type * = nullptr + > + void set(CallbackT callback) { const_shared_ptr_callback = callback; } template< typename CallbackT, - std::size_t Arity = 2 - > - typename std::enable_if::value, void>::type - set( - CallbackT callback, typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<0>, - typename std::shared_ptr - >::value - >::type * = nullptr, - typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<1>, + rclcpp::check_argument_types< + CallbackT, + typename std::shared_ptr, const rmw_message_info_t & >::value - >::type * = nullptr) + >::type * = nullptr + > + void set(CallbackT callback) { const_shared_ptr_with_info_callback = callback; } diff --git a/rclcpp/include/rclcpp/function_traits.hpp b/rclcpp/include/rclcpp/function_traits.hpp index 7c64787754..4b1948b748 100644 --- a/rclcpp/include/rclcpp/function_traits.hpp +++ b/rclcpp/include/rclcpp/function_traits.hpp @@ -64,10 +64,36 @@ struct function_traits * the arity of a function. */ template -struct arity_comparator -{ - static constexpr bool value = (Arity == function_traits::arity); -}; +struct arity_comparator : std::integral_constant< + bool, (Arity == function_traits::arity)>{}; + +template +struct check_argument_types_recursive : std::conditional< + std::is_same< + typename function_traits::template argument_type< + function_traits::arity - sizeof ... (Last) -1 + >, + First + >::value, + check_argument_types_recursive, + std::false_type>::type +{}; + +template +struct check_argument_types_recursive: std::is_same< + typename function_traits::template argument_type< + function_traits::arity - 1 + >, + Arg + > +{}; + +template +struct check_argument_types : std::conditional< + arity_comparator::value, + check_argument_types_recursive, + std::false_type>::type +{}; } /* namespace rclcpp */ diff --git a/rclcpp/include/rclcpp/node.hpp b/rclcpp/include/rclcpp/node.hpp index 50c1b6c63d..816d2630b8 100644 --- a/rclcpp/include/rclcpp/node.hpp +++ b/rclcpp/include/rclcpp/node.hpp @@ -283,42 +283,23 @@ class Node bool ignore_local_publications, typename message_memory_strategy::MessageMemoryStrategy::SharedPtr msg_mem_strat); -/* NOTE(esteve): - * The following template machinery works around VS2015's lack of support for expression SFINAE: - * - We first declare the arity we want to match, i.e. 2 or 3. - * - Then we use the arity_comparator template to SFINAE on the arity of the passed functor. - * - Lastly, we SFINAE on the types of the arguments of the functor. - * These steps happen in different parts of the function signature because we want to stagger - * instantation of the templates because VS2015 can't conditionally enable templates that depend - * on another template. - * See test_function_traits.cpp for streamlined examples of how to use this pattern. - */ template< typename ServiceT, typename FunctorT, - std::size_t Arity = 2 + typename std::enable_if< + rclcpp::check_argument_types< + FunctorT, + typename std::shared_ptr, + typename std::shared_ptr + >::value + >::type * = nullptr > - typename std::enable_if< - rclcpp::arity_comparator::value, - typename rclcpp::service::Service::SharedPtr - >::type + typename rclcpp::service::Service::SharedPtr create_service_internal( std::shared_ptr node_handle, rmw_service_t * service_handle, const std::string & service_name, - FunctorT callback, - typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<0>, - typename std::shared_ptr - >::value - >::type * = nullptr, - typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<1>, - typename std::shared_ptr - >::value - >::type * = nullptr) + FunctorT callback) { typename rclcpp::service::Service::CallbackType callback_without_header = callback; @@ -329,35 +310,21 @@ class Node template< typename ServiceT, typename FunctorT, - std::size_t Arity = 3 + typename std::enable_if< + rclcpp::check_argument_types< + FunctorT, + std::shared_ptr, + typename std::shared_ptr, + typename std::shared_ptr + >::value + >::type * = nullptr > - typename std::enable_if< - arity_comparator::value, - typename rclcpp::service::Service::SharedPtr - >::type + typename rclcpp::service::Service::SharedPtr create_service_internal( std::shared_ptr node_handle, rmw_service_t * service_handle, const std::string & service_name, - FunctorT callback, - typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<0>, - std::shared_ptr - >::value - >::type * = nullptr, - typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<1>, - typename std::shared_ptr - >::value - >::type * = nullptr, - typename std::enable_if< - std::is_same< - typename function_traits::template argument_type<2>, - typename std::shared_ptr - >::value - >::type * = nullptr) + FunctorT callback) { typename rclcpp::service::Service::CallbackWithHeaderType callback_with_header = callback; diff --git a/rclcpp/test/test_function_traits.cpp b/rclcpp/test/test_function_traits.cpp index 57eb63109c..fb07769dcd 100644 --- a/rclcpp/test/test_function_traits.cpp +++ b/rclcpp/test/test_function_traits.cpp @@ -70,17 +70,21 @@ struct FunctionObjectOneIntOneChar template< typename FunctorT, - std::size_t Arity = 1 + std::size_t Arity = 0, + typename std::enable_if::value>::type * = nullptr > -typename std::enable_if::value, int>::type -func_accept_callback(FunctorT callback, +int func_accept_callback(FunctorT callback) +{ + return callback(); +} + +template< + typename FunctorT, typename std::enable_if< - std::is_same< - int, - typename rclcpp::function_traits::template argument_type<0> - >::value + rclcpp::check_argument_types::value >::type * = nullptr -) +> +int func_accept_callback(FunctorT callback) { int a = 4; return callback(a); @@ -88,22 +92,11 @@ func_accept_callback(FunctorT callback, template< typename FunctorT, - std::size_t Arity = 2 -> -typename std::enable_if::value, int>::type -func_accept_callback(FunctorT callback, - typename std::enable_if< - std::is_same< - int, - typename rclcpp::function_traits::template argument_type<0> - >::value - >::type * = nullptr, typename std::enable_if< - std::is_same< - int, - typename rclcpp::function_traits::template argument_type<1> - >::value - >::type * = nullptr) + rclcpp::check_argument_types::value + >::type * = nullptr +> +int func_accept_callback(FunctorT callback) { int a = 5; int b = 6; @@ -112,22 +105,11 @@ func_accept_callback(FunctorT callback, template< typename FunctorT, - std::size_t Arity = 2 -> -typename std::enable_if::value, int>::type -func_accept_callback(FunctorT callback, - typename std::enable_if< - std::is_same< - int, - typename rclcpp::function_traits::template argument_type<0> - >::value - >::type * = nullptr, typename std::enable_if< - std::is_same< - char, - typename rclcpp::function_traits::template argument_type<1> - >::value - >::type * = nullptr) + rclcpp::check_argument_types::value + >::type * = nullptr +> +int func_accept_callback(FunctorT callback) { int a = 7; char b = 8; @@ -316,13 +298,123 @@ TEST(TestFunctionTraits, argument_types) { >::value, "Functor accepts a char as second argument"); } +/* + Tests that funcion_traits checks the types of the arguments of several functors. + */ +TEST(TestFunctionTraits, check_argument_types) { + // Test regular functions + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts a single int as arguments"); + + static_assert( + !rclcpp::check_argument_types::value, + "Functor does not accept a char as argument"); + + static_assert( + !rclcpp::check_argument_types::value, + "Functor does not accept two arguments"); + + static_assert( + !rclcpp::check_argument_types::value, + "Functor accepts two ints as arguments"); + + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts two ints as arguments"); + + static_assert( + !rclcpp::check_argument_types::value, + "Functor accepts two ints as arguments"); + + static_assert( + !rclcpp::check_argument_types::value, + "Functor accepts two ints as arguments"); + + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts an int and a char as arguments"); + + // Test lambdas + auto lambda_one_int = [](int) { + return 1; + }; + + auto lambda_two_ints = [](int, int) { + return 2; + }; + + auto lambda_one_int_one_char = [](int, char) { + return 3; + }; + + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts an int as the only argument"); + + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts two ints as arguments"); + + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts an int and a char as arguments"); + + // Test objects that have a call operator + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts an int as the only argument"); + + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts two ints as arguments"); + + static_assert( + rclcpp::check_argument_types::value, + "Functor accepts an int and a char as arguments"); +} + /* Tests that functions are matched via SFINAE. */ TEST(TestFunctionTraits, sfinae_match) { + EXPECT_EQ(0, func_accept_callback(func_no_args)); + EXPECT_EQ(1, func_accept_callback(func_one_int)); EXPECT_EQ(2, func_accept_callback(func_two_ints)); EXPECT_EQ(3, func_accept_callback(func_one_int_one_char)); + + auto lambda_no_args = []() { + return 0; + }; + + auto lambda_one_int = [](int) { + return 1; + }; + + auto lambda_two_ints = [](int, int) { + return 2; + }; + + auto lambda_one_int_one_char = [](int, char) { + return 3; + }; + + EXPECT_EQ(0, func_accept_callback(lambda_no_args)); + + EXPECT_EQ(1, func_accept_callback(lambda_one_int)); + + EXPECT_EQ(2, func_accept_callback(lambda_two_ints)); + + EXPECT_EQ(3, func_accept_callback(lambda_one_int_one_char)); + + EXPECT_EQ(0, func_accept_callback(FunctionObjectNoArgs())); + + EXPECT_EQ(1, func_accept_callback(FunctionObjectOneInt())); + + EXPECT_EQ(2, func_accept_callback(FunctionObjectTwoInts())); + + EXPECT_EQ(3, func_accept_callback(FunctionObjectOneIntOneChar())); }