Added check_argument_types to simplify checking for a functor's arity and the types of its arguments
This commit is contained in:
parent
a0f1db1187
commit
d0ebdb239c
4 changed files with 201 additions and 138 deletions
|
@ -59,80 +59,58 @@ struct AnySubscriptionCallback
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename CallbackT,
|
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<
|
typename std::enable_if<
|
||||||
std::is_same<
|
rclcpp::check_argument_types<
|
||||||
typename function_traits<CallbackT>::template argument_type<0>,
|
CallbackT,
|
||||||
typename std::shared_ptr<MessageT>
|
typename std::shared_ptr<MessageT>
|
||||||
>::value
|
>::value
|
||||||
>::type * = nullptr)
|
>::type * = nullptr
|
||||||
|
>
|
||||||
|
void set(CallbackT callback)
|
||||||
{
|
{
|
||||||
shared_ptr_callback = callback;
|
shared_ptr_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename CallbackT,
|
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<
|
typename std::enable_if<
|
||||||
std::is_same<
|
rclcpp::check_argument_types<
|
||||||
typename function_traits<CallbackT>::template argument_type<0>,
|
CallbackT,
|
||||||
typename std::shared_ptr<MessageT>
|
typename std::shared_ptr<MessageT>,
|
||||||
>::value
|
|
||||||
>::type * = nullptr,
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same<
|
|
||||||
typename function_traits<CallbackT>::template argument_type<1>,
|
|
||||||
const rmw_message_info_t &
|
const rmw_message_info_t &
|
||||||
>::value
|
>::value
|
||||||
>::type * = nullptr)
|
>::type * = nullptr
|
||||||
|
>
|
||||||
|
void set(CallbackT callback)
|
||||||
{
|
{
|
||||||
shared_ptr_with_info_callback = callback;
|
shared_ptr_with_info_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename CallbackT,
|
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<
|
typename std::enable_if<
|
||||||
std::is_same<
|
rclcpp::check_argument_types<
|
||||||
typename function_traits<CallbackT>::template argument_type<0>,
|
CallbackT,
|
||||||
typename std::shared_ptr<const MessageT>
|
typename std::shared_ptr<const MessageT>
|
||||||
>::value
|
>::value
|
||||||
>::type * = nullptr)
|
>::type * = nullptr
|
||||||
|
>
|
||||||
|
void set(CallbackT callback)
|
||||||
{
|
{
|
||||||
const_shared_ptr_callback = callback;
|
const_shared_ptr_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename CallbackT,
|
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<
|
typename std::enable_if<
|
||||||
std::is_same<
|
rclcpp::check_argument_types<
|
||||||
typename function_traits<CallbackT>::template argument_type<0>,
|
CallbackT,
|
||||||
typename std::shared_ptr<const MessageT>
|
typename std::shared_ptr<const MessageT>,
|
||||||
>::value
|
|
||||||
>::type * = nullptr,
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same<
|
|
||||||
typename function_traits<CallbackT>::template argument_type<1>,
|
|
||||||
const rmw_message_info_t &
|
const rmw_message_info_t &
|
||||||
>::value
|
>::value
|
||||||
>::type * = nullptr)
|
>::type * = nullptr
|
||||||
|
>
|
||||||
|
void set(CallbackT callback)
|
||||||
{
|
{
|
||||||
const_shared_ptr_with_info_callback = callback;
|
const_shared_ptr_with_info_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,36 @@ struct function_traits<ReturnTypeT (ClassT::*)(Args ...) const>
|
||||||
* the arity of a function.
|
* the arity of a function.
|
||||||
*/
|
*/
|
||||||
template<std::size_t Arity, typename FunctorT>
|
template<std::size_t Arity, typename FunctorT>
|
||||||
struct arity_comparator
|
struct arity_comparator : std::integral_constant<
|
||||||
{
|
bool, (Arity == function_traits<FunctorT>::arity)>{};
|
||||||
static constexpr bool value = (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 */
|
} /* namespace rclcpp */
|
||||||
|
|
||||||
|
|
|
@ -283,42 +283,23 @@ private:
|
||||||
bool ignore_local_publications,
|
bool ignore_local_publications,
|
||||||
typename message_memory_strategy::MessageMemoryStrategy<MessageT>::SharedPtr msg_mem_strat);
|
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<
|
template<
|
||||||
typename ServiceT,
|
typename ServiceT,
|
||||||
typename FunctorT,
|
typename FunctorT,
|
||||||
std::size_t Arity = 2
|
|
||||||
>
|
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
rclcpp::arity_comparator<Arity, FunctorT>::value,
|
rclcpp::check_argument_types<
|
||||||
|
FunctorT,
|
||||||
|
typename std::shared_ptr<typename ServiceT::Request>,
|
||||||
|
typename std::shared_ptr<typename ServiceT::Response>
|
||||||
|
>::value
|
||||||
|
>::type * = nullptr
|
||||||
|
>
|
||||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||||
>::type
|
|
||||||
create_service_internal(
|
create_service_internal(
|
||||||
std::shared_ptr<rmw_node_t> node_handle,
|
std::shared_ptr<rmw_node_t> node_handle,
|
||||||
rmw_service_t * service_handle,
|
rmw_service_t * service_handle,
|
||||||
const std::string & service_name,
|
const std::string & service_name,
|
||||||
FunctorT callback,
|
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)
|
|
||||||
{
|
{
|
||||||
typename rclcpp::service::Service<ServiceT>::CallbackType callback_without_header =
|
typename rclcpp::service::Service<ServiceT>::CallbackType callback_without_header =
|
||||||
callback;
|
callback;
|
||||||
|
@ -329,35 +310,21 @@ private:
|
||||||
template<
|
template<
|
||||||
typename ServiceT,
|
typename ServiceT,
|
||||||
typename FunctorT,
|
typename FunctorT,
|
||||||
std::size_t Arity = 3
|
|
||||||
>
|
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
arity_comparator<Arity, FunctorT>::value,
|
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 rclcpp::service::Service<ServiceT>::SharedPtr
|
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||||
>::type
|
|
||||||
create_service_internal(
|
create_service_internal(
|
||||||
std::shared_ptr<rmw_node_t> node_handle,
|
std::shared_ptr<rmw_node_t> node_handle,
|
||||||
rmw_service_t * service_handle,
|
rmw_service_t * service_handle,
|
||||||
const std::string & service_name,
|
const std::string & service_name,
|
||||||
FunctorT callback,
|
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)
|
|
||||||
{
|
{
|
||||||
typename rclcpp::service::Service<ServiceT>::CallbackWithHeaderType callback_with_header =
|
typename rclcpp::service::Service<ServiceT>::CallbackWithHeaderType callback_with_header =
|
||||||
callback;
|
callback;
|
||||||
|
|
|
@ -70,17 +70,21 @@ struct FunctionObjectOneIntOneChar
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename FunctorT,
|
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
|
int func_accept_callback(FunctorT callback)
|
||||||
func_accept_callback(FunctorT callback,
|
{
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename FunctorT,
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_same<
|
rclcpp::check_argument_types<FunctorT, int>::value
|
||||||
int,
|
|
||||||
typename rclcpp::function_traits<FunctorT>::template argument_type<0>
|
|
||||||
>::value
|
|
||||||
>::type * = nullptr
|
>::type * = nullptr
|
||||||
)
|
>
|
||||||
|
int func_accept_callback(FunctorT callback)
|
||||||
{
|
{
|
||||||
int a = 4;
|
int a = 4;
|
||||||
return callback(a);
|
return callback(a);
|
||||||
|
@ -88,22 +92,11 @@ func_accept_callback(FunctorT callback,
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename FunctorT,
|
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
|
int func_accept_callback(FunctorT callback)
|
||||||
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 a = 5;
|
int a = 5;
|
||||||
int b = 6;
|
int b = 6;
|
||||||
|
@ -112,22 +105,11 @@ func_accept_callback(FunctorT callback,
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename FunctorT,
|
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
|
int func_accept_callback(FunctorT callback)
|
||||||
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 a = 7;
|
int a = 7;
|
||||||
char b = 8;
|
char b = 8;
|
||||||
|
@ -316,13 +298,123 @@ TEST(TestFunctionTraits, argument_types) {
|
||||||
>::value, "Functor accepts a char as second argument");
|
>::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.
|
Tests that functions are matched via SFINAE.
|
||||||
*/
|
*/
|
||||||
TEST(TestFunctionTraits, sfinae_match) {
|
TEST(TestFunctionTraits, sfinae_match) {
|
||||||
|
EXPECT_EQ(0, func_accept_callback(func_no_args));
|
||||||
|
|
||||||
EXPECT_EQ(1, func_accept_callback(func_one_int));
|
EXPECT_EQ(1, func_accept_callback(func_one_int));
|
||||||
|
|
||||||
EXPECT_EQ(2, func_accept_callback(func_two_ints));
|
EXPECT_EQ(2, func_accept_callback(func_two_ints));
|
||||||
|
|
||||||
EXPECT_EQ(3, func_accept_callback(func_one_int_one_char));
|
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