Merge pull request #122 from ros2/expression-sfinae-windows
Work around VS2015's broken support for expression SFINAE
This commit is contained in:
commit
ae682baf1c
5 changed files with 435 additions and 89 deletions
|
@ -22,6 +22,7 @@ if(AMENT_ENABLE_TESTING)
|
|||
|
||||
include_directories(include)
|
||||
|
||||
ament_add_gtest(test_function_traits test/test_function_traits.cpp)
|
||||
ament_add_gtest(test_mapped_ring_buffer test/test_mapped_ring_buffer.cpp)
|
||||
ament_add_gtest(test_intra_process_manager test/test_intra_process_manager.cpp)
|
||||
if(TARGET test_intra_process_manager)
|
||||
|
|
|
@ -57,75 +57,83 @@ struct AnySubscriptionCallback
|
|||
|
||||
AnySubscriptionCallback(const AnySubscriptionCallback &) = default;
|
||||
|
||||
template<typename CallbackT,
|
||||
typename std::enable_if<
|
||||
function_traits<CallbackT>::arity == 1
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
typename std::shared_ptr<MessageT>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
template<
|
||||
typename CallbackT,
|
||||
std::size_t Arity = 1
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
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)
|
||||
{
|
||||
shared_ptr_callback = callback;
|
||||
}
|
||||
|
||||
template<typename CallbackT,
|
||||
typename std::enable_if<
|
||||
function_traits<CallbackT>::arity == 2
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
typename std::shared_ptr<MessageT>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
template<
|
||||
typename CallbackT,
|
||||
std::size_t Arity = 2
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
{
|
||||
static_assert(std::is_same<
|
||||
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>,
|
||||
const rmw_message_info_t &>::value,
|
||||
"Passed incorrect argument type to callback, should be rmw_message_info_t");
|
||||
const rmw_message_info_t &
|
||||
>::value
|
||||
>::type * = nullptr)
|
||||
{
|
||||
shared_ptr_with_info_callback = callback;
|
||||
}
|
||||
|
||||
template<typename CallbackT,
|
||||
typename std::enable_if<
|
||||
function_traits<CallbackT>::arity == 1
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
typename std::shared_ptr<const MessageT>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
template<
|
||||
typename CallbackT,
|
||||
std::size_t Arity = 1
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
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)
|
||||
{
|
||||
const_shared_ptr_callback = callback;
|
||||
}
|
||||
|
||||
template<typename CallbackT,
|
||||
typename std::enable_if<
|
||||
function_traits<CallbackT>::arity == 2
|
||||
>::type * = nullptr,
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename function_traits<CallbackT>::template argument_type<0>,
|
||||
typename std::shared_ptr<const MessageT>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
template<
|
||||
typename CallbackT,
|
||||
std::size_t Arity = 2
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
{
|
||||
static_assert(std::is_same<
|
||||
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>,
|
||||
const rmw_message_info_t &>::value,
|
||||
"Passed incorrect argument type to callback, should be rmw_message_info_t");
|
||||
const rmw_message_info_t &
|
||||
>::value
|
||||
>::type * = nullptr)
|
||||
{
|
||||
const_shared_ptr_with_info_callback = callback;
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace rclcpp
|
|||
* but unfortunately std::function's constructor on VS2015 is too greedy,
|
||||
* so we need a mechanism for checking the arity and the type of each argument
|
||||
* in a callback function.
|
||||
* See http://blogs.msdn.com/b/vcblog/archive/2015/06/19/c-11-14-17-features-in-vs-2015-rtm.aspx
|
||||
*/
|
||||
|
||||
|
||||
|
@ -58,6 +59,16 @@ struct function_traits<ReturnTypeT (ClassT::*)(Args ...) const>
|
|||
: public function_traits<ReturnTypeT(ClassT &, Args ...)>
|
||||
{};
|
||||
|
||||
/* NOTE(esteve):
|
||||
* VS2015 does not support expression SFINAE, so we're using this template to evaluate
|
||||
* the arity of a function.
|
||||
*/
|
||||
template<std::size_t Arity, typename FunctorT>
|
||||
struct arity_comparator
|
||||
{
|
||||
static constexpr bool value = (Arity == function_traits<FunctorT>::arity);
|
||||
};
|
||||
|
||||
} /* namespace rclcpp */
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_FUNCTION_TRAITS_HPP_ */
|
||||
|
|
|
@ -247,12 +247,30 @@ 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,
|
||||
typename std::enable_if<
|
||||
function_traits<FunctorT>::arity == 2
|
||||
>::type * = nullptr,
|
||||
std::size_t Arity = 2
|
||||
>
|
||||
typename std::enable_if<
|
||||
rclcpp::arity_comparator<Arity, FunctorT>::value,
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
>::type
|
||||
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>,
|
||||
|
@ -264,14 +282,7 @@ private:
|
|||
typename function_traits<FunctorT>::template argument_type<1>,
|
||||
typename std::shared_ptr<typename ServiceT::Response>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
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)
|
||||
>::type * = nullptr)
|
||||
{
|
||||
typename rclcpp::service::Service<ServiceT>::CallbackType callback_without_header =
|
||||
callback;
|
||||
|
@ -282,9 +293,17 @@ private:
|
|||
template<
|
||||
typename ServiceT,
|
||||
typename FunctorT,
|
||||
typename std::enable_if<
|
||||
function_traits<FunctorT>::arity == 3
|
||||
>::type * = nullptr,
|
||||
std::size_t Arity = 3
|
||||
>
|
||||
typename std::enable_if<
|
||||
arity_comparator<Arity, FunctorT>::value,
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
>::type
|
||||
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>,
|
||||
|
@ -296,35 +315,14 @@ private:
|
|||
typename function_traits<FunctorT>::template argument_type<1>,
|
||||
typename std::shared_ptr<typename ServiceT::Request>
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
/*
|
||||
TODO(esteve): reenable this block of code when VS2015 gets better support
|
||||
for SFINAE and remove the static_assert from the body of this method. For
|
||||
more info about the current support for SFINAE in VS2015 RC:
|
||||
|
||||
http://blogs.msdn.com/b/vcblog/archive/2015/04/29/c-11-14-17-features-in-vs-2015-rc.aspx
|
||||
,
|
||||
>::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>::SharedPtr
|
||||
create_service_internal(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_service_t * service_handle,
|
||||
const std::string & service_name,
|
||||
FunctorT callback)
|
||||
>::type * = nullptr)
|
||||
{
|
||||
static_assert(
|
||||
std::is_same<
|
||||
typename function_traits<FunctorT>::template argument_type<2>,
|
||||
typename std::shared_ptr<typename ServiceT::Response>
|
||||
>::value, "Third argument must be of type std::shared_ptr<ServiceT::Response>");
|
||||
|
||||
typename rclcpp::service::Service<ServiceT>::CallbackWithHeaderType callback_with_header =
|
||||
callback;
|
||||
return service::Service<ServiceT>::make_shared(
|
||||
|
|
328
rclcpp/test/test_function_traits.cpp
Normal file
328
rclcpp/test/test_function_traits.cpp
Normal file
|
@ -0,0 +1,328 @@
|
|||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <rclcpp/function_traits.hpp>
|
||||
|
||||
int func_no_args()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int func_one_int(int)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int func_two_ints(int, int)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int func_one_int_one_char(int, char)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
struct FunctionObjectNoArgs
|
||||
{
|
||||
int operator()() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionObjectOneInt
|
||||
{
|
||||
int operator()(int) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionObjectTwoInts
|
||||
{
|
||||
int operator()(int, int) const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionObjectOneIntOneChar
|
||||
{
|
||||
int operator()(int, char) const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
typename FunctorT,
|
||||
std::size_t Arity = 1
|
||||
>
|
||||
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
|
||||
)
|
||||
{
|
||||
int a = 4;
|
||||
return callback(a);
|
||||
}
|
||||
|
||||
template<
|
||||
typename FunctorT,
|
||||
std::size_t Arity = 2
|
||||
>
|
||||
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 a = 5;
|
||||
int b = 6;
|
||||
return callback(a, b);
|
||||
}
|
||||
|
||||
template<
|
||||
typename FunctorT,
|
||||
std::size_t Arity = 2
|
||||
>
|
||||
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 a = 7;
|
||||
char b = 8;
|
||||
return callback(a, b);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests that funcion_traits calculates arity of several functors.
|
||||
*/
|
||||
TEST(TestFunctionTraits, arity) {
|
||||
// Test regular functions
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(func_no_args)>::arity == 0,
|
||||
"Functor does not accept arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(func_one_int)>::arity == 1,
|
||||
"Functor only accepts one argument");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(func_two_ints)>::arity == 2,
|
||||
"Functor only accepts two arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(func_one_int_one_char)>::arity == 2,
|
||||
"Functor only accepts two arguments");
|
||||
|
||||
// Test lambdas
|
||||
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;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(lambda_no_args)>::arity == 0,
|
||||
"Functor does not accept arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(lambda_one_int)>::arity == 1,
|
||||
"Functor only accepts one argument");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(lambda_two_ints)>::arity == 2,
|
||||
"Functor only accepts two arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<decltype(lambda_one_int_one_char)>::arity == 2,
|
||||
"Functor only accepts two arguments");
|
||||
|
||||
// Test objects that have a call operator
|
||||
static_assert(
|
||||
rclcpp::function_traits<FunctionObjectNoArgs>::arity == 0,
|
||||
"Functor does not accept arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<FunctionObjectOneInt>::arity == 1,
|
||||
"Functor only accepts one argument");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<FunctionObjectTwoInts>::arity == 2,
|
||||
"Functor only accepts two arguments");
|
||||
|
||||
static_assert(
|
||||
rclcpp::function_traits<FunctionObjectOneIntOneChar>::arity == 2,
|
||||
"Functor only accepts two arguments");
|
||||
}
|
||||
|
||||
/*
|
||||
Tests that funcion_traits deducts the type of the arguments of several functors.
|
||||
*/
|
||||
TEST(TestFunctionTraits, argument_types) {
|
||||
// Test regular functions
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(func_one_int)>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(func_two_ints)>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(func_two_ints)>::template argument_type<1>
|
||||
>::value, "Functor accepts an int as second argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(func_one_int_one_char)>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
char,
|
||||
rclcpp::function_traits<decltype(func_one_int_one_char)>::template argument_type<1>
|
||||
>::value, "Functor accepts a char as second argument");
|
||||
|
||||
// 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(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(lambda_one_int)>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(lambda_two_ints)>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(lambda_two_ints)>::template argument_type<1>
|
||||
>::value, "Functor accepts an int as second argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<decltype(lambda_one_int_one_char)>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
char,
|
||||
rclcpp::function_traits<decltype(lambda_one_int_one_char)>::template argument_type<1>
|
||||
>::value, "Functor accepts a char as second argument");
|
||||
|
||||
// Test objects that have a call operator
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<FunctionObjectOneInt>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<FunctionObjectTwoInts>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<FunctionObjectTwoInts>::template argument_type<1>
|
||||
>::value, "Functor accepts an int as second argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
int,
|
||||
rclcpp::function_traits<FunctionObjectOneIntOneChar>::template argument_type<0>
|
||||
>::value, "Functor accepts an int as first argument");
|
||||
|
||||
static_assert(
|
||||
std::is_same<
|
||||
char,
|
||||
rclcpp::function_traits<FunctionObjectOneIntOneChar>::template argument_type<1>
|
||||
>::value, "Functor accepts a char as second argument");
|
||||
}
|
||||
|
||||
/*
|
||||
Tests that functions are matched via SFINAE.
|
||||
*/
|
||||
TEST(TestFunctionTraits, sfinae_match) {
|
||||
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));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue