Merge pull request #122 from ros2/expression-sfinae-windows

Work around VS2015's broken support for expression SFINAE
This commit is contained in:
Esteve Fernandez 2015-10-13 14:28:18 -07:00
commit ae682baf1c
5 changed files with 435 additions and 89 deletions

View file

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

View file

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

View file

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

View file

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

View 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));
}