Skip to content

Commit 952e22e

Browse files
stevewolterclalancette
authored andcommitted
Make get_rcl_allocator a function family
As explained in #1254, there's conceptually no way to implement RCL allocators in terms of C++ allocators. In order to fix this behavior, we have to delete the generic version of get_rcl_allocator. Since some ROS code depends on this, we need to allow users to write their own version of get_rcl_allocator for allocators that support the C-style interface (most do). So this CL changes get_rcl_allocator from a template function into a family of (potentially templated) functions, which allows users to add their own overloads and rely on the "most specialized" mechanism for function specialization to select the right one. See http://www.gotw.ca/publications/mill17.htm for details. This also allows us to return get_rcl_default_allocator for all specializations of std::allocator (previously, only for std::allocator), which will already fix #1254 for pretty much all clients. I'll continue to work on deleting the generic version, though, to make sure that nobody is accidentally bitten by it. I've tried to test this by doing a full ROS compilation following the Dockerfile of the source Docker image, and all packages compile. Signed-off-by: Steve Wolter <[email protected]>
1 parent bff6916 commit 952e22e

File tree

6 files changed

+18
-106
lines changed

6 files changed

+18
-106
lines changed

rclcpp/include/rclcpp/allocator/allocator_common.hpp

+11-59
Original file line numberDiff line numberDiff line change
@@ -29,71 +29,23 @@ namespace allocator
2929
template<typename T, typename Alloc>
3030
using AllocRebind = typename std::allocator_traits<Alloc>::template rebind_traits<T>;
3131

32-
template<typename Alloc>
33-
void * retyped_allocate(size_t size, void * untyped_allocator)
32+
/// Return the equivalent rcl_allocator_t for the C++ standard allocator.
33+
/**
34+
* This assumes that the user intent behind both allocators is the
35+
* same: Using system malloc for allocation.
36+
*
37+
* If you're using a custom allocator in ROS, you'll need to provide
38+
* your own overload for this function.
39+
*/
40+
template<typename T>
41+
rcl_allocator_t get_rcl_allocator(std::allocator<T> allocator)
3442
{
35-
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
36-
if (!typed_allocator) {
37-
throw std::runtime_error("Received incorrect allocator type");
38-
}
39-
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
40-
}
41-
42-
template<typename T, typename Alloc>
43-
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
44-
{
45-
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
46-
if (!typed_allocator) {
47-
throw std::runtime_error("Received incorrect allocator type");
48-
}
49-
auto typed_ptr = static_cast<T *>(untyped_pointer);
50-
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
51-
}
52-
53-
template<typename T, typename Alloc>
54-
void * retyped_reallocate(void * untyped_pointer, size_t size, void * untyped_allocator)
55-
{
56-
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
57-
if (!typed_allocator) {
58-
throw std::runtime_error("Received incorrect allocator type");
59-
}
60-
auto typed_ptr = static_cast<T *>(untyped_pointer);
61-
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
62-
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
63-
}
64-
65-
66-
// Convert a std::allocator_traits-formatted Allocator into an rcl allocator
67-
template<
68-
typename T,
69-
typename Alloc,
70-
typename std::enable_if<!std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
71-
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
72-
{
73-
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
74-
#ifndef _WIN32
75-
rcl_allocator.allocate = &retyped_allocate<Alloc>;
76-
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
77-
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
78-
rcl_allocator.state = &allocator;
79-
#else
8043
(void)allocator; // Remove warning
81-
#endif
82-
return rcl_allocator;
83-
}
84-
85-
// TODO(jacquelinekay) Workaround for an incomplete implementation of std::allocator<void>
86-
template<
87-
typename T,
88-
typename Alloc,
89-
typename std::enable_if<std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
90-
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
91-
{
92-
(void)allocator;
9344
return rcl_get_default_allocator();
9445
}
9546

9647
} // namespace allocator
48+
9749
} // namespace rclcpp
9850

9951
#endif // RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_

rclcpp/include/rclcpp/message_memory_strategy.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ class MessageMemoryStrategy
6161
message_allocator_ = std::make_shared<MessageAlloc>();
6262
serialized_message_allocator_ = std::make_shared<SerializedMessageAlloc>();
6363
buffer_allocator_ = std::make_shared<BufferAlloc>();
64-
rcutils_allocator_ = allocator::get_rcl_allocator<char, BufferAlloc>(*buffer_allocator_.get());
64+
rcutils_allocator_ = rclcpp::allocator::get_rcl_allocator(*buffer_allocator_.get());
6565
}
6666

6767
explicit MessageMemoryStrategy(std::shared_ptr<Alloc> allocator)
6868
{
6969
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
7070
serialized_message_allocator_ = std::make_shared<SerializedMessageAlloc>(*allocator.get());
7171
buffer_allocator_ = std::make_shared<BufferAlloc>(*allocator.get());
72-
rcutils_allocator_ = allocator::get_rcl_allocator<char, BufferAlloc>(*buffer_allocator_.get());
72+
rcutils_allocator_ = rclcpp::allocator::get_rcl_allocator(*buffer_allocator_.get());
7373
}
7474

7575
virtual ~MessageMemoryStrategy() = default;

rclcpp/include/rclcpp/publisher_options.hpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
8080
to_rcl_publisher_options(const rclcpp::QoS & qos) const
8181
{
8282
rcl_publisher_options_t result = rcl_publisher_get_default_options();
83-
using AllocatorTraits = std::allocator_traits<Allocator>;
84-
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
85-
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
86-
result.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc);
83+
result.allocator = rclcpp::allocator::get_rcl_allocator(*this->get_allocator());
8784
result.qos = qos.get_rmw_qos_profile();
8885
result.rmw_publisher_options.require_unique_network_flow_endpoints =
8986
this->require_unique_network_flow_endpoints;

rclcpp/include/rclcpp/strategies/allocator_memory_strategy.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ class AllocatorMemoryStrategy : public memory_strategy::MemoryStrategy
437437

438438
rcl_allocator_t get_allocator() override
439439
{
440-
return rclcpp::allocator::get_rcl_allocator<void *, VoidAlloc>(*allocator_.get());
440+
return rclcpp::allocator::get_rcl_allocator(*allocator_.get());
441441
}
442442

443443
size_t number_of_ready_subscriptions() const override

rclcpp/include/rclcpp/subscription_options.hpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,7 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
107107
to_rcl_subscription_options(const rclcpp::QoS & qos) const
108108
{
109109
rcl_subscription_options_t result = rcl_subscription_get_default_options();
110-
using AllocatorTraits = std::allocator_traits<Allocator>;
111-
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
112-
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
113-
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
110+
result.allocator = rclcpp::allocator::get_rcl_allocator(*this->get_allocator());
114111
result.qos = qos.get_rmw_qos_profile();
115112
result.rmw_subscription_options.ignore_local_publications = this->ignore_local_publications;
116113
result.rmw_subscription_options.require_unique_network_flow_endpoints =

rclcpp/test/rclcpp/allocator/test_allocator_common.cpp

+2-36
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,9 @@
1818

1919
#include "rclcpp/allocator/allocator_common.hpp"
2020

21-
TEST(TestAllocatorCommon, retyped_allocate) {
22-
std::allocator<int> allocator;
23-
void * untyped_allocator = &allocator;
24-
void * allocated_mem =
25-
rclcpp::allocator::retyped_allocate<std::allocator<char>>(1u, untyped_allocator);
26-
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
27-
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
28-
ASSERT_TRUE(nullptr != allocated_mem);
29-
30-
auto code = [&untyped_allocator, allocated_mem]() {
31-
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
32-
allocated_mem, untyped_allocator);
33-
};
34-
EXPECT_NO_THROW(code());
35-
36-
allocated_mem = allocator.allocate(1);
37-
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
38-
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
39-
ASSERT_TRUE(nullptr != allocated_mem);
40-
void * reallocated_mem =
41-
rclcpp::allocator::retyped_reallocate<int, std::allocator<int>>(
42-
allocated_mem, 2u, untyped_allocator);
43-
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
44-
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
45-
ASSERT_TRUE(nullptr != reallocated_mem);
46-
47-
auto code2 = [&untyped_allocator, reallocated_mem]() {
48-
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
49-
reallocated_mem, untyped_allocator);
50-
};
51-
EXPECT_NO_THROW(code2());
52-
}
53-
5421
TEST(TestAllocatorCommon, get_rcl_allocator) {
5522
std::allocator<int> allocator;
56-
auto rcl_allocator = rclcpp::allocator::get_rcl_allocator<int>(allocator);
23+
auto rcl_allocator = rclcpp::allocator::get_rcl_allocator(allocator);
5724
EXPECT_NE(nullptr, rcl_allocator.allocate);
5825
EXPECT_NE(nullptr, rcl_allocator.deallocate);
5926
EXPECT_NE(nullptr, rcl_allocator.reallocate);
@@ -63,8 +30,7 @@ TEST(TestAllocatorCommon, get_rcl_allocator) {
6330

6431
TEST(TestAllocatorCommon, get_void_rcl_allocator) {
6532
std::allocator<void> allocator;
66-
auto rcl_allocator =
67-
rclcpp::allocator::get_rcl_allocator<void, std::allocator<void>>(allocator);
33+
auto rcl_allocator = rclcpp::allocator::get_rcl_allocator(allocator);
6834
EXPECT_NE(nullptr, rcl_allocator.allocate);
6935
EXPECT_NE(nullptr, rcl_allocator.deallocate);
7036
EXPECT_NE(nullptr, rcl_allocator.reallocate);

0 commit comments

Comments
 (0)