Added check_argument_types to simplify checking for a functor's arity and the types of its arguments

This commit is contained in:
Esteve Fernandez 2015-10-14 12:25:31 -07:00
parent a0f1db1187
commit d0ebdb239c
4 changed files with 201 additions and 138 deletions

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;

View file

@ -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()));
}