Merge pull request #130 from ros2/cleaner-sfinae
Add check_argument_types
This commit is contained in:
commit
535008f6e8
4 changed files with 201 additions and 138 deletions
|
@ -59,80 +59,58 @@ struct AnySubscriptionCallback
|
|||
|
||||
template<
|
||||
typename CallbackT,
|
||||
std::size_t Arity = 1
|
||||
>
|
||||
typename std::enable_if<rclcpp::arity_comparator<Arity, CallbackT>::value, void>::type
|
||||
set(
|
||||
CallbackT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
rclcpp::check_argument_types<
|
||||
CallbackT,
|
||||
typename std::shared_ptr<MessageT>
|
||||
>::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<rclcpp::arity_comparator<Arity, CallbackT>::value, void>::type
|
||||
set(
|
||||
CallbackT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
typename std::shared_ptr<MessageT>
|
||||
>::value
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<1>,
|
||||
rclcpp::check_argument_types<
|
||||
CallbackT,
|
||||
typename std::shared_ptr<MessageT>,
|
||||
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<rclcpp::arity_comparator<Arity, CallbackT>::value, void>::type
|
||||
set(
|
||||
CallbackT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
rclcpp::check_argument_types<
|
||||
CallbackT,
|
||||
typename std::shared_ptr<const MessageT>
|
||||
>::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<rclcpp::arity_comparator<Arity, CallbackT>::value, void>::type
|
||||
set(
|
||||
CallbackT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
typename std::shared_ptr<const MessageT>
|
||||
>::value
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<1>,
|
||||
rclcpp::check_argument_types<
|
||||
CallbackT,
|
||||
typename std::shared_ptr<const MessageT>,
|
||||
const rmw_message_info_t &
|
||||
>::value
|
||||
>::type * = nullptr)
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
{
|
||||
const_shared_ptr_with_info_callback = callback;
|
||||
}
|
||||
|
|
|
@ -64,10 +64,36 @@ struct function_traits<ReturnTypeT (ClassT::*)(Args ...) const>
|
|||
* the arity of a function.
|
||||
*/
|
||||
template<std::size_t Arity, typename FunctorT>
|
||||
struct arity_comparator
|
||||
{
|
||||
static constexpr bool value = (Arity == function_traits<FunctorT>::arity);
|
||||
};
|
||||
struct arity_comparator : std::integral_constant<
|
||||
bool, (Arity == function_traits<FunctorT>::arity)>{};
|
||||
|
||||
template<typename FunctorT, typename First, typename ... Last>
|
||||
struct check_argument_types_recursive : std::conditional<
|
||||
std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<
|
||||
function_traits<FunctorT>::arity - sizeof ... (Last) -1
|
||||
>,
|
||||
First
|
||||
>::value,
|
||||
check_argument_types_recursive<FunctorT, Last ...>,
|
||||
std::false_type>::type
|
||||
{};
|
||||
|
||||
template<typename FunctorT, typename Arg>
|
||||
struct check_argument_types_recursive<FunctorT, Arg>: std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<
|
||||
function_traits<FunctorT>::arity - 1
|
||||
>,
|
||||
Arg
|
||||
>
|
||||
{};
|
||||
|
||||
template<typename FunctorT, typename ... Args>
|
||||
struct check_argument_types : std::conditional<
|
||||
arity_comparator<sizeof ... (Args), FunctorT>::value,
|
||||
check_argument_types_recursive<FunctorT, Args ...>,
|
||||
std::false_type>::type
|
||||
{};
|
||||
|
||||
} /* namespace rclcpp */
|
||||
|
||||
|
|
|
@ -283,42 +283,23 @@ private:
|
|||
bool ignore_local_publications,
|
||||
typename message_memory_strategy::MessageMemoryStrategy<MessageT>::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 ServiceT::Request>,
|
||||
typename std::shared_ptr<typename ServiceT::Response>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
typename std::enable_if<
|
||||
rclcpp::arity_comparator<Arity, FunctorT>::value,
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
>::type
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
create_service_internal(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_service_t * service_handle,
|
||||
const std::string & service_name,
|
||||
FunctorT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<0>,
|
||||
typename std::shared_ptr<typename ServiceT::Request>
|
||||
>::value
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<1>,
|
||||
typename std::shared_ptr<typename ServiceT::Response>
|
||||
>::value
|
||||
>::type * = nullptr)
|
||||
FunctorT callback)
|
||||
{
|
||||
typename rclcpp::service::Service<ServiceT>::CallbackType callback_without_header =
|
||||
callback;
|
||||
|
@ -329,35 +310,21 @@ private:
|
|||
template<
|
||||
typename ServiceT,
|
||||
typename FunctorT,
|
||||
std::size_t Arity = 3
|
||||
typename std::enable_if<
|
||||
rclcpp::check_argument_types<
|
||||
FunctorT,
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
typename std::shared_ptr<typename ServiceT::Request>,
|
||||
typename std::shared_ptr<typename ServiceT::Response>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
typename std::enable_if<
|
||||
arity_comparator<Arity, FunctorT>::value,
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
>::type
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
create_service_internal(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_service_t * service_handle,
|
||||
const std::string & service_name,
|
||||
FunctorT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<0>,
|
||||
std::shared_ptr<rmw_request_id_t>
|
||||
>::value
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<1>,
|
||||
typename std::shared_ptr<typename ServiceT::Request>
|
||||
>::value
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<2>,
|
||||
typename std::shared_ptr<typename ServiceT::Response>
|
||||
>::value
|
||||
>::type * = nullptr)
|
||||
FunctorT callback)
|
||||
{
|
||||
typename rclcpp::service::Service<ServiceT>::CallbackWithHeaderType callback_with_header =
|
||||
callback;
|
||||
|
|
|
@ -70,17 +70,21 @@ struct FunctionObjectOneIntOneChar
|
|||
|
||||
template<
|
||||
typename FunctorT,
|
||||
std::size_t Arity = 1
|
||||
std::size_t Arity = 0,
|
||||
typename std::enable_if<rclcpp::arity_comparator<Arity, FunctorT>::value>::type * = nullptr
|
||||
>
|
||||
typename std::enable_if<rclcpp::arity_comparator<Arity, FunctorT>::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<FunctorT>::template argument_type<0>
|
||||
>::value
|
||||
rclcpp::check_argument_types<FunctorT, int>::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<
|
||||
rclcpp::check_argument_types<FunctorT, int, int>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
typename std::enable_if<rclcpp::arity_comparator<Arity, FunctorT>::value, int>::type
|
||||
func_accept_callback(FunctorT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
int,
|
||||
typename rclcpp::function_traits<FunctorT>::template argument_type<0>
|
||||
>::value
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
int,
|
||||
typename rclcpp::function_traits<FunctorT>::template argument_type<1>
|
||||
>::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<
|
||||
rclcpp::check_argument_types<FunctorT, int, char>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
typename std::enable_if<rclcpp::arity_comparator<Arity, FunctorT>::value, int>::type
|
||||
func_accept_callback(FunctorT callback,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
int,
|
||||
typename rclcpp::function_traits<FunctorT>::template argument_type<0>
|
||||
>::value
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
char,
|
||||
typename rclcpp::function_traits<FunctorT>::template argument_type<1>
|
||||
>::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<decltype(func_one_int), int>::value,
|
||||
"Functor accepts a single int as arguments");
|
||||
|
||||
static_assert(
|
||||
!rclcpp::check_argument_types<decltype(func_one_int), char>::value,
|
||||
"Functor does not accept a char as argument");
|
||||
|
||||
static_assert(
|
||||
!rclcpp::check_argument_types<decltype(func_one_int), char, int>::value,
|
||||
"Functor does not accept two arguments");
|
||||
|
||||
static_assert(
|
||||
!rclcpp::check_argument_types<decltype(func_two_ints), int>::value,
|
||||
"Functor accepts two ints as arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::check_argument_types<decltype(func_two_ints), int, int>::value,
|
||||
"Functor accepts two ints as arguments");
|
||||
|
||||
static_assert(
|
||||
!rclcpp::check_argument_types<decltype(func_two_ints), bool, int>::value,
|
||||
"Functor accepts two ints as arguments");
|
||||
|
||||
static_assert(
|
||||
!rclcpp::check_argument_types<decltype(func_two_ints), int, char>::value,
|
||||
"Functor accepts two ints as arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::check_argument_types<decltype(func_one_int_one_char), int, char>::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<decltype(lambda_one_int), int>::value,
|
||||
"Functor accepts an int as the only argument");
|
||||
|
||||
static_assert(
|
||||
rclcpp::check_argument_types<decltype(lambda_two_ints), int, int>::value,
|
||||
"Functor accepts two ints as arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::check_argument_types<decltype(lambda_one_int_one_char), int, char>::value,
|
||||
"Functor accepts an int and a char as arguments");
|
||||
|
||||
// Test objects that have a call operator
|
||||
static_assert(
|
||||
rclcpp::check_argument_types<FunctionObjectOneInt, int>::value,
|
||||
"Functor accepts an int as the only argument");
|
||||
|
||||
static_assert(
|
||||
rclcpp::check_argument_types<FunctionObjectTwoInts, int, int>::value,
|
||||
"Functor accepts two ints as arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::check_argument_types<FunctionObjectOneIntOneChar, int, char>::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()));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue