Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00f2d563be | ||
|
|
64ee7d6822 | ||
|
|
0750dc418a | ||
|
|
0d6d9e6778 | ||
|
|
86c079de31 | ||
|
|
fb8519070c | ||
|
|
e1095adeee | ||
|
|
4ecb3dd090 | ||
|
|
0034929eef | ||
|
|
dbb717cd6e | ||
|
|
e9e398d2d4 | ||
|
|
7d8b2690ff | ||
|
|
55d2f67b90 | ||
|
|
39bfb30eb0 | ||
|
|
1c61751540 | ||
|
|
0251f70fe8 | ||
|
|
07b6ea0ff4 | ||
|
|
56f68f9c44 | ||
|
|
18fcd9138b | ||
|
|
f245b4cc81 | ||
|
|
d488535f36 | ||
|
|
72443ff295 | ||
|
|
a3383c309a | ||
|
|
5dad229662 | ||
|
|
2e4c97ac56 | ||
|
|
0659d829ce | ||
|
|
1fc2d58799 | ||
|
|
c15eb1eaac | ||
|
|
d051b8aa20 | ||
|
|
6806cdf825 | ||
|
|
79c2dd8e8b | ||
|
|
fba080cf34 | ||
|
|
bff6916e8f | ||
|
|
b8df9347a1 |
@@ -2,6 +2,46 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
11.2.0 (2021-07-21)
|
||||
-------------------
|
||||
* Support to defer to send a response in services. (`#1709 <https://github.com/ros2/rclcpp/issues/1709>`_)
|
||||
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
|
||||
* Fix documentation bug. (`#1719 <https://github.com/ros2/rclcpp/issues/1719>`_)
|
||||
Signed-off-by: William Woodall <william@osrfoundation.org>
|
||||
* Contributors: Ivan Santiago Paunovic, William Woodall
|
||||
|
||||
11.1.0 (2021-07-13)
|
||||
-------------------
|
||||
* Removed left over ``is_initialized()`` implementation (`#1711 <https://github.com/ros2/rclcpp/issues/1711>`_)
|
||||
Leftover from https://github.com/ros2/rclcpp/pull/1622
|
||||
* Fixed declare parameter methods for int and float vectors (`#1696 <https://github.com/ros2/rclcpp/issues/1696>`_)
|
||||
* Cleaned up implementation of the intra-process manager (`#1695 <https://github.com/ros2/rclcpp/issues/1695>`_)
|
||||
* Added the node name to an executor ``runtime_error`` (`#1686 <https://github.com/ros2/rclcpp/issues/1686>`_)
|
||||
* Fixed a typo "Attack" -> "Attach" (`#1687 <https://github.com/ros2/rclcpp/issues/1687>`_)
|
||||
* Removed use of std::allocator<>::rebind (`#1678 <https://github.com/ros2/rclcpp/issues/1678>`_)
|
||||
rebind is deprecated in c++17 and removed in c++20
|
||||
* Contributors: Alberto Soragna, Chen Lihui, Chris Lalancette, Petter Nilsson, Steve Macenski, William Woodall
|
||||
|
||||
11.0.0 (2021-05-18)
|
||||
-------------------
|
||||
* Allow declare uninitialized parameters (`#1673 <https://github.com/ros2/rclcpp/issues/1673>`_)
|
||||
* Fix syntax issue with gcc (`#1674 <https://github.com/ros2/rclcpp/issues/1674>`_)
|
||||
* [service] Don't use a weak_ptr to avoid leaking (`#1668 <https://github.com/ros2/rclcpp/issues/1668>`_)
|
||||
* Contributors: Ivan Santiago Paunovic, Jacob Perron, William Woodall
|
||||
|
||||
10.0.0 (2021-05-11)
|
||||
-------------------
|
||||
* Fix doc typo (`#1663 <https://github.com/ros2/rclcpp/issues/1663>`_)
|
||||
* [rclcpp] Type Adaptation feature (`#1557 <https://github.com/ros2/rclcpp/issues/1557>`_)
|
||||
* Do not attempt to use void allocators for memory allocation. (`#1657 <https://github.com/ros2/rclcpp/issues/1657>`_)
|
||||
* Keep custom allocator in publisher and subscription options alive. (`#1647 <https://github.com/ros2/rclcpp/issues/1647>`_)
|
||||
* Fix get_publishers_subscriptions_info_by_topic test in test_node.cpp (`#1648 <https://github.com/ros2/rclcpp/issues/1648>`_)
|
||||
* Use OnShutdown callback handle instead of OnShutdown callback (`#1639 <https://github.com/ros2/rclcpp/issues/1639>`_)
|
||||
* use dynamic_pointer_cast to detect allocator mismatch in intra process manager (`#1643 <https://github.com/ros2/rclcpp/issues/1643>`_)
|
||||
* Increase cppcheck timeout to 500s (`#1634 <https://github.com/ros2/rclcpp/issues/1634>`_)
|
||||
* Clarify node parameters docs (`#1631 <https://github.com/ros2/rclcpp/issues/1631>`_)
|
||||
* Contributors: Audrow Nash, Barry Xu, Jacob Perron, Michel Hidalgo, Shane Loretz, William Woodall
|
||||
|
||||
9.0.2 (2021-04-14)
|
||||
------------------
|
||||
* Avoid returning loan when none was obtained. (`#1629 <https://github.com/ros2/rclcpp/issues/1629>`_)
|
||||
|
||||
@@ -247,3 +247,8 @@ install(
|
||||
DIRECTORY include/ ${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
DESTINATION include
|
||||
)
|
||||
|
||||
if(TEST cppcheck)
|
||||
# must set the property after ament_package()
|
||||
set_tests_properties(cppcheck PROPERTIES TIMEOUT 500)
|
||||
endif()
|
||||
|
||||
@@ -95,7 +95,7 @@ void set_allocator_for_deleter(AllocatorDeleter<T> * deleter, Alloc * alloc)
|
||||
template<typename Alloc, typename T>
|
||||
using Deleter = typename std::conditional<
|
||||
std::is_same<typename std::allocator_traits<Alloc>::template rebind_alloc<T>,
|
||||
typename std::allocator<void>::template rebind<T>::other>::value,
|
||||
std::allocator<T>>::value,
|
||||
std::default_delete<T>,
|
||||
AllocatorDeleter<Alloc>
|
||||
>::type;
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
#ifndef RCLCPP__ANY_SERVICE_CALLBACK_HPP_
|
||||
#define RCLCPP__ANY_SERVICE_CALLBACK_HPP_
|
||||
|
||||
#include <variant>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -29,93 +31,133 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename T, typename = void>
|
||||
struct can_be_nullptr : std::false_type {};
|
||||
|
||||
// Some lambdas define a comparison with nullptr,
|
||||
// but we see a warning that they can never be null when using it.
|
||||
// We also test if `T &` can be assigned to `nullptr` to avoid the issue.
|
||||
template<typename T>
|
||||
struct can_be_nullptr<T, std::void_t<
|
||||
decltype(std::declval<T>() == nullptr), decltype(std::declval<T &>() = nullptr)>>
|
||||
: std::true_type {};
|
||||
} // namespace detail
|
||||
|
||||
// Forward declare
|
||||
template<typename ServiceT>
|
||||
class Service;
|
||||
|
||||
template<typename ServiceT>
|
||||
class AnyServiceCallback
|
||||
{
|
||||
private:
|
||||
using SharedPtrCallback = std::function<
|
||||
void (
|
||||
const std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
using SharedPtrWithRequestHeaderCallback = std::function<
|
||||
void (
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
|
||||
SharedPtrCallback shared_ptr_callback_;
|
||||
SharedPtrWithRequestHeaderCallback shared_ptr_with_request_header_callback_;
|
||||
|
||||
public:
|
||||
AnyServiceCallback()
|
||||
: shared_ptr_callback_(nullptr), shared_ptr_with_request_header_callback_(nullptr)
|
||||
: callback_(std::monostate{})
|
||||
{}
|
||||
|
||||
AnyServiceCallback(const AnyServiceCallback &) = default;
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
shared_ptr_callback_ = callback;
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
shared_ptr_with_request_header_callback_ = callback;
|
||||
if (!callback) {
|
||||
throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
|
||||
}
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
|
||||
void dispatch(
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<typename ServiceT::Request> request,
|
||||
std::shared_ptr<typename ServiceT::Response> response)
|
||||
// template<typename Allocator = std::allocator<typename ServiceT::Response>>
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
dispatch(
|
||||
const std::shared_ptr<rclcpp::Service<ServiceT>> & service_handle,
|
||||
const std::shared_ptr<rmw_request_id_t> & request_header,
|
||||
std::shared_ptr<typename ServiceT::Request> request)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
if (shared_ptr_callback_ != nullptr) {
|
||||
if (std::holds_alternative<std::monostate>(callback_)) {
|
||||
// TODO(ivanpauno): Remove the set method, and force the users of this class
|
||||
// to pass a callback at construnciton.
|
||||
throw std::runtime_error{"unexpected request without any callback set"};
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
|
||||
cb(request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
|
||||
cb(service_handle, request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
// auto response = allocate_shared<typename ServiceT::Response, Allocator>();
|
||||
auto response = std::make_shared<typename ServiceT::Response>();
|
||||
if (std::holds_alternative<SharedPtrCallback>(callback_)) {
|
||||
(void)request_header;
|
||||
shared_ptr_callback_(request, response);
|
||||
} else if (shared_ptr_with_request_header_callback_ != nullptr) {
|
||||
shared_ptr_with_request_header_callback_(request_header, request, response);
|
||||
} else {
|
||||
throw std::runtime_error("unexpected request without any callback set");
|
||||
const auto & cb = std::get<SharedPtrCallback>(callback_);
|
||||
cb(std::move(request), response);
|
||||
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
|
||||
cb(request_header, std::move(request), response);
|
||||
}
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return response;
|
||||
}
|
||||
|
||||
void register_callback_for_tracing()
|
||||
{
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
if (shared_ptr_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(shared_ptr_callback_));
|
||||
} else if (shared_ptr_with_request_header_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(shared_ptr_with_request_header_callback_));
|
||||
}
|
||||
std::visit(
|
||||
[this](auto && arg) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(arg));
|
||||
}, callback_);
|
||||
#endif // TRACETOOLS_DISABLED
|
||||
}
|
||||
|
||||
private:
|
||||
using SharedPtrCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
using SharedPtrWithRequestHeaderCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
std::shared_ptr<typename ServiceT::Request>
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
|
||||
void (
|
||||
std::shared_ptr<rclcpp::Service<ServiceT>>,
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
std::shared_ptr<typename ServiceT::Request>
|
||||
)>;
|
||||
|
||||
std::variant<
|
||||
std::monostate,
|
||||
SharedPtrCallback,
|
||||
SharedPtrWithRequestHeaderCallback,
|
||||
SharedPtrDeferResponseCallback,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <utility>
|
||||
#include <variant> // NOLINT[build/include_order]
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
#include "tracetools/utils.hpp"
|
||||
|
||||
@@ -29,6 +30,9 @@
|
||||
#include "rclcpp/detail/subscription_callback_type_helper.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/message_info.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
|
||||
template<class>
|
||||
inline constexpr bool always_false_v = false;
|
||||
@@ -42,53 +46,189 @@ namespace detail
|
||||
template<typename MessageT, typename AllocatorT>
|
||||
struct MessageDeleterHelper
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
|
||||
using AllocTraits = allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using Alloc = typename AllocTraits::allocator_type;
|
||||
using Deleter = allocator::Deleter<Alloc, MessageT>;
|
||||
};
|
||||
|
||||
/// Struct which contains all possible callback signatures, with or without a TypeAdapter.s
|
||||
template<typename MessageT, typename AllocatorT>
|
||||
struct AnySubscriptionCallbackHelper
|
||||
struct AnySubscriptionCallbackPossibleTypes
|
||||
{
|
||||
using MessageDeleter = typename MessageDeleterHelper<MessageT, AllocatorT>::MessageDeleter;
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using SubscribedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
|
||||
|
||||
using SubscribedMessageDeleter =
|
||||
typename MessageDeleterHelper<SubscribedType, AllocatorT>::Deleter;
|
||||
using ROSMessageDeleter =
|
||||
typename MessageDeleterHelper<ROSMessageType, AllocatorT>::Deleter;
|
||||
using SerializedMessageDeleter =
|
||||
typename MessageDeleterHelper<rclcpp::SerializedMessage, AllocatorT>::Deleter;
|
||||
|
||||
using ConstRefCallback =
|
||||
std::function<void (const MessageT &)>;
|
||||
std::function<void (const SubscribedType &)>;
|
||||
using ConstRefROSMessageCallback =
|
||||
std::function<void (const ROSMessageType &)>;
|
||||
using ConstRefWithInfoCallback =
|
||||
std::function<void (const MessageT &, const rclcpp::MessageInfo &)>;
|
||||
std::function<void (const SubscribedType &, const rclcpp::MessageInfo &)>;
|
||||
using ConstRefWithInfoROSMessageCallback =
|
||||
std::function<void (const ROSMessageType &, const rclcpp::MessageInfo &)>;
|
||||
using ConstRefSerializedMessageCallback =
|
||||
std::function<void (const rclcpp::SerializedMessage &)>;
|
||||
using ConstRefSerializedMessageWithInfoCallback =
|
||||
std::function<void (const rclcpp::SerializedMessage &, const rclcpp::MessageInfo &)>;
|
||||
|
||||
using UniquePtrCallback =
|
||||
std::function<void (std::unique_ptr<MessageT, MessageDeleter>)>;
|
||||
std::function<void (std::unique_ptr<SubscribedType, SubscribedMessageDeleter>)>;
|
||||
using UniquePtrROSMessageCallback =
|
||||
std::function<void (std::unique_ptr<ROSMessageType, ROSMessageDeleter>)>;
|
||||
using UniquePtrWithInfoCallback =
|
||||
std::function<void (std::unique_ptr<MessageT, MessageDeleter>, const rclcpp::MessageInfo &)>;
|
||||
std::function<void (
|
||||
std::unique_ptr<SubscribedType, SubscribedMessageDeleter>,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
using UniquePtrWithInfoROSMessageCallback =
|
||||
std::function<void (
|
||||
std::unique_ptr<ROSMessageType, ROSMessageDeleter>,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
using UniquePtrSerializedMessageCallback =
|
||||
std::function<void (std::unique_ptr<rclcpp::SerializedMessage, SerializedMessageDeleter>)>;
|
||||
using UniquePtrSerializedMessageWithInfoCallback =
|
||||
std::function<void (
|
||||
std::unique_ptr<rclcpp::SerializedMessage, SerializedMessageDeleter>,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
|
||||
using SharedConstPtrCallback =
|
||||
std::function<void (std::shared_ptr<const MessageT>)>;
|
||||
std::function<void (std::shared_ptr<const SubscribedType>)>;
|
||||
using SharedConstPtrROSMessageCallback =
|
||||
std::function<void (std::shared_ptr<const ROSMessageType>)>;
|
||||
using SharedConstPtrWithInfoCallback =
|
||||
std::function<void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>;
|
||||
std::function<void (
|
||||
std::shared_ptr<const SubscribedType>,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
using SharedConstPtrWithInfoROSMessageCallback =
|
||||
std::function<void (
|
||||
std::shared_ptr<const ROSMessageType>,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
using SharedConstPtrSerializedMessageCallback =
|
||||
std::function<void (std::shared_ptr<const rclcpp::SerializedMessage>)>;
|
||||
using SharedConstPtrSerializedMessageWithInfoCallback =
|
||||
std::function<void (
|
||||
std::shared_ptr<const rclcpp::SerializedMessage>,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
|
||||
using ConstRefSharedConstPtrCallback =
|
||||
std::function<void (const std::shared_ptr<const MessageT> &)>;
|
||||
std::function<void (const std::shared_ptr<const SubscribedType> &)>;
|
||||
using ConstRefSharedConstPtrROSMessageCallback =
|
||||
std::function<void (const std::shared_ptr<const ROSMessageType> &)>;
|
||||
using ConstRefSharedConstPtrWithInfoCallback =
|
||||
std::function<void (const std::shared_ptr<const MessageT> &, const rclcpp::MessageInfo &)>;
|
||||
std::function<void (
|
||||
const std::shared_ptr<const SubscribedType> &,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
using ConstRefSharedConstPtrWithInfoROSMessageCallback =
|
||||
std::function<void (
|
||||
const std::shared_ptr<const ROSMessageType> &,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
using ConstRefSharedConstPtrSerializedMessageCallback =
|
||||
std::function<void (const std::shared_ptr<const rclcpp::SerializedMessage> &)>;
|
||||
using ConstRefSharedConstPtrSerializedMessageWithInfoCallback =
|
||||
std::function<void (
|
||||
const std::shared_ptr<const rclcpp::SerializedMessage> &,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
|
||||
// Deprecated signatures:
|
||||
using SharedPtrCallback =
|
||||
std::function<void (std::shared_ptr<MessageT>)>;
|
||||
std::function<void (std::shared_ptr<SubscribedType>)>;
|
||||
using SharedPtrROSMessageCallback =
|
||||
std::function<void (std::shared_ptr<ROSMessageType>)>;
|
||||
using SharedPtrWithInfoCallback =
|
||||
std::function<void (std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>;
|
||||
std::function<void (std::shared_ptr<SubscribedType>, const rclcpp::MessageInfo &)>;
|
||||
using SharedPtrWithInfoROSMessageCallback =
|
||||
std::function<void (
|
||||
std::shared_ptr<ROSMessageType>,
|
||||
const rclcpp::MessageInfo &)>;
|
||||
using SharedPtrSerializedMessageCallback =
|
||||
std::function<void (std::shared_ptr<rclcpp::SerializedMessage>)>;
|
||||
using SharedPtrSerializedMessageWithInfoCallback =
|
||||
std::function<void (std::shared_ptr<rclcpp::SerializedMessage>, const rclcpp::MessageInfo &)>;
|
||||
};
|
||||
|
||||
/// Template helper to select the variant type based on whether or not MessageT is a TypeAdapter.
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT,
|
||||
bool is_adapted_type = rclcpp::TypeAdapter<MessageT>::is_specialized::value
|
||||
>
|
||||
struct AnySubscriptionCallbackHelper;
|
||||
|
||||
/// Specialization for when MessageT is not a TypeAdapter.
|
||||
template<typename MessageT, typename AllocatorT>
|
||||
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, false>
|
||||
{
|
||||
using CallbackTypes = AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
|
||||
|
||||
using variant_type = std::variant<
|
||||
ConstRefCallback,
|
||||
ConstRefWithInfoCallback,
|
||||
UniquePtrCallback,
|
||||
UniquePtrWithInfoCallback,
|
||||
SharedConstPtrCallback,
|
||||
SharedConstPtrWithInfoCallback,
|
||||
ConstRefSharedConstPtrCallback,
|
||||
ConstRefSharedConstPtrWithInfoCallback,
|
||||
SharedPtrCallback,
|
||||
SharedPtrWithInfoCallback
|
||||
typename CallbackTypes::ConstRefCallback,
|
||||
typename CallbackTypes::ConstRefWithInfoCallback,
|
||||
typename CallbackTypes::ConstRefSerializedMessageCallback,
|
||||
typename CallbackTypes::ConstRefSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::UniquePtrCallback,
|
||||
typename CallbackTypes::UniquePtrWithInfoCallback,
|
||||
typename CallbackTypes::UniquePtrSerializedMessageCallback,
|
||||
typename CallbackTypes::UniquePtrSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::SharedConstPtrCallback,
|
||||
typename CallbackTypes::SharedConstPtrWithInfoCallback,
|
||||
typename CallbackTypes::SharedConstPtrSerializedMessageCallback,
|
||||
typename CallbackTypes::SharedConstPtrSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrWithInfoCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::SharedPtrCallback,
|
||||
typename CallbackTypes::SharedPtrWithInfoCallback,
|
||||
typename CallbackTypes::SharedPtrSerializedMessageCallback,
|
||||
typename CallbackTypes::SharedPtrSerializedMessageWithInfoCallback
|
||||
>;
|
||||
};
|
||||
|
||||
/// Specialization for when MessageT is a TypeAdapter.
|
||||
template<typename MessageT, typename AllocatorT>
|
||||
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, true>
|
||||
{
|
||||
using CallbackTypes = AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
|
||||
|
||||
using variant_type = std::variant<
|
||||
typename CallbackTypes::ConstRefCallback,
|
||||
typename CallbackTypes::ConstRefROSMessageCallback,
|
||||
typename CallbackTypes::ConstRefWithInfoCallback,
|
||||
typename CallbackTypes::ConstRefWithInfoROSMessageCallback,
|
||||
typename CallbackTypes::ConstRefSerializedMessageCallback,
|
||||
typename CallbackTypes::ConstRefSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::UniquePtrCallback,
|
||||
typename CallbackTypes::UniquePtrROSMessageCallback,
|
||||
typename CallbackTypes::UniquePtrWithInfoCallback,
|
||||
typename CallbackTypes::UniquePtrWithInfoROSMessageCallback,
|
||||
typename CallbackTypes::UniquePtrSerializedMessageCallback,
|
||||
typename CallbackTypes::UniquePtrSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::SharedConstPtrCallback,
|
||||
typename CallbackTypes::SharedConstPtrROSMessageCallback,
|
||||
typename CallbackTypes::SharedConstPtrWithInfoCallback,
|
||||
typename CallbackTypes::SharedConstPtrWithInfoROSMessageCallback,
|
||||
typename CallbackTypes::SharedConstPtrSerializedMessageCallback,
|
||||
typename CallbackTypes::SharedConstPtrSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrROSMessageCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrWithInfoCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrWithInfoROSMessageCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageCallback,
|
||||
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageWithInfoCallback,
|
||||
typename CallbackTypes::SharedPtrCallback,
|
||||
typename CallbackTypes::SharedPtrROSMessageCallback,
|
||||
typename CallbackTypes::SharedPtrWithInfoCallback,
|
||||
typename CallbackTypes::SharedPtrWithInfoROSMessageCallback,
|
||||
typename CallbackTypes::SharedPtrSerializedMessageCallback,
|
||||
typename CallbackTypes::SharedPtrSerializedMessageWithInfoCallback
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -101,49 +241,124 @@ template<
|
||||
class AnySubscriptionCallback
|
||||
{
|
||||
private:
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using SubscribedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
|
||||
|
||||
using HelperT = typename rclcpp::detail::AnySubscriptionCallbackHelper<MessageT, AllocatorT>;
|
||||
using MessageDeleterHelper = rclcpp::detail::MessageDeleterHelper<MessageT, AllocatorT>;
|
||||
|
||||
using MessageAllocTraits = typename MessageDeleterHelper::MessageAllocTraits;
|
||||
using MessageAlloc = typename MessageDeleterHelper::MessageAlloc;
|
||||
using MessageDeleter = typename MessageDeleterHelper::MessageDeleter;
|
||||
using SubscribedTypeDeleterHelper =
|
||||
rclcpp::detail::MessageDeleterHelper<SubscribedType, AllocatorT>;
|
||||
using SubscribedTypeAllocatorTraits = typename SubscribedTypeDeleterHelper::AllocTraits;
|
||||
using SubscribedTypeAllocator = typename SubscribedTypeDeleterHelper::Alloc;
|
||||
using SubscribedTypeDeleter = typename SubscribedTypeDeleterHelper::Deleter;
|
||||
|
||||
// See AnySubscriptionCallbackHelper for the types of these.
|
||||
using ConstRefCallback = typename HelperT::ConstRefCallback;
|
||||
using ConstRefWithInfoCallback = typename HelperT::ConstRefWithInfoCallback;
|
||||
using ROSMessageTypeDeleterHelper =
|
||||
rclcpp::detail::MessageDeleterHelper<ROSMessageType, AllocatorT>;
|
||||
using ROSMessageTypeAllocatorTraits = typename ROSMessageTypeDeleterHelper::AllocTraits;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeDeleterHelper::Alloc;
|
||||
using ROSMessageTypeDeleter = typename ROSMessageTypeDeleterHelper::Deleter;
|
||||
|
||||
using UniquePtrCallback = typename HelperT::UniquePtrCallback;
|
||||
using UniquePtrWithInfoCallback = typename HelperT::UniquePtrWithInfoCallback;
|
||||
using SerializedMessageDeleterHelper =
|
||||
rclcpp::detail::MessageDeleterHelper<rclcpp::SerializedMessage, AllocatorT>;
|
||||
using SerializedMessageAllocatorTraits = typename SerializedMessageDeleterHelper::AllocTraits;
|
||||
using SerializedMessageAllocator = typename SerializedMessageDeleterHelper::Alloc;
|
||||
using SerializedMessageDeleter = typename SerializedMessageDeleterHelper::Deleter;
|
||||
|
||||
using SharedConstPtrCallback = typename HelperT::SharedConstPtrCallback;
|
||||
using SharedConstPtrWithInfoCallback = typename HelperT::SharedConstPtrWithInfoCallback;
|
||||
// See AnySubscriptionCallbackPossibleTypes for the types of these.
|
||||
using CallbackTypes = detail::AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
|
||||
|
||||
using ConstRefCallback =
|
||||
typename CallbackTypes::ConstRefCallback;
|
||||
using ConstRefROSMessageCallback =
|
||||
typename CallbackTypes::ConstRefROSMessageCallback;
|
||||
using ConstRefWithInfoCallback =
|
||||
typename CallbackTypes::ConstRefWithInfoCallback;
|
||||
using ConstRefWithInfoROSMessageCallback =
|
||||
typename CallbackTypes::ConstRefWithInfoROSMessageCallback;
|
||||
using ConstRefSerializedMessageCallback =
|
||||
typename CallbackTypes::ConstRefSerializedMessageCallback;
|
||||
using ConstRefSerializedMessageWithInfoCallback =
|
||||
typename CallbackTypes::ConstRefSerializedMessageWithInfoCallback;
|
||||
using UniquePtrCallback =
|
||||
typename CallbackTypes::UniquePtrCallback;
|
||||
using UniquePtrROSMessageCallback =
|
||||
typename CallbackTypes::UniquePtrROSMessageCallback;
|
||||
using UniquePtrWithInfoCallback =
|
||||
typename CallbackTypes::UniquePtrWithInfoCallback;
|
||||
using UniquePtrWithInfoROSMessageCallback =
|
||||
typename CallbackTypes::UniquePtrWithInfoROSMessageCallback;
|
||||
using UniquePtrSerializedMessageCallback =
|
||||
typename CallbackTypes::UniquePtrSerializedMessageCallback;
|
||||
using UniquePtrSerializedMessageWithInfoCallback =
|
||||
typename CallbackTypes::UniquePtrSerializedMessageWithInfoCallback;
|
||||
using SharedConstPtrCallback =
|
||||
typename CallbackTypes::SharedConstPtrCallback;
|
||||
using SharedConstPtrROSMessageCallback =
|
||||
typename CallbackTypes::SharedConstPtrROSMessageCallback;
|
||||
using SharedConstPtrWithInfoCallback =
|
||||
typename CallbackTypes::SharedConstPtrWithInfoCallback;
|
||||
using SharedConstPtrWithInfoROSMessageCallback =
|
||||
typename CallbackTypes::SharedConstPtrWithInfoROSMessageCallback;
|
||||
using SharedConstPtrSerializedMessageCallback =
|
||||
typename CallbackTypes::SharedConstPtrSerializedMessageCallback;
|
||||
using SharedConstPtrSerializedMessageWithInfoCallback =
|
||||
typename CallbackTypes::SharedConstPtrSerializedMessageWithInfoCallback;
|
||||
using ConstRefSharedConstPtrCallback =
|
||||
typename HelperT::ConstRefSharedConstPtrCallback;
|
||||
typename CallbackTypes::ConstRefSharedConstPtrCallback;
|
||||
using ConstRefSharedConstPtrROSMessageCallback =
|
||||
typename CallbackTypes::ConstRefSharedConstPtrROSMessageCallback;
|
||||
using ConstRefSharedConstPtrWithInfoCallback =
|
||||
typename HelperT::ConstRefSharedConstPtrWithInfoCallback;
|
||||
typename CallbackTypes::ConstRefSharedConstPtrWithInfoCallback;
|
||||
using ConstRefSharedConstPtrWithInfoROSMessageCallback =
|
||||
typename CallbackTypes::ConstRefSharedConstPtrWithInfoROSMessageCallback;
|
||||
using ConstRefSharedConstPtrSerializedMessageCallback =
|
||||
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageCallback;
|
||||
using ConstRefSharedConstPtrSerializedMessageWithInfoCallback =
|
||||
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageWithInfoCallback;
|
||||
using SharedPtrCallback =
|
||||
typename CallbackTypes::SharedPtrCallback;
|
||||
using SharedPtrROSMessageCallback =
|
||||
typename CallbackTypes::SharedPtrROSMessageCallback;
|
||||
using SharedPtrWithInfoCallback =
|
||||
typename CallbackTypes::SharedPtrWithInfoCallback;
|
||||
using SharedPtrWithInfoROSMessageCallback =
|
||||
typename CallbackTypes::SharedPtrWithInfoROSMessageCallback;
|
||||
using SharedPtrSerializedMessageCallback =
|
||||
typename CallbackTypes::SharedPtrSerializedMessageCallback;
|
||||
using SharedPtrSerializedMessageWithInfoCallback =
|
||||
typename CallbackTypes::SharedPtrSerializedMessageWithInfoCallback;
|
||||
|
||||
using SharedPtrCallback = typename HelperT::SharedPtrCallback;
|
||||
using SharedPtrWithInfoCallback = typename HelperT::SharedPtrWithInfoCallback;
|
||||
template<typename T>
|
||||
struct NotNull
|
||||
{
|
||||
NotNull(const T * pointer_in, const char * msg)
|
||||
: pointer(pointer_in)
|
||||
{
|
||||
if (pointer == nullptr) {
|
||||
throw std::invalid_argument(msg);
|
||||
}
|
||||
}
|
||||
|
||||
const T * pointer;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit
|
||||
AnySubscriptionCallback(const AllocatorT & allocator = AllocatorT()) // NOLINT[runtime/explicit]
|
||||
: subscribed_type_allocator_(allocator),
|
||||
ros_message_type_allocator_(allocator)
|
||||
{
|
||||
message_allocator_ = allocator;
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, &message_allocator_);
|
||||
allocator::set_allocator_for_deleter(&subscribed_type_deleter_, &subscribed_type_allocator_);
|
||||
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
[[deprecated("use AnySubscriptionCallback(const AllocatorT & allocator) instead")]]
|
||||
explicit
|
||||
AnySubscriptionCallback(std::shared_ptr<AllocatorT> allocator) // NOLINT[runtime/explicit]
|
||||
{
|
||||
if (allocator == nullptr) {
|
||||
throw std::runtime_error("invalid allocator");
|
||||
}
|
||||
message_allocator_ = *allocator;
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, &message_allocator_);
|
||||
}
|
||||
: AnySubscriptionCallback(*NotNull<AllocatorT>(allocator.get(), "invalid allocator").pointer)
|
||||
{}
|
||||
|
||||
AnySubscriptionCallback(const AnySubscriptionCallback &) = default;
|
||||
|
||||
@@ -190,40 +405,79 @@ public:
|
||||
}
|
||||
|
||||
/// Function for shared_ptr to non-const MessageT, which is deprecated.
|
||||
template<typename SetT>
|
||||
// TODO(wjwwood): enable this deprecation after Galactic
|
||||
// [[deprecated(
|
||||
// "use 'void (std::shared_ptr<const MessageT>)' instead"
|
||||
// )]]
|
||||
void
|
||||
set_deprecated(std::function<void(std::shared_ptr<MessageT>)> callback)
|
||||
// set(CallbackT callback)
|
||||
set_deprecated(std::function<void(std::shared_ptr<SetT>)> callback)
|
||||
{
|
||||
callback_variant_ = callback;
|
||||
}
|
||||
|
||||
/// Function for shared_ptr to non-const MessageT with MessageInfo, which is deprecated.
|
||||
template<typename SetT>
|
||||
// TODO(wjwwood): enable this deprecation after Galactic
|
||||
// [[deprecated(
|
||||
// "use 'void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)' instead"
|
||||
// )]]
|
||||
void
|
||||
set_deprecated(
|
||||
std::function<void(std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)> callback)
|
||||
set_deprecated(std::function<void(std::shared_ptr<SetT>, const rclcpp::MessageInfo &)> callback)
|
||||
{
|
||||
callback_variant_ = callback;
|
||||
}
|
||||
|
||||
std::unique_ptr<MessageT, MessageDeleter>
|
||||
create_unique_ptr_from_shared_ptr_message(const std::shared_ptr<const MessageT> & message)
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
create_ros_unique_ptr_from_ros_shared_ptr_message(
|
||||
const std::shared_ptr<const ROSMessageType> & message)
|
||||
{
|
||||
auto ptr = MessageAllocTraits::allocate(message_allocator_, 1);
|
||||
MessageAllocTraits::construct(message_allocator_, ptr, *message);
|
||||
return std::unique_ptr<MessageT, MessageDeleter>(ptr, message_deleter_);
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr, *message);
|
||||
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
|
||||
}
|
||||
|
||||
std::unique_ptr<rclcpp::SerializedMessage, SerializedMessageDeleter>
|
||||
create_serialized_message_unique_ptr_from_shared_ptr(
|
||||
const std::shared_ptr<const rclcpp::SerializedMessage> & serialized_message)
|
||||
{
|
||||
auto ptr = SerializedMessageAllocatorTraits::allocate(serialized_message_allocator_, 1);
|
||||
SerializedMessageAllocatorTraits::construct(
|
||||
serialized_message_allocator_, ptr, *serialized_message);
|
||||
return std::unique_ptr<
|
||||
rclcpp::SerializedMessage,
|
||||
SerializedMessageDeleter
|
||||
>(ptr, serialized_message_deleter_);
|
||||
}
|
||||
|
||||
std::unique_ptr<SubscribedType, SubscribedTypeDeleter>
|
||||
create_custom_unique_ptr_from_custom_shared_ptr_message(
|
||||
const std::shared_ptr<const SubscribedType> & message)
|
||||
{
|
||||
auto ptr = SubscribedTypeAllocatorTraits::allocate(subscribed_type_allocator_, 1);
|
||||
SubscribedTypeAllocatorTraits::construct(subscribed_type_allocator_, ptr, *message);
|
||||
return std::unique_ptr<SubscribedType, SubscribedTypeDeleter>(ptr, subscribed_type_deleter_);
|
||||
}
|
||||
|
||||
std::unique_ptr<SubscribedType, SubscribedTypeDeleter>
|
||||
convert_ros_message_to_custom_type_unique_ptr(const ROSMessageType & msg)
|
||||
{
|
||||
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
|
||||
auto ptr = SubscribedTypeAllocatorTraits::allocate(subscribed_type_allocator_, 1);
|
||||
SubscribedTypeAllocatorTraits::construct(subscribed_type_allocator_, ptr);
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_custom(msg, *ptr);
|
||||
return std::unique_ptr<SubscribedType, SubscribedTypeDeleter>(ptr, subscribed_type_deleter_);
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"convert_ros_message_to_custom_type_unique_ptr "
|
||||
"unexpectedly called without TypeAdapter");
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch when input is a ros message and the output could be anything.
|
||||
void
|
||||
dispatch(
|
||||
std::shared_ptr<MessageT> message,
|
||||
std::shared_ptr<ROSMessageType> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
@@ -238,28 +492,162 @@ public:
|
||||
std::visit(
|
||||
[&message, &message_info, this](auto && callback) {
|
||||
using T = std::decay_t<decltype(callback)>;
|
||||
static constexpr bool is_ta = rclcpp::TypeAdapter<MessageT>::is_specialized::value;
|
||||
|
||||
if constexpr (std::is_same_v<T, ConstRefCallback>) {
|
||||
callback(*message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
|
||||
callback(*message, message_info);
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrCallback>) {
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message));
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrWithInfoCallback>) {
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message), message_info);
|
||||
// conditions for output is custom message
|
||||
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
|
||||
// TODO(wjwwood): consider avoiding heap allocation for small messages
|
||||
// maybe something like:
|
||||
// if constexpr (rosidl_generator_traits::has_fixed_size<T> && sizeof(T) < N) {
|
||||
// ... on stack
|
||||
// }
|
||||
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
|
||||
callback(*local_message);
|
||||
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
|
||||
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
|
||||
callback(*local_message, message_info);
|
||||
} else if constexpr (is_ta && std::is_same_v<T, UniquePtrCallback>) {
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
|
||||
} else if constexpr (is_ta && std::is_same_v<T, UniquePtrWithInfoCallback>) {
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>)
|
||||
is_ta && (
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
|
||||
}
|
||||
// conditions for output is ros message
|
||||
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
|
||||
callback(*message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
|
||||
callback(*message, message_info);
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrROSMessageCallback>) {
|
||||
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message));
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>) {
|
||||
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message), message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrROSMessageCallback>)
|
||||
{
|
||||
callback(message);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>)
|
||||
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
|
||||
{
|
||||
callback(message, message_info);
|
||||
} else {
|
||||
}
|
||||
// condition to catch SerializedMessage types
|
||||
else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, ConstRefSerializedMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, UniquePtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Cannot dispatch std::shared_ptr<ROSMessageType> message "
|
||||
"to rclcpp::SerializedMessage");
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
// Dispatch when input is a serialized message and the output could be anything.
|
||||
void
|
||||
dispatch(
|
||||
std::shared_ptr<rclcpp::SerializedMessage> serialized_message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
if (std::get<0>(callback_variant_) == nullptr) {
|
||||
// This can happen if it is default initialized, or if it is assigned nullptr.
|
||||
throw std::runtime_error("dispatch called on an unset AnySubscriptionCallback");
|
||||
}
|
||||
}
|
||||
// Dispatch.
|
||||
std::visit(
|
||||
[&serialized_message, &message_info, this](auto && callback) {
|
||||
using T = std::decay_t<decltype(callback)>;
|
||||
|
||||
// condition to catch SerializedMessage types
|
||||
if constexpr (std::is_same_v<T, ConstRefSerializedMessageCallback>) {
|
||||
callback(*serialized_message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>) {
|
||||
callback(*serialized_message, message_info);
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrSerializedMessageCallback>) {
|
||||
callback(create_serialized_message_unique_ptr_from_shared_ptr(serialized_message));
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>) {
|
||||
callback(
|
||||
create_serialized_message_unique_ptr_from_shared_ptr(serialized_message),
|
||||
message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageCallback>)
|
||||
{
|
||||
callback(create_serialized_message_unique_ptr_from_shared_ptr(serialized_message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
|
||||
{
|
||||
callback(
|
||||
create_serialized_message_unique_ptr_from_shared_ptr(serialized_message),
|
||||
message_info);
|
||||
}
|
||||
// conditions for output anything else
|
||||
else if constexpr ( // NOLINT[whitespace/newline]
|
||||
std::is_same_v<T, ConstRefCallback>||
|
||||
std::is_same_v<T, ConstRefROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, UniquePtrCallback>||
|
||||
std::is_same_v<T, UniquePtrROSMessageCallback>||
|
||||
std::is_same_v<T, UniquePtrWithInfoCallback>||
|
||||
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>||
|
||||
std::is_same_v<T, SharedPtrROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"cannot dispatch rclcpp::SerializedMessage to "
|
||||
"non-rclcpp::SerializedMessage callbacks");
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
@@ -268,7 +656,7 @@ public:
|
||||
|
||||
void
|
||||
dispatch_intra_process(
|
||||
std::shared_ptr<const MessageT> message,
|
||||
std::shared_ptr<const ROSMessageType> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
@@ -283,32 +671,89 @@ public:
|
||||
std::visit(
|
||||
[&message, &message_info, this](auto && callback) {
|
||||
using T = std::decay_t<decltype(callback)>;
|
||||
static constexpr bool is_ta = rclcpp::TypeAdapter<MessageT>::is_specialized::value;
|
||||
|
||||
if constexpr (std::is_same_v<T, ConstRefCallback>) {
|
||||
// conditions for custom type
|
||||
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
|
||||
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
|
||||
callback(*local_message);
|
||||
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
|
||||
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
|
||||
callback(*local_message, message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, UniquePtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, UniquePtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
|
||||
}
|
||||
// conditions for ros message type
|
||||
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
|
||||
callback(*message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
|
||||
callback(*message, message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>)
|
||||
std::is_same_v<T, UniquePtrROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrROSMessageCallback>)
|
||||
{
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message));
|
||||
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>)
|
||||
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
|
||||
{
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message), message_info);
|
||||
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message), message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>)
|
||||
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>)
|
||||
{
|
||||
callback(message);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>)
|
||||
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>)
|
||||
{
|
||||
callback(message, message_info);
|
||||
} else {
|
||||
}
|
||||
// condition to catch SerializedMessage types
|
||||
else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, ConstRefSerializedMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, UniquePtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Cannot dispatch std::shared_ptr<const ROSMessageType> message "
|
||||
"to rclcpp::SerializedMessage");
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
@@ -317,7 +762,7 @@ public:
|
||||
|
||||
void
|
||||
dispatch_intra_process(
|
||||
std::unique_ptr<MessageT, MessageDeleter> message,
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
@@ -330,28 +775,91 @@ public:
|
||||
}
|
||||
// Dispatch.
|
||||
std::visit(
|
||||
[&message, &message_info](auto && callback) {
|
||||
[&message, &message_info, this](auto && callback) {
|
||||
using T = std::decay_t<decltype(callback)>;
|
||||
static constexpr bool is_ta = rclcpp::TypeAdapter<MessageT>::is_specialized::value;
|
||||
|
||||
if constexpr (std::is_same_v<T, ConstRefCallback>) {
|
||||
// conditions for custom type
|
||||
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
|
||||
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
|
||||
callback(*local_message);
|
||||
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
|
||||
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
|
||||
callback(*local_message, message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, UniquePtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, UniquePtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
is_ta && (
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>
|
||||
))
|
||||
{
|
||||
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
|
||||
}
|
||||
// conditions for ros message type
|
||||
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
|
||||
callback(*message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
|
||||
callback(*message, message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrCallback>||
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>)
|
||||
std::is_same_v<T, UniquePtrROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrROSMessageCallback>)
|
||||
{
|
||||
callback(std::move(message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>)
|
||||
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
|
||||
{
|
||||
callback(std::move(message), message_info);
|
||||
} else {
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>)
|
||||
{
|
||||
callback(std::move(message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>)
|
||||
{
|
||||
callback(std::move(message), message_info);
|
||||
}
|
||||
// condition to catch SerializedMessage types
|
||||
else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, ConstRefSerializedMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, UniquePtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, UniquePtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, SharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrSerializedMessageWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageCallback>||
|
||||
std::is_same_v<T, SharedPtrSerializedMessageWithInfoCallback>)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Cannot dispatch std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> message "
|
||||
"to rclcpp::SerializedMessage");
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
@@ -369,6 +877,18 @@ public:
|
||||
std::holds_alternative<ConstRefSharedConstPtrWithInfoCallback>(callback_variant_);
|
||||
}
|
||||
|
||||
constexpr
|
||||
bool
|
||||
is_serialized_message_callback() const
|
||||
{
|
||||
return
|
||||
std::holds_alternative<ConstRefSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<UniquePtrSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<SharedConstPtrSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<ConstRefSharedConstPtrSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<SharedPtrSerializedMessageCallback>(callback_variant_);
|
||||
}
|
||||
|
||||
void
|
||||
register_callback_for_tracing()
|
||||
{
|
||||
@@ -402,8 +922,12 @@ private:
|
||||
// For now, compose the variant into this class as a private attribute.
|
||||
typename HelperT::variant_type callback_variant_;
|
||||
|
||||
MessageAlloc message_allocator_;
|
||||
MessageDeleter message_deleter_;
|
||||
SubscribedTypeAllocator subscribed_type_allocator_;
|
||||
SubscribedTypeDeleter subscribed_type_deleter_;
|
||||
ROSMessageTypeAllocator ros_message_type_allocator_;
|
||||
ROSMessageTypeDeleter ros_message_type_deleter_;
|
||||
SerializedMessageAllocator serialized_message_allocator_;
|
||||
SerializedMessageDeleter serialized_message_deleter_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -47,6 +48,17 @@ public:
|
||||
/// Forward declare WeakContextsWrapper
|
||||
class WeakContextsWrapper;
|
||||
|
||||
class OnShutdownCallbackHandle
|
||||
{
|
||||
friend class Context;
|
||||
|
||||
public:
|
||||
using OnShutdownCallbackType = std::function<void ()>;
|
||||
|
||||
private:
|
||||
std::weak_ptr<OnShutdownCallbackType> callback;
|
||||
};
|
||||
|
||||
/// Context which encapsulates shared state between nodes and other similar entities.
|
||||
/**
|
||||
* A context also represents the lifecycle between init and shutdown of rclcpp.
|
||||
@@ -80,7 +92,7 @@ public:
|
||||
*
|
||||
* Note that this function does not setup any signal handlers, so if you want
|
||||
* it to be shutdown by the signal handler, then you need to either install
|
||||
* them manually with rclcpp::install_signal_handers() or use rclcpp::init().
|
||||
* them manually with rclcpp::install_signal_handlers() or use rclcpp::init().
|
||||
* In addition to installing the signal handlers, the shutdown_on_sigint
|
||||
* of the InitOptions needs to be `true` for this context to be shutdown by
|
||||
* the signal handler, otherwise it will be passed over.
|
||||
@@ -177,7 +189,7 @@ public:
|
||||
bool
|
||||
shutdown(const std::string & reason);
|
||||
|
||||
using OnShutdownCallback = std::function<void ()>;
|
||||
using OnShutdownCallback = OnShutdownCallbackHandle::OnShutdownCallbackType;
|
||||
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
@@ -203,23 +215,47 @@ public:
|
||||
OnShutdownCallback
|
||||
on_shutdown(OnShutdownCallback callback);
|
||||
|
||||
/// Return the shutdown callbacks as const.
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
* These callbacks will be called in the order they are added as the second
|
||||
* to last step in shutdown().
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronously in the dedicated singal handling thread.
|
||||
*
|
||||
* Also, shutdown() may be called from the destructor of this function.
|
||||
* Therefore, it is not safe to throw exceptions from these callbacks.
|
||||
* Instead, log errors or use some other mechanism to indicate an error has
|
||||
* occurred.
|
||||
*
|
||||
* On shutdown callbacks may be registered before init and after shutdown,
|
||||
* and persist on repeated init's.
|
||||
*
|
||||
* \param[in] callback the on_shutdown callback to be registered
|
||||
* \return the created callback handle
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks() const;
|
||||
virtual
|
||||
OnShutdownCallbackHandle
|
||||
add_on_shutdown_callback(OnShutdownCallback callback);
|
||||
|
||||
/// Remove an registered on_shutdown callbacks.
|
||||
/**
|
||||
* \param[in] callback_handle the on_shutdown callback handle to be removed.
|
||||
* \return true if the callback is found and removed, otherwise false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle);
|
||||
|
||||
/// Return the shutdown callbacks.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
* Returned callbacks are a copy of the registered callbacks.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks();
|
||||
std::vector<OnShutdownCallback>
|
||||
get_on_shutdown_callbacks() const;
|
||||
|
||||
/// Return the internal rcl context.
|
||||
RCLCPP_PUBLIC
|
||||
@@ -299,8 +335,8 @@ private:
|
||||
// attempt to acquire another sub context.
|
||||
std::recursive_mutex sub_contexts_mutex_;
|
||||
|
||||
std::vector<OnShutdownCallback> on_shutdown_callbacks_;
|
||||
std::mutex on_shutdown_callbacks_mutex_;
|
||||
std::unordered_set<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
|
||||
mutable std::mutex on_shutdown_callbacks_mutex_;
|
||||
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
std::condition_variable interrupt_condition_variable_;
|
||||
|
||||
@@ -47,11 +47,11 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT,
|
||||
typename NodeParametersT,
|
||||
typename NodeTopicsT>
|
||||
typename NodeTopicsT,
|
||||
typename ROSMessageType = typename SubscriptionT::ROSMessageType>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
NodeParametersT & node_parameters,
|
||||
@@ -70,7 +70,7 @@ create_subscription(
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics_interface = get_node_topics_interface(node_topics);
|
||||
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
|
||||
subscription_topic_stats = nullptr;
|
||||
|
||||
if (rclcpp::detail::resolve_enable_topic_statistics(
|
||||
@@ -92,11 +92,11 @@ create_subscription(
|
||||
qos);
|
||||
|
||||
subscription_topic_stats = std::make_shared<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
|
||||
>(node_topics_interface->get_node_base_interface()->get_name(), publisher);
|
||||
|
||||
std::weak_ptr<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
|
||||
> weak_subscription_topic_stats(subscription_topic_stats);
|
||||
auto sub_call_back = [weak_subscription_topic_stats]() {
|
||||
auto subscription_topic_stats = weak_subscription_topic_stats.lock();
|
||||
@@ -153,7 +153,6 @@ create_subscription(
|
||||
* \tparam MessageT
|
||||
* \tparam CallbackT
|
||||
* \tparam AllocatorT
|
||||
* \tparam CallbackMessageT
|
||||
* \tparam SubscriptionT
|
||||
* \tparam MessageMemoryStrategyT
|
||||
* \tparam NodeT
|
||||
@@ -171,13 +170,8 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>,
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
|
||||
typename NodeT>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
@@ -194,7 +188,7 @@ create_subscription(
|
||||
)
|
||||
{
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node, node, topic_name, qos, std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
@@ -206,13 +200,8 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & node_parameters,
|
||||
@@ -229,7 +218,7 @@ create_subscription(
|
||||
)
|
||||
{
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node_parameters, node_topics, topic_name, qos,
|
||||
std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
@@ -282,8 +282,8 @@ class ParameterModifiedInCallbackException : public std::runtime_error
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown when a parameter override wasn't provided and one was required.
|
||||
class NoParameterOverrideProvided : public std::runtime_error
|
||||
/// Thrown when an uninitialized parameter is accessed.
|
||||
class ParameterUninitializedException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
/// Construct an instance.
|
||||
@@ -291,8 +291,8 @@ public:
|
||||
* \param[in] name the name of the parameter.
|
||||
* \param[in] message custom exception message.
|
||||
*/
|
||||
explicit NoParameterOverrideProvided(const std::string & name)
|
||||
: std::runtime_error("parameter '" + name + "' requires an user provided parameter override")
|
||||
explicit ParameterUninitializedException(const std::string & name)
|
||||
: std::runtime_error("parameter '" + name + "' is not initialized")
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/executor_options.hpp"
|
||||
@@ -571,6 +572,9 @@ protected:
|
||||
/// nodes that are associated with the executor
|
||||
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>
|
||||
weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// shutdown callback handle registered to Context
|
||||
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/buffers/ring_buffer_implementation.hpp"
|
||||
#include "rclcpp/intra_process_buffer_type.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -37,13 +38,13 @@ template<
|
||||
typename rclcpp::experimental::buffers::IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr
|
||||
create_intra_process_buffer(
|
||||
IntraProcessBufferType buffer_type,
|
||||
rmw_qos_profile_t qos,
|
||||
const rclcpp::QoS & qos,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
using MessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
size_t buffer_size = qos.depth;
|
||||
size_t buffer_size = qos.depth();
|
||||
|
||||
using rclcpp::experimental::buffers::IntraProcessBuffer;
|
||||
typename IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr buffer;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -181,7 +182,7 @@ public:
|
||||
do_intra_process_publish(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
|
||||
@@ -202,7 +203,8 @@ public:
|
||||
// None of the buffers require ownership, so we promote the pointer
|
||||
std::shared_ptr<MessageT> msg = std::move(message);
|
||||
|
||||
this->template add_shared_msg_to_buffers<MessageT>(msg, sub_ids.take_shared_subscriptions);
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
msg, sub_ids.take_shared_subscriptions);
|
||||
} else if (!sub_ids.take_ownership_subscriptions.empty() && // NOLINT
|
||||
sub_ids.take_shared_subscriptions.size() <= 1)
|
||||
{
|
||||
@@ -225,9 +227,9 @@ public:
|
||||
{
|
||||
// Construct a new shared pointer from the message
|
||||
// for the buffers that do not require ownership
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
|
||||
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
shared_msg, sub_ids.take_shared_subscriptions);
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
std::move(message), sub_ids.take_ownership_subscriptions, allocator);
|
||||
@@ -242,7 +244,7 @@ public:
|
||||
do_intra_process_publish_and_return_shared(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
|
||||
@@ -263,17 +265,17 @@ public:
|
||||
// If there are no owning, just convert to shared.
|
||||
std::shared_ptr<MessageT> shared_msg = std::move(message);
|
||||
if (!sub_ids.take_shared_subscriptions.empty()) {
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
shared_msg, sub_ids.take_shared_subscriptions);
|
||||
}
|
||||
return shared_msg;
|
||||
} else {
|
||||
// Construct a new shared pointer from the message for the buffers that
|
||||
// do not require ownership and to return.
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(allocator, *message);
|
||||
|
||||
if (!sub_ids.take_shared_subscriptions.empty()) {
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
shared_msg,
|
||||
sub_ids.take_shared_subscriptions);
|
||||
}
|
||||
@@ -303,25 +305,6 @@ public:
|
||||
get_subscription_intra_process(uint64_t intra_process_subscription_id);
|
||||
|
||||
private:
|
||||
struct SubscriptionInfo
|
||||
{
|
||||
SubscriptionInfo() = default;
|
||||
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr subscription;
|
||||
rmw_qos_profile_t qos;
|
||||
const char * topic_name;
|
||||
bool use_take_shared_method;
|
||||
};
|
||||
|
||||
struct PublisherInfo
|
||||
{
|
||||
PublisherInfo() = default;
|
||||
|
||||
rclcpp::PublisherBase::WeakPtr publisher;
|
||||
rmw_qos_profile_t qos;
|
||||
const char * topic_name;
|
||||
};
|
||||
|
||||
struct SplittedSubscriptions
|
||||
{
|
||||
std::vector<uint64_t> take_shared_subscriptions;
|
||||
@@ -329,10 +312,10 @@ private:
|
||||
};
|
||||
|
||||
using SubscriptionMap =
|
||||
std::unordered_map<uint64_t, SubscriptionInfo>;
|
||||
std::unordered_map<uint64_t, rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr>;
|
||||
|
||||
using PublisherMap =
|
||||
std::unordered_map<uint64_t, PublisherInfo>;
|
||||
std::unordered_map<uint64_t, rclcpp::PublisherBase::WeakPtr>;
|
||||
|
||||
using PublisherToSubscriptionIdsMap =
|
||||
std::unordered_map<uint64_t, SplittedSubscriptions>;
|
||||
@@ -348,9 +331,14 @@ private:
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
can_communicate(PublisherInfo pub_info, SubscriptionInfo sub_info) const;
|
||||
can_communicate(
|
||||
rclcpp::PublisherBase::SharedPtr pub,
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const;
|
||||
|
||||
template<typename MessageT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc,
|
||||
typename Deleter>
|
||||
void
|
||||
add_shared_msg_to_buffers(
|
||||
std::shared_ptr<const MessageT> message,
|
||||
@@ -361,11 +349,18 @@ private:
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
throw std::runtime_error("subscription has unexpectedly gone out of scope");
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription.lock();
|
||||
auto subscription_base = subscription_it->second.lock();
|
||||
if (subscription_base) {
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, which "
|
||||
"can happen when the publisher and subscription use different "
|
||||
"allocator types, which is not supported");
|
||||
}
|
||||
|
||||
subscription->provide_intra_process_message(message);
|
||||
} else {
|
||||
@@ -382,7 +377,7 @@ private:
|
||||
add_owned_msg_to_buffers(
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::vector<uint64_t> subscription_ids,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
@@ -392,11 +387,18 @@ private:
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
throw std::runtime_error("subscription has unexpectedly gone out of scope");
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription.lock();
|
||||
auto subscription_base = subscription_it->second.lock();
|
||||
if (subscription_base) {
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, which "
|
||||
"can happen when the publisher and subscription use different "
|
||||
"allocator types, which is not supported");
|
||||
}
|
||||
|
||||
if (std::next(it) == subscription_ids.end()) {
|
||||
// If this is the last subscription, give up ownership
|
||||
@@ -405,8 +407,8 @@ private:
|
||||
// Copy the message since we have additional subscriptions to serve
|
||||
MessageUniquePtr copy_message;
|
||||
Deleter deleter = message.get_deleter();
|
||||
auto ptr = MessageAllocTraits::allocate(*allocator.get(), 1);
|
||||
MessageAllocTraits::construct(*allocator.get(), ptr, *message);
|
||||
auto ptr = MessageAllocTraits::allocate(allocator, 1);
|
||||
MessageAllocTraits::construct(allocator, ptr, *message);
|
||||
copy_message = MessageUniquePtr(ptr, deleter);
|
||||
|
||||
subscription->provide_intra_process_message(std::move(copy_message));
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
@@ -44,54 +46,43 @@ template<
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>,
|
||||
typename CallbackMessageT = MessageT>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
|
||||
class SubscriptionIntraProcess
|
||||
: public SubscriptionIntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>::UniquePtr;
|
||||
>
|
||||
{
|
||||
using SubscriptionIntraProcessBufferT = SubscriptionIntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
using MessageAllocTraits = typename SubscriptionIntraProcessBufferT::MessageAllocTraits;
|
||||
using MessageAlloc = typename SubscriptionIntraProcessBufferT::MessageAlloc;
|
||||
using ConstMessageSharedPtr = typename SubscriptionIntraProcessBufferT::ConstMessageSharedPtr;
|
||||
using MessageUniquePtr = typename SubscriptionIntraProcessBufferT::MessageUniquePtr;
|
||||
using BufferUniquePtr = typename SubscriptionIntraProcessBufferT::BufferUniquePtr;
|
||||
|
||||
SubscriptionIntraProcess(
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
rmw_qos_profile_t qos_profile,
|
||||
const rclcpp::QoS & qos_profile,
|
||||
rclcpp::IntraProcessBufferType buffer_type)
|
||||
: SubscriptionIntraProcessBase(topic_name, qos_profile),
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(
|
||||
allocator,
|
||||
context,
|
||||
topic_name,
|
||||
qos_profile,
|
||||
buffer_type),
|
||||
any_callback_(callback)
|
||||
{
|
||||
if (!std::is_same<MessageT, CallbackMessageT>::value) {
|
||||
throw std::runtime_error("SubscriptionIntraProcess wrong callback type");
|
||||
}
|
||||
|
||||
// Create the intra-process buffer.
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
allocator);
|
||||
|
||||
// Create the guard condition.
|
||||
rcl_guard_condition_options_t guard_condition_options =
|
||||
rcl_guard_condition_get_default_options();
|
||||
|
||||
gc_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&gc_, context->get_rcl_context().get(), guard_condition_options);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw std::runtime_error("SubscriptionIntraProcess init error initializing guard condition");
|
||||
}
|
||||
|
||||
TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
static_cast<const void *>(this),
|
||||
@@ -104,22 +95,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
~SubscriptionIntraProcess()
|
||||
{
|
||||
if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Failed to destroy guard condition: %s",
|
||||
rcutils_get_error_string().str);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
(void) wait_set;
|
||||
return buffer_->has_data();
|
||||
}
|
||||
virtual ~SubscriptionIntraProcess() = default;
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data()
|
||||
@@ -128,9 +104,9 @@ public:
|
||||
MessageUniquePtr unique_msg;
|
||||
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
shared_msg = buffer_->consume_shared();
|
||||
shared_msg = this->buffer_->consume_shared();
|
||||
} else {
|
||||
unique_msg = buffer_->consume_unique();
|
||||
unique_msg = this->buffer_->consume_unique();
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
|
||||
@@ -141,37 +117,10 @@ public:
|
||||
|
||||
void execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
execute_impl<CallbackMessageT>(data);
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(ConstMessageSharedPtr message)
|
||||
{
|
||||
buffer_->add_shared(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(MessageUniquePtr message)
|
||||
{
|
||||
buffer_->add_unique(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
bool
|
||||
use_take_shared_method() const
|
||||
{
|
||||
return buffer_->use_take_shared_method();
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
trigger_guard_condition()
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
|
||||
(void)ret;
|
||||
execute_impl<MessageT>(data);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
|
||||
execute_impl(std::shared_ptr<void> & data)
|
||||
@@ -206,7 +155,6 @@ private:
|
||||
}
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
|
||||
BufferUniquePtr buffer_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
@@ -39,7 +40,9 @@ public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SubscriptionIntraProcessBase(const std::string & topic_name, rmw_qos_profile_t qos_profile)
|
||||
SubscriptionIntraProcessBase(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile)
|
||||
: topic_name_(topic_name), qos_profile_(qos_profile)
|
||||
{}
|
||||
|
||||
@@ -71,7 +74,7 @@ public:
|
||||
get_topic_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rmw_qos_profile_t
|
||||
QoS
|
||||
get_actual_qos() const;
|
||||
|
||||
protected:
|
||||
@@ -83,7 +86,7 @@ private:
|
||||
trigger_guard_condition() = 0;
|
||||
|
||||
std::string topic_name_;
|
||||
rmw_qos_profile_t qos_profile_;
|
||||
QoS qos_profile_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>
|
||||
>
|
||||
class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
|
||||
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>::UniquePtr;
|
||||
|
||||
SubscriptionIntraProcessBuffer(
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile,
|
||||
rclcpp::IntraProcessBufferType buffer_type)
|
||||
: SubscriptionIntraProcessBase(topic_name, qos_profile)
|
||||
{
|
||||
// Create the intra-process buffer.
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
allocator);
|
||||
|
||||
// Create the guard condition.
|
||||
rcl_guard_condition_options_t guard_condition_options =
|
||||
rcl_guard_condition_get_default_options();
|
||||
|
||||
gc_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&gc_, context->get_rcl_context().get(), guard_condition_options);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw std::runtime_error(
|
||||
"SubscriptionIntraProcessBuffer init error initializing guard condition");
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~SubscriptionIntraProcessBuffer()
|
||||
{
|
||||
if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Failed to destroy guard condition: %s",
|
||||
rcutils_get_error_string().str);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
(void) wait_set;
|
||||
return buffer_->has_data();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(ConstMessageSharedPtr message)
|
||||
{
|
||||
buffer_->add_shared(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(MessageUniquePtr message)
|
||||
{
|
||||
buffer_->add_unique(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
bool
|
||||
use_take_shared_method() const
|
||||
{
|
||||
return buffer_->use_take_shared_method();
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
trigger_guard_condition()
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
|
||||
(void)ret;
|
||||
}
|
||||
|
||||
BufferUniquePtr buffer_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
@@ -136,6 +136,13 @@ public:
|
||||
void handle_message(
|
||||
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
/// Handle dispatching rclcpp::SerializedMessage to user callback.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
/// This function is currently not implemented.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_loaned_message(
|
||||
|
||||
90
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal file
90
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
#define RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
|
||||
#include "rosidl_typesupport_cpp/message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
/// Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Specialization for when MessageT is an actual ROS message type.
|
||||
template<typename MessageT>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<MessageT>::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>();
|
||||
if (!handle) {
|
||||
throw std::runtime_error("Type support handle unexpectedly nullptr");
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
/// Specialization for when MessageT is an adapted type using rclcpp::TypeAdapter.
|
||||
template<typename AdaptedType>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<AdaptedType>::value &&
|
||||
rclcpp::TypeAdapter<AdaptedType>::is_specialized::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
typename TypeAdapter<AdaptedType>::ros_message_type
|
||||
>();
|
||||
if (!handle) {
|
||||
throw std::runtime_error("Type support handle unexpectedly nullptr");
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
/// Specialization for when MessageT is not a ROS message nor an adapted type.
|
||||
/**
|
||||
* This specialization is a pass through runtime check, which allows a better
|
||||
* static_assert to catch this issue further down the line.
|
||||
* This should never get to be called in practice, and is purely defensive.
|
||||
*/
|
||||
template<
|
||||
typename AdaptedType
|
||||
>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<AdaptedType>::value &&
|
||||
!TypeAdapter<AdaptedType>::is_specialized::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"this specialization of rclcpp::get_message_type_support_handle() "
|
||||
"should never be called");
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal file
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
#define RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct is_ros_compatible_type
|
||||
{
|
||||
static constexpr bool value =
|
||||
rosidl_generator_traits::is_message<T>::value ||
|
||||
rclcpp::TypeAdapter<T>::is_specialized::value;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
@@ -103,6 +103,9 @@ public:
|
||||
* \param[in] allocator Allocator instance in case middleware can not allocate messages
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
[[
|
||||
deprecated("used the LoanedMessage constructor that does not use a shared_ptr to the allocator")
|
||||
]]
|
||||
LoanedMessage(
|
||||
const rclcpp::PublisherBase * pub,
|
||||
std::shared_ptr<std::allocator<MessageT>> allocator)
|
||||
|
||||
@@ -206,13 +206,8 @@ public:
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType
|
||||
>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
@@ -984,12 +979,15 @@ public:
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_service_names_and_types() const;
|
||||
|
||||
/// Return the number of publishers that are advertised on a given topic.
|
||||
/// Return a map of existing service names to list of service types for a specific node.
|
||||
/**
|
||||
* \param[in] node_name the node_name on which to count the publishers.
|
||||
* \param[in] namespace_ the namespace of the node associated with the name
|
||||
* \return number of publishers that are advertised on a given topic.
|
||||
* \throws std::runtime_error if publishers could not be counted
|
||||
* This function only considers services - not clients.
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
*
|
||||
* \param[in] node_name name of the node.
|
||||
* \param[in] namespace_ namespace of the node.
|
||||
* \return a map of existing service names to list of service types.
|
||||
* \throws std::runtime_error anything that rcl_error can throw.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
|
||||
@@ -86,7 +86,6 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(const std::vector<rclcpp::Parameter> & parameters) = 0;
|
||||
|
||||
/// Set and initialize a parameter, all at once.
|
||||
/// Set one or more parameters, all at once.
|
||||
/**
|
||||
* \sa rclcpp::Node::set_parameters_atomically
|
||||
*/
|
||||
|
||||
@@ -301,6 +301,16 @@ public:
|
||||
return get<ParameterType::PARAMETER_BOOL_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<int> &>::value, const std::vector<int64_t> &>::type
|
||||
get() const
|
||||
{
|
||||
return get<ParameterType::PARAMETER_INTEGER_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
@@ -311,6 +321,16 @@ public:
|
||||
return get<ParameterType::PARAMETER_INTEGER_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<float> &>::value, const std::vector<double> &>::type
|
||||
get() const
|
||||
{
|
||||
return get<ParameterType::PARAMETER_DOUBLE_ARRAY>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
constexpr
|
||||
typename std::enable_if<
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
#ifndef RCLCPP__PUBLISHER_HPP_
|
||||
#define RCLCPP__PUBLISHER_HPP_
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -27,16 +24,21 @@
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/publisher.h"
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/detail/resolve_use_intra_process.hpp"
|
||||
#include "rclcpp/experimental/intra_process_manager.hpp"
|
||||
#include "rclcpp/get_message_type_support_handle.hpp"
|
||||
#include "rclcpp/is_ros_compatible_type.hpp"
|
||||
#include "rclcpp/loaned_message.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -49,15 +51,62 @@ template<typename MessageT, typename AllocatorT>
|
||||
class LoanedMessage;
|
||||
|
||||
/// A publisher publishes messages of any type to a topic.
|
||||
/**
|
||||
* MessageT must be a:
|
||||
*
|
||||
* - ROS message type with its own type support (e.g. std_msgs::msgs::String), or a
|
||||
* - rclcpp::TypeAdapter<CustomType, ROSMessageType>
|
||||
* (e.g. rclcpp::TypeAdapter<std::string, std_msgs::msg::String), or a
|
||||
* - custom type that has been setup as the implicit type for a ROS type using
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(custom_type, ros_message_type)
|
||||
*
|
||||
* In the case that MessageT is ROS message type (e.g. std_msgs::msg::String),
|
||||
* both PublishedType and ROSMessageType will be that type.
|
||||
* In the case that MessageT is a TypeAdapter<CustomType, ROSMessageType> type
|
||||
* (e.g. TypeAdapter<std::string, std_msgs::msg::String>), PublishedType will
|
||||
* be the custom type, and ROSMessageType will be the ros message type.
|
||||
*
|
||||
* This is achieved because of the "identity specialization" for TypeAdapter,
|
||||
* which returns itself if it is already a TypeAdapter, and the default
|
||||
* specialization which allows ROSMessageType to be void.
|
||||
* \sa rclcpp::TypeAdapter for more details.
|
||||
*/
|
||||
template<typename MessageT, typename AllocatorT = std::allocator<void>>
|
||||
class Publisher : public PublisherBase
|
||||
{
|
||||
public:
|
||||
using MessageAllocatorTraits = allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAllocator, MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
using MessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
static_assert(
|
||||
rclcpp::is_ros_compatible_type<MessageT>::value,
|
||||
"given message type is not compatible with ROS and cannot be used with a Publisher");
|
||||
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
|
||||
|
||||
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, AllocatorT>;
|
||||
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
|
||||
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits
|
||||
[[deprecated("use PublishedTypeAllocatorTraits")]] =
|
||||
PublishedTypeAllocatorTraits;
|
||||
using MessageAllocator
|
||||
[[deprecated("use PublishedTypeAllocator")]] =
|
||||
PublishedTypeAllocator;
|
||||
using MessageDeleter
|
||||
[[deprecated("use PublishedTypeDeleter")]] =
|
||||
PublishedTypeDeleter;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<PublishedType, PublishedTypeDeleter>")]] =
|
||||
std::unique_ptr<PublishedType, PublishedTypeDeleter>;
|
||||
using MessageSharedPtr
|
||||
[[deprecated("use std::shared_ptr<const PublishedType>")]] =
|
||||
std::shared_ptr<const PublishedType>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, AllocatorT>)
|
||||
|
||||
@@ -80,12 +129,14 @@ public:
|
||||
: PublisherBase(
|
||||
node_base,
|
||||
topic,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
rclcpp::get_message_type_support_handle<MessageT>(),
|
||||
options.template to_rcl_publisher_options<MessageT>(qos)),
|
||||
options_(options),
|
||||
message_allocator_(new MessageAllocator(*options.get_allocator().get()))
|
||||
published_type_allocator_(*options.get_allocator()),
|
||||
ros_message_type_allocator_(*options.get_allocator())
|
||||
{
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
|
||||
allocator::set_allocator_for_deleter(&published_type_deleter_, &published_type_allocator_);
|
||||
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
|
||||
|
||||
if (options_.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
@@ -135,15 +186,15 @@ public:
|
||||
// Get the intra process manager instance for this context.
|
||||
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
|
||||
// Register the publisher with the intra process manager.
|
||||
if (qos.get_rmw_qos_profile().history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
|
||||
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with keep all history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos.get_rmw_qos_profile().depth == 0) {
|
||||
if (qos.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with a zero qos history depth value");
|
||||
}
|
||||
if (qos.get_rmw_qos_profile().durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
|
||||
if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with volatile durability");
|
||||
}
|
||||
@@ -170,21 +221,33 @@ public:
|
||||
* allocator.
|
||||
* \sa rclcpp::LoanedMessage for details of the LoanedMessage class.
|
||||
*
|
||||
* \return LoanedMessage containing memory for a ROS message of type MessageT
|
||||
* \return LoanedMessage containing memory for a ROS message of type ROSMessageType
|
||||
*/
|
||||
rclcpp::LoanedMessage<MessageT, AllocatorT>
|
||||
rclcpp::LoanedMessage<ROSMessageType, AllocatorT>
|
||||
borrow_loaned_message()
|
||||
{
|
||||
return rclcpp::LoanedMessage<MessageT, AllocatorT>(this, this->get_allocator());
|
||||
return rclcpp::LoanedMessage<ROSMessageType, AllocatorT>(
|
||||
*this,
|
||||
this->get_ros_message_type_allocator());
|
||||
}
|
||||
|
||||
/// Send a message to the topic for this publisher.
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This function is templated on the input message type, MessageT.
|
||||
* \param[in] msg A shared pointer to the message to send.
|
||||
* This signature is enabled if the element_type of the std::unique_ptr is
|
||||
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
|
||||
* that type matches the type given when creating the publisher.
|
||||
*
|
||||
* This signature allows the user to give ownership of the message to rclcpp,
|
||||
* allowing for more efficient intra-process communication optimizations.
|
||||
*
|
||||
* \param[in] msg A unique pointer to the message to send.
|
||||
*/
|
||||
virtual void
|
||||
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<T>::value &&
|
||||
std::is_same<T, ROSMessageType>::value
|
||||
>
|
||||
publish(std::unique_ptr<T, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
if (!intra_process_is_enabled_) {
|
||||
this->do_inter_process_publish(*msg);
|
||||
@@ -207,8 +270,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
publish(const MessageT & msg)
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if the object being published is
|
||||
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
|
||||
* that type matches the type given when creating the publisher.
|
||||
*
|
||||
* This signature allows the user to give a reference to a message, which is
|
||||
* copied onto the heap without modification so that a copy can be owned by
|
||||
* rclcpp and ownership of the copy can be moved later if needed.
|
||||
*
|
||||
* \param[in] msg A const reference to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<T>::value &&
|
||||
std::is_same<T, ROSMessageType>::value
|
||||
>
|
||||
publish(const T & msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!intra_process_is_enabled_) {
|
||||
@@ -218,12 +297,77 @@ public:
|
||||
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
|
||||
// As the message is not const, a copy should be made.
|
||||
// A shared_ptr<const MessageT> could also be constructed here.
|
||||
auto ptr = MessageAllocatorTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocatorTraits::construct(*message_allocator_.get(), ptr, msg);
|
||||
MessageUniquePtr unique_msg(ptr, message_deleter_);
|
||||
auto unique_msg = this->duplicate_ros_message_as_unique_ptr(msg);
|
||||
this->publish(std::move(unique_msg));
|
||||
}
|
||||
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if this class was created with a TypeAdapter and
|
||||
* the element_type of the std::unique_ptr matches the custom_type for the
|
||||
* TypeAdapter used with this class.
|
||||
*
|
||||
* This signature allows the user to give ownership of the message to rclcpp,
|
||||
* allowing for more efficient intra-process communication optimizations.
|
||||
*
|
||||
* \param[in] msg A unique pointer to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
|
||||
std::is_same<T, PublishedType>::value
|
||||
>
|
||||
publish(std::unique_ptr<T, PublishedTypeDeleter> msg)
|
||||
{
|
||||
// TODO(wjwwood): later update this to give the unique_ptr to the intra
|
||||
// process manager and let it decide if it needs to be converted or not.
|
||||
// For now, convert it unconditionally and pass it the ROSMessageType
|
||||
// publish function specialization.
|
||||
auto unique_ros_msg = this->create_ros_message_unique_ptr();
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, *unique_ros_msg);
|
||||
this->publish(std::move(unique_ros_msg));
|
||||
}
|
||||
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if this class was created with a TypeAdapter and
|
||||
* the given type matches the custom_type of the TypeAdapter.
|
||||
*
|
||||
* This signature allows the user to give a reference to a message, which is
|
||||
* copied onto the heap without modification so that a copy can be owned by
|
||||
* rclcpp and ownership of the copy can be moved later if needed.
|
||||
*
|
||||
* \param[in] msg A const reference to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
|
||||
std::is_same<T, PublishedType>::value
|
||||
>
|
||||
publish(const T & msg)
|
||||
{
|
||||
// TODO(wjwwood): later update this to give the unique_ptr to the intra
|
||||
// process manager and let it decide if it needs to be converted or not.
|
||||
// For now, convert it unconditionally and pass it the ROSMessageType
|
||||
// publish function specialization.
|
||||
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!intra_process_is_enabled_) {
|
||||
// Convert to the ROS message equivalent and publish it.
|
||||
ROSMessageType ros_msg;
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, ros_msg);
|
||||
// In this case we're not using intra process.
|
||||
return this->do_inter_process_publish(ros_msg);
|
||||
}
|
||||
// Otherwise we have to allocate memory in a unique_ptr, convert it,
|
||||
// and pass it along.
|
||||
// As the message is not const, a copy should be made.
|
||||
// A shared_ptr<const MessageT> could also be constructed here.
|
||||
auto unique_ros_msg = this->create_ros_message_unique_ptr();
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, *unique_ros_msg);
|
||||
this->publish(std::move(unique_ros_msg));
|
||||
}
|
||||
|
||||
void
|
||||
publish(const rcl_serialized_message_t & serialized_msg)
|
||||
{
|
||||
@@ -245,7 +389,7 @@ public:
|
||||
* \param loaned_msg The LoanedMessage instance to be published.
|
||||
*/
|
||||
void
|
||||
publish(rclcpp::LoanedMessage<MessageT, AllocatorT> && loaned_msg)
|
||||
publish(rclcpp::LoanedMessage<ROSMessageType, AllocatorT> && loaned_msg)
|
||||
{
|
||||
if (!loaned_msg.is_valid()) {
|
||||
throw std::runtime_error("loaned message is not valid");
|
||||
@@ -271,15 +415,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageAllocator>
|
||||
[[deprecated("use get_published_type_allocator() or get_ros_message_type_allocator() instead")]]
|
||||
std::shared_ptr<PublishedTypeAllocator>
|
||||
get_allocator() const
|
||||
{
|
||||
return message_allocator_;
|
||||
return std::make_shared<PublishedTypeAllocator>(published_type_allocator_);
|
||||
}
|
||||
|
||||
PublishedTypeAllocator
|
||||
get_published_type_allocator() const
|
||||
{
|
||||
return published_type_allocator_;
|
||||
}
|
||||
|
||||
ROSMessageTypeAllocator
|
||||
get_ros_message_type_allocator() const
|
||||
{
|
||||
return ros_message_type_allocator_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
do_inter_process_publish(const MessageT & msg)
|
||||
do_inter_process_publish(const ROSMessageType & msg)
|
||||
{
|
||||
TRACEPOINT(
|
||||
rclcpp_publish,
|
||||
@@ -316,7 +473,8 @@ protected:
|
||||
}
|
||||
|
||||
void
|
||||
do_loaned_message_publish(std::unique_ptr<MessageT, std::function<void(MessageT *)>> msg)
|
||||
do_loaned_message_publish(
|
||||
std::unique_ptr<ROSMessageType, std::function<void(ROSMessageType *)>> msg)
|
||||
{
|
||||
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg.get(), nullptr);
|
||||
|
||||
@@ -336,7 +494,7 @@ protected:
|
||||
}
|
||||
|
||||
void
|
||||
do_intra_process_publish(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
do_intra_process_publish(std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
@@ -347,14 +505,15 @@ protected:
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
|
||||
ipm->template do_intra_process_publish<MessageT, AllocatorT>(
|
||||
ipm->template do_intra_process_publish<ROSMessageType, AllocatorT>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
std::shared_ptr<const MessageT>
|
||||
do_intra_process_publish_and_return_shared(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
std::shared_ptr<const ROSMessageType>
|
||||
do_intra_process_publish_and_return_shared(
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
@@ -365,10 +524,29 @@ protected:
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
|
||||
return ipm->template do_intra_process_publish_and_return_shared<MessageT, AllocatorT>(
|
||||
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType,
|
||||
AllocatorT>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
/// Return a new unique_ptr using the ROSMessageType of the publisher.
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
create_ros_message_unique_ptr()
|
||||
{
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr);
|
||||
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
|
||||
}
|
||||
|
||||
/// Duplicate a given ros message as a unique_ptr.
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
duplicate_ros_message_as_unique_ptr(const ROSMessageType & msg)
|
||||
{
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr, msg);
|
||||
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
|
||||
}
|
||||
|
||||
/// Copy of original options passed during construction.
|
||||
@@ -378,9 +556,10 @@ protected:
|
||||
*/
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> options_;
|
||||
|
||||
std::shared_ptr<MessageAllocator> message_allocator_;
|
||||
|
||||
MessageDeleter message_deleter_;
|
||||
PublishedTypeAllocator published_type_allocator_;
|
||||
PublishedTypeDeleter published_type_deleter_;
|
||||
ROSMessageTypeAllocator ros_message_type_allocator_;
|
||||
ROSMessageTypeDeleter ros_message_type_deleter_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/publisher.h"
|
||||
@@ -64,6 +65,10 @@ struct PublisherOptionsBase
|
||||
template<typename Allocator>
|
||||
struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
{
|
||||
static_assert(
|
||||
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
|
||||
"Publisher allocator value type must be void");
|
||||
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
@@ -80,10 +85,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
to_rcl_publisher_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_publisher_options_t result = rcl_publisher_get_default_options();
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
|
||||
result.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.allocator = this->get_rcl_allocator();
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
result.rmw_publisher_options.require_unique_network_flow_endpoints =
|
||||
this->require_unique_network_flow_endpoints;
|
||||
@@ -102,10 +104,35 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
get_allocator() const
|
||||
{
|
||||
if (!this->allocator) {
|
||||
return std::make_shared<Allocator>();
|
||||
if (!allocator_storage_) {
|
||||
allocator_storage_ = std::make_shared<Allocator>();
|
||||
}
|
||||
return allocator_storage_;
|
||||
}
|
||||
return this->allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
using PlainAllocator =
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
|
||||
|
||||
rcl_allocator_t
|
||||
get_rcl_allocator() const
|
||||
{
|
||||
if (!plain_allocator_storage_) {
|
||||
plain_allocator_storage_ =
|
||||
std::make_shared<PlainAllocator>(*this->get_allocator());
|
||||
}
|
||||
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
|
||||
}
|
||||
|
||||
// This is a temporal workaround, to make sure that get_allocator()
|
||||
// always returns a copy of the same allocator.
|
||||
mutable std::shared_ptr<Allocator> allocator_storage_;
|
||||
|
||||
// This is a temporal workaround, to keep the plain allocator that backs
|
||||
// up the rcl allocator returned in rcl_publisher_options_t alive.
|
||||
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
|
||||
};
|
||||
|
||||
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
* \param[in] initial_capacity The amount of memory to be allocated.
|
||||
* \param[in] allocator The allocator to be used for the initialization.
|
||||
*/
|
||||
SerializedMessage(
|
||||
explicit SerializedMessage(
|
||||
size_t initial_capacity,
|
||||
const rcl_allocator_t & allocator = rcl_get_default_allocator());
|
||||
|
||||
|
||||
@@ -141,7 +141,9 @@ protected:
|
||||
};
|
||||
|
||||
template<typename ServiceT>
|
||||
class Service : public ServiceBase
|
||||
class Service
|
||||
: public ServiceBase,
|
||||
public std::enable_shared_from_this<Service<ServiceT>>
|
||||
{
|
||||
public:
|
||||
using CallbackType = std::function<
|
||||
@@ -177,25 +179,16 @@ public:
|
||||
using rosidl_typesupport_cpp::get_service_type_support_handle;
|
||||
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
|
||||
|
||||
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
|
||||
// rcl does the static memory allocation here
|
||||
service_handle_ = std::shared_ptr<rcl_service_t>(
|
||||
new rcl_service_t, [weak_node_handle, service_name](rcl_service_t * service)
|
||||
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
|
||||
{
|
||||
auto handle = weak_node_handle.lock();
|
||||
if (handle) {
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
} else {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Error in destruction of rcl service handle " << service_name <<
|
||||
": the Node Handle was destructed too early. You will leak memory");
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
delete service;
|
||||
});
|
||||
@@ -344,9 +337,10 @@ public:
|
||||
std::shared_ptr<void> request) override
|
||||
{
|
||||
auto typed_request = std::static_pointer_cast<typename ServiceT::Request>(request);
|
||||
auto response = std::make_shared<typename ServiceT::Response>();
|
||||
any_callback_.dispatch(request_header, typed_request, response);
|
||||
send_response(*request_header, *response);
|
||||
auto response = any_callback_.dispatch(this->shared_from_this(), request_header, typed_request);
|
||||
if (response) {
|
||||
send_response(*request_header, *response);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -60,10 +60,16 @@ class NodeTopicsInterface;
|
||||
|
||||
/// Subscription implementation, templated on the type of message this subscription receives.
|
||||
template<
|
||||
typename CallbackMessageT,
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter,
|
||||
/// otherwise just MessageT.
|
||||
typename SubscribedT = typename rclcpp::TypeAdapter<MessageT>::custom_type,
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter,
|
||||
/// otherwise just MessageT.
|
||||
typename ROSMessageT = typename rclcpp::TypeAdapter<MessageT>::ros_message_type,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
ROSMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
class Subscription : public SubscriptionBase
|
||||
@@ -71,14 +77,36 @@ class Subscription : public SubscriptionBase
|
||||
friend class rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
|
||||
public:
|
||||
using MessageAllocatorTraits = allocator::AllocRebind<CallbackMessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAllocator, CallbackMessageT>;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const CallbackMessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
|
||||
using SubscriptionTopicStatisticsSharedPtr =
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>;
|
||||
// Redeclare these here to use outside of the class.
|
||||
using SubscribedType = SubscribedT;
|
||||
using ROSMessageType = ROSMessageT;
|
||||
using MessageMemoryStrategyType = MessageMemoryStrategyT;
|
||||
|
||||
using SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, AllocatorT>;
|
||||
using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
|
||||
using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits [[deprecated("use ROSMessageTypeAllocatorTraits")]] =
|
||||
ROSMessageTypeAllocatorTraits;
|
||||
using MessageAllocator [[deprecated("use ROSMessageTypeAllocator")]] =
|
||||
ROSMessageTypeAllocator;
|
||||
using MessageDeleter [[deprecated("use ROSMessageTypeDeleter")]] =
|
||||
ROSMessageTypeDeleter;
|
||||
|
||||
using ConstMessageSharedPtr [[deprecated]] = std::shared_ptr<const ROSMessageType>;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> instead")]] =
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>;
|
||||
|
||||
private:
|
||||
using SubscriptionTopicStatisticsSharedPtr =
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
|
||||
|
||||
/// Default constructor.
|
||||
@@ -104,7 +132,7 @@ public:
|
||||
const rosidl_message_type_support_t & type_support_handle,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> callback,
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename MessageMemoryStrategyT::SharedPtr message_memory_strategy,
|
||||
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics = nullptr)
|
||||
@@ -112,8 +140,8 @@ public:
|
||||
node_base,
|
||||
type_support_handle,
|
||||
topic_name,
|
||||
options.template to_rcl_subscription_options<CallbackMessageT>(qos),
|
||||
rclcpp::subscription_traits::is_serialized_subscription_argument<CallbackMessageT>::value),
|
||||
options.template to_rcl_subscription_options<ROSMessageType>(qos),
|
||||
callback.is_serialized_message_callback()),
|
||||
any_callback_(callback),
|
||||
options_(options),
|
||||
message_memory_strategy_(message_memory_strategy)
|
||||
@@ -155,16 +183,16 @@ public:
|
||||
using rclcpp::detail::resolve_intra_process_buffer_type;
|
||||
|
||||
// Check if the QoS is compatible with intra-process.
|
||||
rmw_qos_profile_t qos_profile = get_actual_qos().get_rmw_qos_profile();
|
||||
if (qos_profile.history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with keep all history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos_profile.depth == 0) {
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with 0 depth qos policy");
|
||||
}
|
||||
if (qos_profile.durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
|
||||
if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with volatile durability");
|
||||
}
|
||||
@@ -241,11 +269,34 @@ public:
|
||||
* \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
|
||||
*/
|
||||
bool
|
||||
take(CallbackMessageT & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
take(ROSMessageType & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
{
|
||||
return this->take_type_erased(static_cast<void *>(&message_out), message_info_out);
|
||||
}
|
||||
|
||||
/// Take the next message from the inter-process subscription.
|
||||
/**
|
||||
* This verison takes a SubscribedType which is different frmo the
|
||||
* ROSMessageType when a rclcpp::TypeAdapter is in used.
|
||||
*
|
||||
* \sa take(ROSMessageType &, rclcpp::MessageInfo &)
|
||||
*/
|
||||
template<typename TakeT>
|
||||
std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<TakeT>::value &&
|
||||
std::is_same_v<TakeT, SubscribedType>,
|
||||
bool
|
||||
>
|
||||
take(TakeT & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
{
|
||||
ROSMessageType local_message;
|
||||
bool taken = this->take_type_erased(static_cast<void *>(&local_message), message_info_out);
|
||||
if (taken) {
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_custom(local_message, message_out);
|
||||
}
|
||||
return taken;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
create_message() override
|
||||
{
|
||||
@@ -272,7 +323,7 @@ public:
|
||||
// we should ignore this copy of the message.
|
||||
return;
|
||||
}
|
||||
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
|
||||
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> now;
|
||||
if (subscription_topic_statistics_) {
|
||||
@@ -290,15 +341,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) override
|
||||
{
|
||||
// TODO(wjwwood): enable topic statistics for serialized messages
|
||||
any_callback_.dispatch(serialized_message, message_info);
|
||||
}
|
||||
|
||||
void
|
||||
handle_loaned_message(
|
||||
void * loaned_message,
|
||||
const rclcpp::MessageInfo & message_info) override
|
||||
{
|
||||
auto typed_message = static_cast<CallbackMessageT *>(loaned_message);
|
||||
auto typed_message = static_cast<ROSMessageType *>(loaned_message);
|
||||
// message is loaned, so we have to make sure that the deleter does not deallocate the message
|
||||
auto sptr = std::shared_ptr<CallbackMessageT>(
|
||||
typed_message, [](CallbackMessageT * msg) {(void) msg;});
|
||||
auto sptr = std::shared_ptr<ROSMessageType>(
|
||||
typed_message, [](ROSMessageType * msg) {(void) msg;});
|
||||
any_callback_.dispatch(sptr, message_info);
|
||||
}
|
||||
|
||||
@@ -309,7 +369,7 @@ public:
|
||||
void
|
||||
return_message(std::shared_ptr<void> & message) override
|
||||
{
|
||||
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
|
||||
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
|
||||
message_memory_strategy_->return_message(typed_message);
|
||||
}
|
||||
|
||||
@@ -332,21 +392,24 @@ public:
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Subscription)
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_callback_;
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> any_callback_;
|
||||
/// Copy of original options passed during construction.
|
||||
/**
|
||||
* It is important to save a copy of this so that the rmw payload which it
|
||||
* may contain is kept alive for the duration of the subscription.
|
||||
*/
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> options_;
|
||||
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, AllocatorT>::SharedPtr
|
||||
typename message_memory_strategy::MessageMemoryStrategy<ROSMessageType, AllocatorT>::SharedPtr
|
||||
message_memory_strategy_;
|
||||
|
||||
/// Component which computes and publishes topic statistics for this subscriber
|
||||
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics_{nullptr};
|
||||
|
||||
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
|
||||
CallbackMessageT,
|
||||
ROSMessageType,
|
||||
AllocatorT,
|
||||
typename MessageUniquePtr::deleter_type>;
|
||||
ROSMessageTypeDeleter,
|
||||
MessageT>;
|
||||
std::shared_ptr<SubscriptionIntraProcessT> subscription_intra_process_;
|
||||
};
|
||||
|
||||
|
||||
@@ -183,6 +183,13 @@ public:
|
||||
void
|
||||
handle_message(std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rosidl_typesupport_cpp/message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/get_message_type_support_handle.hpp"
|
||||
#include "rclcpp/intra_process_buffer_type.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
@@ -74,26 +75,23 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
|
||||
typename ROSMessageType = typename SubscriptionT::ROSMessageType
|
||||
>
|
||||
SubscriptionFactory
|
||||
create_subscription_factory(
|
||||
CallbackT && callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat,
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
|
||||
subscription_topic_stats = nullptr
|
||||
)
|
||||
{
|
||||
auto allocator = options.get_allocator();
|
||||
|
||||
using rclcpp::AnySubscriptionCallback;
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_subscription_callback(*allocator);
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> any_subscription_callback(*allocator);
|
||||
any_subscription_callback.set(std::forward<CallbackT>(callback));
|
||||
|
||||
SubscriptionFactory factory {
|
||||
@@ -107,9 +105,9 @@ create_subscription_factory(
|
||||
using rclcpp::Subscription;
|
||||
using rclcpp::SubscriptionBase;
|
||||
|
||||
auto sub = Subscription<CallbackMessageT, AllocatorT>::make_shared(
|
||||
auto sub = Subscription<MessageT, AllocatorT>::make_shared(
|
||||
node_base,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
rclcpp::get_message_type_support_handle<MessageT>(),
|
||||
topic_name,
|
||||
qos,
|
||||
any_subscription_callback,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
@@ -86,6 +87,10 @@ struct SubscriptionOptionsBase
|
||||
template<typename Allocator>
|
||||
struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
{
|
||||
static_assert(
|
||||
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
|
||||
"Subscription allocator value type must be void");
|
||||
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
@@ -107,10 +112,7 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
to_rcl_subscription_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_subscription_options_t result = rcl_subscription_get_default_options();
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
|
||||
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.allocator = this->get_rcl_allocator();
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
result.rmw_subscription_options.ignore_local_publications = this->ignore_local_publications;
|
||||
result.rmw_subscription_options.require_unique_network_flow_endpoints =
|
||||
@@ -124,15 +126,39 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Get the allocator, creating one if needed.
|
||||
std::shared_ptr<Allocator>
|
||||
get_allocator() const
|
||||
{
|
||||
if (!this->allocator) {
|
||||
return std::make_shared<Allocator>();
|
||||
if (!allocator_storage_) {
|
||||
allocator_storage_ = std::make_shared<Allocator>();
|
||||
}
|
||||
return allocator_storage_;
|
||||
}
|
||||
return this->allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
using PlainAllocator =
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
|
||||
|
||||
rcl_allocator_t
|
||||
get_rcl_allocator() const
|
||||
{
|
||||
if (!plain_allocator_storage_) {
|
||||
plain_allocator_storage_ =
|
||||
std::make_shared<PlainAllocator>(*this->get_allocator());
|
||||
}
|
||||
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
|
||||
}
|
||||
|
||||
// This is a temporal workaround, to make sure that get_allocator()
|
||||
// always returns a copy of the same allocator.
|
||||
mutable std::shared_ptr<Allocator> allocator_storage_;
|
||||
|
||||
// This is a temporal workaround, to keep the plain allocator that backs
|
||||
// up the rcl allocator returned in rcl_subscription_options_t alive.
|
||||
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
|
||||
};
|
||||
|
||||
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
@@ -74,14 +74,14 @@ public:
|
||||
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
|
||||
bool use_clock_thread = true);
|
||||
|
||||
/// Attack node to the time source.
|
||||
/// Attach node to the time source.
|
||||
/**
|
||||
* \param node std::shared pointer to a initialized node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void attachNode(rclcpp::Node::SharedPtr node);
|
||||
|
||||
/// Attack node to the time source.
|
||||
/// Attach node to the time source.
|
||||
/**
|
||||
* If the parameter `use_sim_time` is `true` then the source time is the simulation time,
|
||||
* otherwise the source time is defined by the system.
|
||||
|
||||
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal file
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef RCLCPP__TYPE_ADAPTER_HPP_
|
||||
#define RCLCPP__TYPE_ADAPTER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Template structure used to adapt custom, user-defined types to ROS types.
|
||||
/**
|
||||
* Adapting a custom, user-defined type to a ROS type allows that custom type
|
||||
* to be used when publishing and subscribing in ROS.
|
||||
*
|
||||
* In order to adapt a custom type to a ROS type, the user must create a
|
||||
* template specialization of this structure for the custom type.
|
||||
* In that specialization they must:
|
||||
*
|
||||
* - change `is_specialized` to `std::true_type`,
|
||||
* - specify the custom type with `using custom_type = ...`,
|
||||
* - specify the ROS type with `using ros_message_type = ...`,
|
||||
* - provide static convert functions with the signatures:
|
||||
* - static void convert_to_ros(const custom_type &, ros_message_type &)
|
||||
* - static void convert_to_custom(const ros_message_type &, custom_type &)
|
||||
*
|
||||
* The convert functions must convert from one type to the other.
|
||||
*
|
||||
* For example, here is a theoretical example for adapting `std::string` to the
|
||||
* `std_msgs::msg::String` ROS message type:
|
||||
*
|
||||
* template<>
|
||||
* struct rclcpp::TypeAdapter<std::string, std_msgs::msg::String>
|
||||
* {
|
||||
* using is_specialized = std::true_type;
|
||||
* using custom_type = std::string;
|
||||
* using ros_message_type = std_msgs::msg::String;
|
||||
*
|
||||
* static
|
||||
* void
|
||||
* convert_to_ros_message(
|
||||
* const custom_type & source,
|
||||
* ros_message_type & destination)
|
||||
* {
|
||||
* destination.data = source;
|
||||
* }
|
||||
*
|
||||
* static
|
||||
* void
|
||||
* convert_to_custom(
|
||||
* const ros_message_type & source,
|
||||
* custom_type & destination)
|
||||
* {
|
||||
* destination = source.data;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* The adapter can then be used when creating a publisher or subscription,
|
||||
* e.g.:
|
||||
*
|
||||
* using MyAdaptedType = TypeAdapter<std::string, std_msgs::msg::String>;
|
||||
* auto pub = node->create_publisher<MyAdaptedType>("topic", 10);
|
||||
* auto sub = node->create_subscription<MyAdaptedType>(
|
||||
* "topic",
|
||||
* 10,
|
||||
* [](const std::string & msg) {...});
|
||||
*
|
||||
* You can also be more declarative by using the adapt_type::as metafunctions,
|
||||
* which are a bit less ambiguous to read:
|
||||
*
|
||||
* using AdaptedType = rclcpp::adapt_type<std::string>::as<std_msgs::msg::String>;
|
||||
* auto pub = node->create_publisher<AdaptedType>(...);
|
||||
*
|
||||
* If you wish, you may associate a custom type with a single ROS message type,
|
||||
* allowing you to be a bit more brief when creating entities, e.g.:
|
||||
*
|
||||
* // First you must declare the association, this is similar to how you
|
||||
* // would avoid using the namespace in C++ by doing `using std::vector;`.
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
*
|
||||
* // Then you can create things with just the custom type, and the ROS
|
||||
* // message type is implied based on the previous statement.
|
||||
* auto pub = node->create_publisher<std::string>(...);
|
||||
*/
|
||||
template<typename CustomType, typename ROSMessageType = void, class Enable = void>
|
||||
struct TypeAdapter
|
||||
{
|
||||
using is_specialized = std::false_type;
|
||||
using custom_type = CustomType;
|
||||
// In this case, the CustomType is the only thing given, or there is no specialization.
|
||||
// Assign ros_message_type to CustomType for the former case.
|
||||
using ros_message_type = CustomType;
|
||||
};
|
||||
|
||||
/// Helper template to determine if a type is a TypeAdapter, false specialization.
|
||||
template<typename T>
|
||||
struct is_type_adapter : std::false_type {};
|
||||
|
||||
/// Helper template to determine if a type is a TypeAdapter, true specialization.
|
||||
template<typename ... Ts>
|
||||
struct is_type_adapter<TypeAdapter<Ts...>>: std::true_type {};
|
||||
|
||||
/// Identity specialization for TypeAdapter.
|
||||
template<typename T>
|
||||
struct TypeAdapter<T, void, std::enable_if_t<is_type_adapter<T>::value>>: T {};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename CustomType, typename ROSMessageType>
|
||||
struct assert_type_pair_is_specialized_type_adapter
|
||||
{
|
||||
using type_adapter = TypeAdapter<CustomType, ROSMessageType>;
|
||||
static_assert(
|
||||
type_adapter::is_specialized::value,
|
||||
"No type adapter for this custom type/ros message type pair");
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Template metafunction that can make the type being adapted explicit.
|
||||
template<typename CustomType>
|
||||
struct adapt_type
|
||||
{
|
||||
template<typename ROSMessageType>
|
||||
using as = typename ::rclcpp::detail::assert_type_pair_is_specialized_type_adapter<
|
||||
CustomType,
|
||||
ROSMessageType
|
||||
>::type_adapter;
|
||||
};
|
||||
|
||||
/// Implicit type adapter used as a short hand way to create something with just the custom type.
|
||||
/**
|
||||
* This is used when creating a publisher or subscription using just the custom
|
||||
* type in conjunction with RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE().
|
||||
* For example:
|
||||
*
|
||||
* #include "type_adapter_for_std_string_to_std_msgs_String.hpp"
|
||||
*
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
*
|
||||
* int main(...) {
|
||||
* // ...
|
||||
* auto pub = node->create_publisher<std::string>(...);
|
||||
* }
|
||||
*
|
||||
* \sa TypeAdapter for more examples.
|
||||
*/
|
||||
template<typename CustomType>
|
||||
struct ImplicitTypeAdapter
|
||||
{
|
||||
using is_specialized = std::false_type;
|
||||
};
|
||||
|
||||
/// Specialization of TypeAdapter for ImplicitTypeAdapter.
|
||||
/**
|
||||
* This allows for things like this:
|
||||
*
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
* auto pub = node->create_publisher<std::string>("topic", 10);
|
||||
*
|
||||
*/
|
||||
template<typename T>
|
||||
struct TypeAdapter<T, void, std::enable_if_t<ImplicitTypeAdapter<T>::is_specialized::value>>
|
||||
: ImplicitTypeAdapter<T>
|
||||
{};
|
||||
|
||||
/// Assigns the custom type implicitly to the given custom type/ros message type pair.
|
||||
/**
|
||||
* Note: this macro needs to be used in the root namespace.
|
||||
* We cannot use ::rclcpp to protect against this, due to how GCC interprets the
|
||||
* syntax, see: https://stackoverflow.com/a/2781537
|
||||
*
|
||||
* \sa TypeAdapter
|
||||
* \sa ImplicitTypeAdapter
|
||||
*/
|
||||
#define RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(CustomType, ROSMessageType) \
|
||||
template<> \
|
||||
struct rclcpp::ImplicitTypeAdapter<CustomType> \
|
||||
: public rclcpp::TypeAdapter<CustomType, ROSMessageType> \
|
||||
{ \
|
||||
static_assert( \
|
||||
is_specialized::value, \
|
||||
"Cannot use custom type as ros type when there is no TypeAdapter for that pair"); \
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__TYPE_ADAPTER_HPP_
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp</name>
|
||||
<version>9.0.2</version>
|
||||
<version>11.2.0</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -315,9 +315,13 @@ Context::shutdown(const std::string & reason)
|
||||
// set shutdown reason
|
||||
shutdown_reason_ = reason;
|
||||
// call each shutdown callback
|
||||
for (const auto & callback : on_shutdown_callbacks_) {
|
||||
callback();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
for (const auto & callback : on_shutdown_callbacks_) {
|
||||
(*callback)();
|
||||
}
|
||||
}
|
||||
|
||||
// interrupt all blocking sleep_for() and all blocking executors or wait sets
|
||||
this->interrupt_all_sleep_for();
|
||||
// remove self from the global contexts
|
||||
@@ -344,20 +348,48 @@ Context::shutdown(const std::string & reason)
|
||||
rclcpp::Context::OnShutdownCallback
|
||||
Context::on_shutdown(OnShutdownCallback callback)
|
||||
{
|
||||
on_shutdown_callbacks_.push_back(callback);
|
||||
add_on_shutdown_callback(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::Context::OnShutdownCallback> &
|
||||
Context::get_on_shutdown_callbacks() const
|
||||
rclcpp::OnShutdownCallbackHandle
|
||||
Context::add_on_shutdown_callback(OnShutdownCallback callback)
|
||||
{
|
||||
return on_shutdown_callbacks_;
|
||||
auto callback_shared_ptr = std::make_shared<OnShutdownCallback>(callback);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
on_shutdown_callbacks_.emplace(callback_shared_ptr);
|
||||
}
|
||||
|
||||
OnShutdownCallbackHandle callback_handle;
|
||||
callback_handle.callback = callback_shared_ptr;
|
||||
return callback_handle;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::OnShutdownCallback> &
|
||||
Context::get_on_shutdown_callbacks()
|
||||
bool
|
||||
Context::remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle)
|
||||
{
|
||||
return on_shutdown_callbacks_;
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
auto callback_shared_ptr = callback_handle.callback.lock();
|
||||
if (callback_shared_ptr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return on_shutdown_callbacks_.erase(callback_shared_ptr) == 1;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::OnShutdownCallback>
|
||||
Context::get_on_shutdown_callbacks() const
|
||||
{
|
||||
std::vector<OnShutdownCallback> callbacks;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
for (auto & iter : on_shutdown_callbacks_) {
|
||||
callbacks.emplace_back(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
std::shared_ptr<rcl_context_t>
|
||||
|
||||
@@ -56,7 +56,7 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
throw_from_rcl_error(ret, "Failed to create interrupt guard condition in Executor constructor");
|
||||
}
|
||||
|
||||
context_->on_shutdown(
|
||||
shutdown_callback_handle_ = context_->add_on_shutdown_callback(
|
||||
[weak_gc = std::weak_ptr<rclcpp::GuardCondition>{shutdown_guard_condition_}]() {
|
||||
auto strong_gc = weak_gc.lock();
|
||||
if (strong_gc) {
|
||||
@@ -138,6 +138,14 @@ Executor::~Executor()
|
||||
}
|
||||
// Remove and release the sigint guard condition
|
||||
memory_strategy_->remove_guard_condition(&shutdown_guard_condition_->get_rcl_guard_condition());
|
||||
|
||||
// Remove shutdown callback handle registered to Context
|
||||
if (!context_->remove_on_shutdown_callback(shutdown_callback_handle_)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to remove registered on_shutdown callback");
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
@@ -260,7 +268,9 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
|
||||
// If the node already has an executor
|
||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||
if (has_executor.exchange(true)) {
|
||||
throw std::runtime_error("Node has already been added to an executor.");
|
||||
throw std::runtime_error(
|
||||
std::string("Node '") + node_ptr->get_fully_qualified_name() +
|
||||
"' has already been added to an executor.");
|
||||
}
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
for (auto & weak_group : node_ptr->get_callback_groups()) {
|
||||
@@ -581,8 +591,7 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
|
||||
[&]() {return subscription->take_serialized(*serialized_msg.get(), message_info);},
|
||||
[&]()
|
||||
{
|
||||
auto void_serialized_msg = std::static_pointer_cast<void>(serialized_msg);
|
||||
subscription->handle_message(void_serialized_msg, message_info);
|
||||
subscription->handle_serialized_message(serialized_msg, message_info);
|
||||
});
|
||||
subscription->return_serialized_message(serialized_msg);
|
||||
} else if (subscription->can_loan_messages()) {
|
||||
|
||||
@@ -36,11 +36,19 @@ std::shared_ptr<rclcpp::SerializedMessage> GenericSubscription::create_serialize
|
||||
}
|
||||
|
||||
void GenericSubscription::handle_message(
|
||||
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info)
|
||||
std::shared_ptr<void> &,
|
||||
const rclcpp::MessageInfo &)
|
||||
{
|
||||
(void) message_info;
|
||||
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
|
||||
callback_(typed_message);
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"handle_message is not implemented for GenericSubscription");
|
||||
}
|
||||
|
||||
void
|
||||
GenericSubscription::handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & message,
|
||||
const rclcpp::MessageInfo &)
|
||||
{
|
||||
callback_(message);
|
||||
}
|
||||
|
||||
void GenericSubscription::handle_loaned_message(
|
||||
|
||||
@@ -36,23 +36,26 @@ IntraProcessManager::add_publisher(rclcpp::PublisherBase::SharedPtr publisher)
|
||||
{
|
||||
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
|
||||
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
uint64_t pub_id = IntraProcessManager::get_next_unique_id();
|
||||
|
||||
publishers_[id].publisher = publisher;
|
||||
publishers_[id].topic_name = publisher->get_topic_name();
|
||||
publishers_[id].qos = publisher->get_actual_qos().get_rmw_qos_profile();
|
||||
publishers_[pub_id] = publisher;
|
||||
|
||||
// Initialize the subscriptions storage for this publisher.
|
||||
pub_to_subs_[id] = SplittedSubscriptions();
|
||||
pub_to_subs_[pub_id] = SplittedSubscriptions();
|
||||
|
||||
// create an entry for the publisher id and populate with already existing subscriptions
|
||||
for (auto & pair : subscriptions_) {
|
||||
if (can_communicate(publishers_[id], pair.second)) {
|
||||
insert_sub_id_for_pub(pair.first, id, pair.second.use_take_shared_method);
|
||||
auto subscription = pair.second.lock();
|
||||
if (!subscription) {
|
||||
continue;
|
||||
}
|
||||
if (can_communicate(publisher, subscription)) {
|
||||
uint64_t sub_id = pair.first;
|
||||
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
return pub_id;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
@@ -60,21 +63,23 @@ IntraProcessManager::add_subscription(SubscriptionIntraProcessBase::SharedPtr su
|
||||
{
|
||||
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
|
||||
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
uint64_t sub_id = IntraProcessManager::get_next_unique_id();
|
||||
|
||||
subscriptions_[id].subscription = subscription;
|
||||
subscriptions_[id].topic_name = subscription->get_topic_name();
|
||||
subscriptions_[id].qos = subscription->get_actual_qos();
|
||||
subscriptions_[id].use_take_shared_method = subscription->use_take_shared_method();
|
||||
subscriptions_[sub_id] = subscription;
|
||||
|
||||
// adds the subscription id to all the matchable publishers
|
||||
for (auto & pair : publishers_) {
|
||||
if (can_communicate(pair.second, subscriptions_[id])) {
|
||||
insert_sub_id_for_pub(id, pair.first, subscriptions_[id].use_take_shared_method);
|
||||
auto publisher = pair.second.lock();
|
||||
if (!publisher) {
|
||||
continue;
|
||||
}
|
||||
if (can_communicate(publisher, subscription)) {
|
||||
uint64_t pub_id = pair.first;
|
||||
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
return sub_id;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -116,7 +121,7 @@ IntraProcessManager::matches_any_publishers(const rmw_gid_t * id) const
|
||||
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
|
||||
|
||||
for (auto & publisher_pair : publishers_) {
|
||||
auto publisher = publisher_pair.second.publisher.lock();
|
||||
auto publisher = publisher_pair.second.lock();
|
||||
if (!publisher) {
|
||||
continue;
|
||||
}
|
||||
@@ -157,7 +162,7 @@ IntraProcessManager::get_subscription_intra_process(uint64_t intra_process_subsc
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
auto subscription = subscription_it->second.subscription.lock();
|
||||
auto subscription = subscription_it->second.lock();
|
||||
if (subscription) {
|
||||
return subscription;
|
||||
} else {
|
||||
@@ -204,25 +209,16 @@ IntraProcessManager::insert_sub_id_for_pub(
|
||||
|
||||
bool
|
||||
IntraProcessManager::can_communicate(
|
||||
PublisherInfo pub_info,
|
||||
SubscriptionInfo sub_info) const
|
||||
rclcpp::PublisherBase::SharedPtr pub,
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const
|
||||
{
|
||||
// publisher and subscription must be on the same topic
|
||||
if (strcmp(pub_info.topic_name, sub_info.topic_name) != 0) {
|
||||
if (strcmp(pub->get_topic_name(), sub->get_topic_name()) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(alsora): the following checks for qos compatibility should be provided by the RMW
|
||||
// a reliable subscription can't be connected with a best effort publisher
|
||||
if (
|
||||
sub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_RELIABLE &&
|
||||
pub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// a publisher and a subscription with different durability can't communicate
|
||||
if (sub_info.qos.durability != pub_info.qos.durability) {
|
||||
auto check_result = rclcpp::qos_check_compatible(pub->get_actual_qos(), sub->get_actual_qos());
|
||||
if (check_result.compatibility == rclcpp::QoSCompatibility::Error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -349,6 +349,21 @@ __declare_parameter_common(
|
||||
initial_value = &overrides_it->second;
|
||||
}
|
||||
|
||||
// If there is no initial value, then skip initialization
|
||||
if (initial_value->get_type() == rclcpp::PARAMETER_NOT_SET) {
|
||||
// Add declared parameters to storage (without a value)
|
||||
parameter_infos[name].descriptor.name = name;
|
||||
if (parameter_descriptor.dynamic_typing) {
|
||||
parameter_infos[name].descriptor.type = rclcpp::PARAMETER_NOT_SET;
|
||||
} else {
|
||||
parameter_infos[name].descriptor.type = parameter_descriptor.type;
|
||||
}
|
||||
parameters_out[name] = parameter_infos.at(name);
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check with the user's callback to see if the initial value can be set.
|
||||
std::vector<rclcpp::Parameter> parameter_wrappers {rclcpp::Parameter(name, *initial_value)};
|
||||
// This function also takes care of default vs initial value.
|
||||
@@ -413,14 +428,6 @@ declare_parameter_helper(
|
||||
parameter_descriptor.type = static_cast<uint8_t>(type);
|
||||
}
|
||||
|
||||
if (
|
||||
rclcpp::PARAMETER_NOT_SET == default_value.get_type() &&
|
||||
overrides.find(name) == overrides.end() &&
|
||||
parameter_descriptor.dynamic_typing == false)
|
||||
{
|
||||
throw rclcpp::exceptions::NoParameterOverrideProvided(name);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterEvent parameter_event;
|
||||
auto result = __declare_parameter_common(
|
||||
name,
|
||||
@@ -806,14 +813,21 @@ NodeParameters::get_parameters(const std::vector<std::string> & names) const
|
||||
rclcpp::Parameter
|
||||
NodeParameters::get_parameter(const std::string & name) const
|
||||
{
|
||||
rclcpp::Parameter parameter;
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
if (get_parameter(name, parameter)) {
|
||||
return parameter;
|
||||
auto param_iter = parameters_.find(name);
|
||||
if (
|
||||
parameters_.end() != param_iter &&
|
||||
(param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET ||
|
||||
param_iter->second.descriptor.dynamic_typing))
|
||||
{
|
||||
return rclcpp::Parameter{name, param_iter->second.value};
|
||||
} else if (this->allow_undeclared_) {
|
||||
return parameter;
|
||||
} else {
|
||||
return rclcpp::Parameter{};
|
||||
} else if (parameters_.end() == param_iter) {
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
|
||||
} else {
|
||||
throw rclcpp::exceptions::ParameterUninitializedException(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ SubscriptionIntraProcessBase::get_topic_name() const
|
||||
return topic_name_.c_str();
|
||||
}
|
||||
|
||||
rmw_qos_profile_t
|
||||
rclcpp::QoS
|
||||
SubscriptionIntraProcessBase::get_actual_qos() const
|
||||
{
|
||||
return qos_profile_;
|
||||
|
||||
@@ -156,12 +156,6 @@ ok(Context::SharedPtr context)
|
||||
return context->is_valid();
|
||||
}
|
||||
|
||||
bool
|
||||
is_initialized(Context::SharedPtr context)
|
||||
{
|
||||
return ok(context);
|
||||
}
|
||||
|
||||
bool
|
||||
shutdown(Context::SharedPtr context, const std::string & reason)
|
||||
{
|
||||
|
||||
1
rclcpp/test/msg/String.msg
Normal file
1
rclcpp/test/msg/String.msg
Normal file
@@ -0,0 +1 @@
|
||||
string data
|
||||
@@ -7,6 +7,7 @@ add_definitions(-DTEST_RESOURCES_DIRECTORY="${TEST_RESOURCES_DIRECTORY}")
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
|
||||
../msg/Header.msg
|
||||
../msg/MessageWithHeader.msg
|
||||
../msg/String.msg
|
||||
DEPENDENCIES builtin_interfaces
|
||||
LIBRARY_NAME ${PROJECT_NAME}
|
||||
SKIP_INSTALL
|
||||
@@ -143,6 +144,13 @@ if(TARGET test_intra_process_manager)
|
||||
)
|
||||
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gmock(test_intra_process_manager_with_allocators test_intra_process_manager_with_allocators.cpp)
|
||||
if(TARGET test_intra_process_manager_with_allocators)
|
||||
ament_target_dependencies(test_intra_process_manager_with_allocators
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_intra_process_manager_with_allocators ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_ring_buffer_implementation test_ring_buffer_implementation.cpp)
|
||||
if(TARGET test_ring_buffer_implementation)
|
||||
ament_target_dependencies(test_ring_buffer_implementation
|
||||
@@ -348,6 +356,44 @@ if(TARGET test_publisher)
|
||||
)
|
||||
target_link_libraries(test_publisher ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if(WIN32)
|
||||
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_publisher_with_type_adapter test_publisher_with_type_adapter.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
)
|
||||
if(TARGET test_publisher_with_type_adapter)
|
||||
ament_target_dependencies(test_publisher_with_type_adapter
|
||||
"rcutils"
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
rosidl_target_interfaces(test_publisher_with_type_adapter ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
|
||||
target_link_libraries(test_publisher_with_type_adapter ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_subscription_with_type_adapter test_subscription_with_type_adapter.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
)
|
||||
if(TARGET test_subscription_with_type_adapter)
|
||||
ament_target_dependencies(test_subscription_with_type_adapter
|
||||
"rcutils"
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
rosidl_target_interfaces(test_subscription_with_type_adapter ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
|
||||
target_link_libraries(test_subscription_with_type_adapter ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_publisher_subscription_count_api test_publisher_subscription_count_api.cpp)
|
||||
if(TARGET test_publisher_subscription_count_api)
|
||||
ament_target_dependencies(test_publisher_subscription_count_api
|
||||
@@ -500,11 +546,6 @@ if(TARGET test_find_weak_nodes)
|
||||
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if(WIN32)
|
||||
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_externally_defined_services test_externally_defined_services.cpp)
|
||||
ament_target_dependencies(test_externally_defined_services
|
||||
"rcl"
|
||||
|
||||
@@ -71,6 +71,8 @@ public:
|
||||
|
||||
void handle_message(std::shared_ptr<void> &, const rclcpp::MessageInfo &) override {}
|
||||
void handle_loaned_message(void *, const rclcpp::MessageInfo &) override {}
|
||||
void handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> &, const rclcpp::MessageInfo &) override {}
|
||||
void return_message(std::shared_ptr<void> &) override {}
|
||||
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> &) override {}
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/any_service_callback.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
#include "test_msgs/srv/empty.h"
|
||||
|
||||
@@ -44,7 +45,7 @@ protected:
|
||||
|
||||
TEST_F(TestAnyServiceCallback, no_set_and_dispatch_throw) {
|
||||
EXPECT_THROW(
|
||||
any_service_callback_.dispatch(request_header_, request_, response_),
|
||||
any_service_callback_.dispatch(nullptr, request_header_, request_),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ TEST_F(TestAnyServiceCallback, set_and_dispatch_no_header) {
|
||||
|
||||
any_service_callback_.set(callback);
|
||||
EXPECT_NO_THROW(
|
||||
any_service_callback_.dispatch(request_header_, request_, response_));
|
||||
EXPECT_NE(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
|
||||
EXPECT_EQ(callback_calls, 1);
|
||||
}
|
||||
|
||||
@@ -73,6 +74,36 @@ TEST_F(TestAnyServiceCallback, set_and_dispatch_header) {
|
||||
|
||||
any_service_callback_.set(callback_with_header);
|
||||
EXPECT_NO_THROW(
|
||||
any_service_callback_.dispatch(request_header_, request_, response_));
|
||||
EXPECT_NE(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
|
||||
EXPECT_EQ(callback_with_header_calls, 1);
|
||||
}
|
||||
|
||||
TEST_F(TestAnyServiceCallback, set_and_dispatch_defered) {
|
||||
int callback_with_header_calls = 0;
|
||||
auto callback_with_header =
|
||||
[&callback_with_header_calls](const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<test_msgs::srv::Empty::Request>) {
|
||||
callback_with_header_calls++;
|
||||
};
|
||||
|
||||
any_service_callback_.set(callback_with_header);
|
||||
EXPECT_NO_THROW(
|
||||
EXPECT_EQ(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
|
||||
EXPECT_EQ(callback_with_header_calls, 1);
|
||||
}
|
||||
|
||||
TEST_F(TestAnyServiceCallback, set_and_dispatch_defered_with_service_handle) {
|
||||
int callback_with_header_calls = 0;
|
||||
auto callback_with_header =
|
||||
[&callback_with_header_calls](std::shared_ptr<rclcpp::Service<test_msgs::srv::Empty>>,
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<test_msgs::srv::Empty::Request>)
|
||||
{
|
||||
callback_with_header_calls++;
|
||||
};
|
||||
|
||||
any_service_callback_.set(callback_with_header);
|
||||
EXPECT_NO_THROW(
|
||||
EXPECT_EQ(nullptr, any_service_callback_.dispatch(nullptr, request_header_, request_)));
|
||||
EXPECT_EQ(callback_with_header_calls, 1);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ void construct_with_null_allocator()
|
||||
TEST(AnySubscriptionCallback, null_allocator) {
|
||||
EXPECT_THROW(
|
||||
construct_with_null_allocator(),
|
||||
std::runtime_error);
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, construct_destruct) {
|
||||
@@ -95,44 +95,69 @@ TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) {
|
||||
// Parameterized test to test across all callback types and dispatch types.
|
||||
//
|
||||
|
||||
// Type adapter to be used in tests.
|
||||
struct MyEmpty {};
|
||||
|
||||
template<>
|
||||
struct rclcpp::TypeAdapter<MyEmpty, test_msgs::msg::Empty>
|
||||
{
|
||||
using is_specialized = std::true_type;
|
||||
using custom_type = MyEmpty;
|
||||
using ros_message_type = test_msgs::msg::Empty;
|
||||
|
||||
static
|
||||
void
|
||||
convert_to_ros_message(const custom_type &, ros_message_type &)
|
||||
{}
|
||||
|
||||
static
|
||||
void
|
||||
convert_to_custom(const ros_message_type &, custom_type &)
|
||||
{}
|
||||
};
|
||||
|
||||
using MyTA = rclcpp::TypeAdapter<MyEmpty, test_msgs::msg::Empty>;
|
||||
|
||||
template<typename MessageT>
|
||||
class InstanceContextImpl
|
||||
{
|
||||
public:
|
||||
InstanceContextImpl() = default;
|
||||
virtual ~InstanceContextImpl() = default;
|
||||
|
||||
explicit InstanceContextImpl(rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc)
|
||||
explicit InstanceContextImpl(rclcpp::AnySubscriptionCallback<MessageT> asc)
|
||||
: any_subscription_callback_(asc)
|
||||
{}
|
||||
|
||||
virtual
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
|
||||
rclcpp::AnySubscriptionCallback<MessageT>
|
||||
get_any_subscription_callback_to_test() const
|
||||
{
|
||||
return any_subscription_callback_;
|
||||
}
|
||||
|
||||
protected:
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> any_subscription_callback_;
|
||||
rclcpp::AnySubscriptionCallback<MessageT> any_subscription_callback_;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
class InstanceContext
|
||||
{
|
||||
public:
|
||||
InstanceContext(const std::string & name, std::shared_ptr<InstanceContextImpl> impl)
|
||||
InstanceContext(const std::string & name, std::shared_ptr<InstanceContextImpl<MessageT>> impl)
|
||||
: name(name), impl_(impl)
|
||||
{}
|
||||
|
||||
InstanceContext(
|
||||
const std::string & name,
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc)
|
||||
: name(name), impl_(std::make_shared<InstanceContextImpl>(asc))
|
||||
rclcpp::AnySubscriptionCallback<MessageT> asc)
|
||||
: name(name), impl_(std::make_shared<InstanceContextImpl<MessageT>>(asc))
|
||||
{}
|
||||
|
||||
InstanceContext(const InstanceContext & other)
|
||||
: InstanceContext(other.name, other.impl_) {}
|
||||
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
|
||||
rclcpp::AnySubscriptionCallback<MessageT>
|
||||
get_any_subscription_callback_to_test() const
|
||||
{
|
||||
return impl_->get_any_subscription_callback_to_test();
|
||||
@@ -141,12 +166,17 @@ public:
|
||||
std::string name;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<InstanceContextImpl> impl_;
|
||||
std::shared_ptr<InstanceContextImpl<MessageT>> impl_;
|
||||
};
|
||||
|
||||
class DispatchTests
|
||||
: public TestAnySubscriptionCallback,
|
||||
public ::testing::WithParamInterface<InstanceContext>
|
||||
public ::testing::WithParamInterface<InstanceContext<test_msgs::msg::Empty>>
|
||||
{};
|
||||
|
||||
class DispatchTestsWithTA
|
||||
: public TestAnySubscriptionCallback,
|
||||
public ::testing::WithParamInterface<InstanceContext<MyTA>>
|
||||
{};
|
||||
|
||||
auto
|
||||
@@ -155,57 +185,67 @@ format_parameter(const ::testing::TestParamInfo<DispatchTests::ParamType> & info
|
||||
return info.param.name;
|
||||
}
|
||||
|
||||
// Testing dispatch with shared_ptr<MessageT> as input
|
||||
TEST_P(DispatchTests, test_inter_shared_dispatch) {
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
|
||||
any_subscription_callback_to_test.dispatch(msg_shared_ptr_, message_info_);
|
||||
auto
|
||||
format_parameter_with_ta(const ::testing::TestParamInfo<DispatchTestsWithTA::ParamType> & info)
|
||||
{
|
||||
return info.param.name;
|
||||
}
|
||||
|
||||
// Testing dispatch with shared_ptr<const MessageT> as input
|
||||
TEST_P(DispatchTests, test_intra_shared_dispatch) {
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
|
||||
any_subscription_callback_to_test.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
}
|
||||
#define PARAMETERIZED_TESTS(DispatchTests_name) \
|
||||
/* Testing dispatch with shared_ptr<MessageT> as input */ \
|
||||
TEST_P(DispatchTests_name, test_inter_shared_dispatch) { \
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test(); \
|
||||
any_subscription_callback_to_test.dispatch(msg_shared_ptr_, message_info_); \
|
||||
} \
|
||||
\
|
||||
/* Testing dispatch with shared_ptr<const MessageT> as input */ \
|
||||
TEST_P(DispatchTests_name, test_intra_shared_dispatch) { \
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test(); \
|
||||
any_subscription_callback_to_test.dispatch_intra_process(msg_shared_ptr_, message_info_); \
|
||||
} \
|
||||
\
|
||||
/* Testing dispatch with unique_ptr<MessageT> as input */ \
|
||||
TEST_P(DispatchTests_name, test_intra_unique_dispatch) { \
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test(); \
|
||||
any_subscription_callback_to_test.dispatch_intra_process(get_unique_ptr_msg(), message_info_); \
|
||||
}
|
||||
|
||||
// Testing dispatch with unique_ptr<MessageT> as input
|
||||
TEST_P(DispatchTests, test_intra_unique_dispatch) {
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
|
||||
any_subscription_callback_to_test.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
|
||||
}
|
||||
PARAMETERIZED_TESTS(DispatchTests)
|
||||
PARAMETERIZED_TESTS(DispatchTestsWithTA)
|
||||
|
||||
// Generic classes for testing callbacks using std::bind to class methods.
|
||||
template<typename ... CallbackArgs>
|
||||
class BindContextImpl : public InstanceContextImpl
|
||||
template<typename MessageT, typename ... CallbackArgs>
|
||||
class BindContextImpl : public InstanceContextImpl<MessageT>
|
||||
{
|
||||
static constexpr size_t number_of_callback_args{sizeof...(CallbackArgs)};
|
||||
|
||||
public:
|
||||
using InstanceContextImpl::InstanceContextImpl;
|
||||
using InstanceContextImpl<MessageT>::InstanceContextImpl;
|
||||
virtual ~BindContextImpl() = default;
|
||||
|
||||
void on_message(CallbackArgs ...) const {}
|
||||
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
|
||||
rclcpp::AnySubscriptionCallback<MessageT>
|
||||
get_any_subscription_callback_to_test() const override
|
||||
{
|
||||
if constexpr (number_of_callback_args == 1) {
|
||||
return rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
return rclcpp::AnySubscriptionCallback<MessageT>().set(
|
||||
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1)
|
||||
);
|
||||
} else {
|
||||
return rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
return rclcpp::AnySubscriptionCallback<MessageT>().set(
|
||||
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1, std::placeholders::_2)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ... CallbackArgs>
|
||||
class BindContext : public InstanceContext
|
||||
template<typename MessageT, typename ... CallbackArgs>
|
||||
class BindContext : public InstanceContext<MessageT>
|
||||
{
|
||||
public:
|
||||
explicit BindContext(const std::string & name)
|
||||
: InstanceContext(name, std::make_shared<BindContextImpl<CallbackArgs ...>>())
|
||||
: InstanceContext<MessageT>(name, std::make_shared<BindContextImpl<MessageT, CallbackArgs ...>>())
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -232,13 +272,53 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
const_ref_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<const test_msgs::msg::Empty &>("bind_method"),
|
||||
BindContext<const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
|
||||
BindContext<test_msgs::msg::Empty, const test_msgs::msg::Empty &>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
void const_ref_ta_free_func(const MyEmpty &) {}
|
||||
void const_ref_ta_w_info_free_func(const MyEmpty &, const rclcpp::MessageInfo &) {}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ConstRefTACallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const MyEmpty &) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const MyEmpty &, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const test_msgs::msg::Empty &) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const test_msgs::msg::Empty &, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, const MyEmpty &>("bind_method_ta"),
|
||||
BindContext<MyTA, const MyEmpty &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, const test_msgs::msg::Empty &>("bind_method"),
|
||||
BindContext<MyTA, const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
//
|
||||
// Versions of `std::unique_ptr<MessageT, MessageDeleter>`
|
||||
//
|
||||
@@ -264,13 +344,56 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
unique_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
BindContext<test_msgs::msg::Empty, std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<
|
||||
test_msgs::msg::Empty,
|
||||
std::unique_ptr<test_msgs::msg::Empty>,
|
||||
const rclcpp::MessageInfo &
|
||||
>("bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
void unique_ptr_ta_free_func(std::unique_ptr<MyEmpty>) {}
|
||||
void unique_ptr_ta_w_info_free_func(std::unique_ptr<MyEmpty>, const rclcpp::MessageInfo &) {}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UniquePtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<MyEmpty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<MyEmpty>, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<test_msgs::msg::Empty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>>("bind_method_ta"),
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
//
|
||||
// Versions of `std::shared_ptr<const MessageT>`
|
||||
//
|
||||
@@ -296,13 +419,56 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
BindContext<test_msgs::msg::Empty, std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, std::shared_ptr<const test_msgs::msg::Empty>,
|
||||
const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
void shared_const_ptr_ta_free_func(std::shared_ptr<const MyEmpty>) {}
|
||||
void shared_const_ptr_ta_w_info_free_func(
|
||||
std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SharedConstPtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const MyEmpty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const test_msgs::msg::Empty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, std::shared_ptr<const MyEmpty>>("bind_method_ta"),
|
||||
BindContext<MyTA, std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<MyTA, std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
//
|
||||
// Versions of `const std::shared_ptr<const MessageT> &`
|
||||
//
|
||||
@@ -328,13 +494,58 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
const_ref_shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
|
||||
BindContext<const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &>(
|
||||
BindContext<test_msgs::msg::Empty,
|
||||
const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, const std::shared_ptr<const test_msgs::msg::Empty> &,
|
||||
const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
void const_ref_shared_const_ptr_ta_free_func(const std::shared_ptr<const MyEmpty> &) {}
|
||||
void const_ref_shared_const_ptr_ta_w_info_free_func(
|
||||
const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ConstRefSharedConstPtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const MyEmpty> &) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const test_msgs::msg::Empty> &) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, const std::shared_ptr<const MyEmpty> &>("bind_method_ta"),
|
||||
BindContext<MyTA, const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
|
||||
BindContext<MyTA, const std::shared_ptr<const test_msgs::msg::Empty> &,
|
||||
const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
//
|
||||
// Versions of `std::shared_ptr<MessageT>`
|
||||
//
|
||||
@@ -360,9 +571,52 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
shared_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
BindContext<test_msgs::msg::Empty, std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, std::shared_ptr<test_msgs::msg::Empty>,
|
||||
const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
void shared_ptr_ta_free_func(std::shared_ptr<MyEmpty>) {}
|
||||
void shared_ptr_ta_w_info_free_func(
|
||||
std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SharedPtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<MyEmpty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, std::shared_ptr<MyEmpty>>("bind_method_ta"),
|
||||
BindContext<MyTA, std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<MyTA, std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
@@ -52,8 +52,8 @@ class PublisherBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
|
||||
|
||||
PublisherBase()
|
||||
: qos(rclcpp::QoS(10)),
|
||||
explicit PublisherBase(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos),
|
||||
topic_name("topic")
|
||||
{}
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
rclcpp::QoS
|
||||
get_actual_qos() const
|
||||
{
|
||||
return qos;
|
||||
return qos_profile;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
rclcpp::QoS qos;
|
||||
rclcpp::QoS qos_profile;
|
||||
std::string topic_name;
|
||||
uint64_t intra_process_publisher_id_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
@@ -111,9 +111,9 @@ public:
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
|
||||
|
||||
Publisher()
|
||||
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: PublisherBase(qos)
|
||||
{
|
||||
qos = rclcpp::QoS(10);
|
||||
auto allocator = std::make_shared<Alloc>();
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
}
|
||||
@@ -136,7 +136,10 @@ namespace buffers
|
||||
{
|
||||
namespace mock
|
||||
{
|
||||
template<typename MessageT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename MessageDeleter = std::default_delete<MessageT>>
|
||||
class IntraProcessBuffer
|
||||
{
|
||||
public:
|
||||
@@ -191,8 +194,8 @@ class SubscriptionIntraProcessBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
|
||||
|
||||
SubscriptionIntraProcessBase()
|
||||
: qos_profile(rmw_qos_profile_default), topic_name("topic")
|
||||
explicit SubscriptionIntraProcessBase(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos), topic_name("topic")
|
||||
{}
|
||||
|
||||
virtual ~SubscriptionIntraProcessBase() {}
|
||||
@@ -200,7 +203,7 @@ public:
|
||||
virtual bool
|
||||
use_take_shared_method() const = 0;
|
||||
|
||||
rmw_qos_profile_t
|
||||
QoS
|
||||
get_actual_qos()
|
||||
{
|
||||
return qos_profile;
|
||||
@@ -212,18 +215,21 @@ public:
|
||||
return topic_name;
|
||||
}
|
||||
|
||||
rmw_qos_profile_t qos_profile;
|
||||
rclcpp::QoS qos_profile;
|
||||
const char * topic_name;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
|
||||
|
||||
SubscriptionIntraProcess()
|
||||
: take_shared_method(false)
|
||||
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
|
||||
: SubscriptionIntraProcessBase(qos), take_shared_method(false)
|
||||
{
|
||||
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
|
||||
}
|
||||
@@ -259,6 +265,25 @@ public:
|
||||
typename rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>::UniquePtr buffer;
|
||||
};
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mock
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
@@ -267,12 +292,14 @@ public:
|
||||
#define RCLCPP__PUBLISHER_HPP_
|
||||
#define RCLCPP__PUBLISHER_BASE_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
|
||||
// Force ipm to use our mock publisher class.
|
||||
#define Publisher mock::Publisher
|
||||
#define PublisherBase mock::PublisherBase
|
||||
#define IntraProcessBuffer mock::IntraProcessBuffer
|
||||
#define SubscriptionIntraProcessBase mock::SubscriptionIntraProcessBase
|
||||
#define SubscriptionIntraProcessBuffer mock::SubscriptionIntraProcessBuffer
|
||||
#define SubscriptionIntraProcess mock::SubscriptionIntraProcess
|
||||
#include "../src/rclcpp/intra_process_manager.cpp"
|
||||
#undef Publisher
|
||||
@@ -304,7 +331,7 @@ void Publisher<T, Alloc>::publish(MessageUniquePtr msg)
|
||||
ipm->template do_intra_process_publish<T, Alloc>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
*message_allocator_);
|
||||
}
|
||||
|
||||
} // namespace mock
|
||||
@@ -332,15 +359,12 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
p1->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p2 = std::make_shared<PublisherT>();
|
||||
p2->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
|
||||
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
p2->topic_name = "different_topic_name";
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
s1->qos_profile.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
auto p2_id = ipm->add_publisher(p2);
|
||||
@@ -356,11 +380,9 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
ASSERT_EQ(0u, p2_subs);
|
||||
ASSERT_EQ(0u, non_existing_pub_subs);
|
||||
|
||||
auto p3 = std::make_shared<PublisherT>();
|
||||
p3->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
|
||||
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
s2->qos_profile.reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
|
||||
|
||||
auto s2_id = ipm->add_subscription(s2);
|
||||
auto p3_id = ipm->add_publisher(p3);
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
// Copyright 2021 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 <gmock/gmock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
|
||||
|
||||
// For demonstration purposes only, not necessary for allocator_traits
|
||||
static uint32_t num_allocs = 0;
|
||||
static uint32_t num_deallocs = 0;
|
||||
// A very simple custom allocator. Counts calls to allocate and deallocate.
|
||||
template<typename T>
|
||||
struct MyAllocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
||||
|
||||
MyAllocator() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
~MyAllocator() noexcept {}
|
||||
|
||||
template<typename U>
|
||||
MyAllocator(const MyAllocator<U> &) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
T * allocate(size_t size, const void * = 0)
|
||||
{
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
num_allocs++;
|
||||
return static_cast<T *>(std::malloc(size * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T * ptr, size_t size)
|
||||
{
|
||||
(void)size;
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
num_deallocs++;
|
||||
std::free(ptr);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
struct rebind
|
||||
{
|
||||
typedef MyAllocator<U> other;
|
||||
};
|
||||
};
|
||||
|
||||
// Explicit specialization for void
|
||||
template<>
|
||||
struct MyAllocator<void>
|
||||
{
|
||||
public:
|
||||
using value_type = void;
|
||||
using pointer = void *;
|
||||
using const_pointer = const void *;
|
||||
|
||||
MyAllocator() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
~MyAllocator() noexcept {}
|
||||
|
||||
template<typename U>
|
||||
MyAllocator(const MyAllocator<U> &) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
struct rebind
|
||||
{
|
||||
typedef MyAllocator<U> other;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool operator==(
|
||||
const MyAllocator<T> &,
|
||||
const MyAllocator<U> &) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool operator!=(
|
||||
const MyAllocator<T> &,
|
||||
const MyAllocator<U> &) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<
|
||||
typename PublishedMessageAllocatorT,
|
||||
typename PublisherAllocatorT,
|
||||
typename SubscribedMessageAllocatorT,
|
||||
typename SubscriptionAllocatorT,
|
||||
typename MessageMemoryStrategyAllocatorT,
|
||||
typename MemoryStrategyAllocatorT,
|
||||
typename ExpectedExceptionT
|
||||
>
|
||||
void
|
||||
do_custom_allocator_test(
|
||||
PublishedMessageAllocatorT published_message_allocator,
|
||||
PublisherAllocatorT publisher_allocator,
|
||||
SubscribedMessageAllocatorT /* subscribed_message_allocator */, // intentionally unused
|
||||
SubscriptionAllocatorT subscription_allocator,
|
||||
MessageMemoryStrategyAllocatorT message_memory_strategy,
|
||||
MemoryStrategyAllocatorT memory_strategy_allocator)
|
||||
{
|
||||
using PublishedMessageAllocTraits =
|
||||
rclcpp::allocator::AllocRebind<test_msgs::msg::Empty, PublishedMessageAllocatorT>;
|
||||
using PublishedMessageAlloc = typename PublishedMessageAllocTraits::allocator_type;
|
||||
using PublishedMessageDeleter =
|
||||
rclcpp::allocator::Deleter<PublishedMessageAlloc, test_msgs::msg::Empty>;
|
||||
|
||||
using SubscribedMessageAllocTraits =
|
||||
rclcpp::allocator::AllocRebind<test_msgs::msg::Empty, SubscribedMessageAllocatorT>;
|
||||
using SubscribedMessageAlloc = typename SubscribedMessageAllocTraits::allocator_type;
|
||||
using SubscribedMessageDeleter =
|
||||
rclcpp::allocator::Deleter<SubscribedMessageAlloc, test_msgs::msg::Empty>;
|
||||
|
||||
// init and node
|
||||
auto context = std::make_shared<rclcpp::Context>();
|
||||
context->init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>(
|
||||
"custom_allocator_test",
|
||||
rclcpp::NodeOptions().context(context).use_intra_process_comms(true));
|
||||
|
||||
// publisher
|
||||
auto shared_publisher_allocator = std::make_shared<PublisherAllocatorT>(publisher_allocator);
|
||||
rclcpp::PublisherOptionsWithAllocator<PublisherAllocatorT> publisher_options;
|
||||
publisher_options.allocator = shared_publisher_allocator;
|
||||
auto publisher =
|
||||
node->create_publisher<test_msgs::msg::Empty>("custom_allocator_test", 10, publisher_options);
|
||||
|
||||
// callback for subscription
|
||||
uint32_t counter = 0;
|
||||
std::promise<std::unique_ptr<test_msgs::msg::Empty, SubscribedMessageDeleter>> received_message;
|
||||
auto received_message_future = received_message.get_future();
|
||||
auto callback =
|
||||
[&counter, &received_message](
|
||||
std::unique_ptr<test_msgs::msg::Empty, SubscribedMessageDeleter> msg)
|
||||
{
|
||||
++counter;
|
||||
received_message.set_value(std::move(msg));
|
||||
};
|
||||
|
||||
// subscription
|
||||
auto shared_subscription_allocator =
|
||||
std::make_shared<SubscriptionAllocatorT>(subscription_allocator);
|
||||
rclcpp::SubscriptionOptionsWithAllocator<SubscriptionAllocatorT> subscription_options;
|
||||
subscription_options.allocator = shared_subscription_allocator;
|
||||
auto shared_message_strategy_allocator =
|
||||
std::make_shared<MessageMemoryStrategyAllocatorT>(message_memory_strategy);
|
||||
auto msg_mem_strat = std::make_shared<
|
||||
rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
test_msgs::msg::Empty,
|
||||
MessageMemoryStrategyAllocatorT
|
||||
>
|
||||
>(shared_message_strategy_allocator);
|
||||
using CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<decltype(callback)>::type;
|
||||
auto subscriber = node->create_subscription<
|
||||
test_msgs::msg::Empty,
|
||||
decltype(callback),
|
||||
SubscriptionAllocatorT,
|
||||
rclcpp::Subscription<CallbackMessageT, SubscriptionAllocatorT>,
|
||||
rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
MessageMemoryStrategyAllocatorT
|
||||
>
|
||||
>(
|
||||
"custom_allocator_test",
|
||||
10,
|
||||
std::forward<decltype(callback)>(callback),
|
||||
subscription_options,
|
||||
msg_mem_strat);
|
||||
|
||||
// executor memory strategy
|
||||
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;
|
||||
auto shared_memory_strategy_allocator = std::make_shared<MemoryStrategyAllocatorT>(
|
||||
memory_strategy_allocator);
|
||||
std::shared_ptr<rclcpp::memory_strategy::MemoryStrategy> memory_strategy =
|
||||
std::make_shared<AllocatorMemoryStrategy<MemoryStrategyAllocatorT>>(
|
||||
shared_memory_strategy_allocator);
|
||||
|
||||
// executor
|
||||
rclcpp::ExecutorOptions options;
|
||||
options.memory_strategy = memory_strategy;
|
||||
options.context = context;
|
||||
rclcpp::executors::SingleThreadedExecutor executor(options);
|
||||
|
||||
executor.add_node(node);
|
||||
|
||||
// rebind message_allocator to ensure correct type
|
||||
PublishedMessageDeleter message_deleter;
|
||||
PublishedMessageAlloc rebound_message_allocator = published_message_allocator;
|
||||
rclcpp::allocator::set_allocator_for_deleter(&message_deleter, &rebound_message_allocator);
|
||||
|
||||
// allocate a message
|
||||
auto ptr = PublishedMessageAllocTraits::allocate(rebound_message_allocator, 1);
|
||||
PublishedMessageAllocTraits::construct(rebound_message_allocator, ptr);
|
||||
std::unique_ptr<test_msgs::msg::Empty, PublishedMessageDeleter> msg(ptr, message_deleter);
|
||||
|
||||
// publisher and receive
|
||||
if constexpr (std::is_same_v<ExpectedExceptionT, void>) {
|
||||
// no exception expected
|
||||
EXPECT_NO_THROW(
|
||||
{
|
||||
publisher->publish(std::move(msg));
|
||||
executor.spin_until_future_complete(received_message_future, std::chrono::seconds(10));
|
||||
});
|
||||
EXPECT_EQ(ptr, received_message_future.get().get());
|
||||
EXPECT_EQ(1u, counter);
|
||||
} else {
|
||||
// exception expected
|
||||
EXPECT_THROW(
|
||||
{
|
||||
publisher->publish(std::move(msg));
|
||||
executor.spin_until_future_complete(received_message_future, std::chrono::seconds(10));
|
||||
}, ExpectedExceptionT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This tests the case where a custom allocator is used correctly, i.e. the same
|
||||
custom allocator on both sides.
|
||||
*/
|
||||
TEST(TestIntraProcessManagerWithAllocators, custom_allocator) {
|
||||
using PublishedMessageAllocatorT = std::allocator<void>;
|
||||
using PublisherAllocatorT = std::allocator<void>;
|
||||
using SubscribedMessageAllocatorT = std::allocator<void>;
|
||||
using SubscriptionAllocatorT = std::allocator<void>;
|
||||
using MessageMemoryStrategyAllocatorT = std::allocator<void>;
|
||||
using MemoryStrategyAllocatorT = std::allocator<void>;
|
||||
auto allocator = std::allocator<void>();
|
||||
do_custom_allocator_test<
|
||||
PublishedMessageAllocatorT,
|
||||
PublisherAllocatorT,
|
||||
SubscribedMessageAllocatorT,
|
||||
SubscriptionAllocatorT,
|
||||
MessageMemoryStrategyAllocatorT,
|
||||
MemoryStrategyAllocatorT,
|
||||
void // no exception expected
|
||||
>(allocator, allocator, allocator, allocator, allocator, allocator);
|
||||
}
|
||||
|
||||
/*
|
||||
This tests the case where a custom allocator is used incorrectly, i.e. different
|
||||
custom allocators on both sides.
|
||||
*/
|
||||
TEST(TestIntraProcessManagerWithAllocators, custom_allocator_wrong) {
|
||||
// explicitly use a different allocator here to provoke a failure
|
||||
using PublishedMessageAllocatorT = std::allocator<void>;
|
||||
using PublisherAllocatorT = std::allocator<void>;
|
||||
using SubscribedMessageAllocatorT = MyAllocator<void>;
|
||||
using SubscriptionAllocatorT = MyAllocator<void>;
|
||||
using MessageMemoryStrategyAllocatorT = MyAllocator<void>;
|
||||
using MemoryStrategyAllocatorT = std::allocator<void>;
|
||||
auto allocator = std::allocator<void>();
|
||||
auto my_allocator = MyAllocator<void>();
|
||||
do_custom_allocator_test<
|
||||
PublishedMessageAllocatorT,
|
||||
PublisherAllocatorT,
|
||||
SubscribedMessageAllocatorT,
|
||||
SubscriptionAllocatorT,
|
||||
MessageMemoryStrategyAllocatorT,
|
||||
MemoryStrategyAllocatorT,
|
||||
std::runtime_error // expected exception
|
||||
>(allocator, allocator, my_allocator, my_allocator, my_allocator, allocator);
|
||||
}
|
||||
@@ -43,7 +43,42 @@ TEST_F(TestLoanedMessage, initialize) {
|
||||
auto node = std::make_shared<rclcpp::Node>("loaned_message_test_node");
|
||||
auto pub = node->create_publisher<MessageT>("loaned_message_test_topic", 1);
|
||||
|
||||
auto loaned_msg = rclcpp::LoanedMessage<MessageT>(pub.get(), pub->get_allocator());
|
||||
// suppress deprecated function warning
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
auto pub_allocator = pub->get_allocator();
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// suppress deprecated function warning
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
auto loaned_msg = rclcpp::LoanedMessage<MessageT>(pub.get(), pub_allocator);
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_TRUE(loaned_msg.is_valid());
|
||||
loaned_msg.get().float32_value = 42.0f;
|
||||
ASSERT_EQ(42.0f, loaned_msg.get().float32_value);
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -333,9 +336,14 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
// no default, no initial
|
||||
const std::string parameter_name = "parameter"_unq;
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter"_unq, rclcpp::ParameterValue{}, descriptor);
|
||||
parameter_name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
// Does not throw if unset before access
|
||||
EXPECT_EQ(
|
||||
rclcpp::PARAMETER_NOT_SET,
|
||||
node->get_parameter(parameter_name).get_parameter_value().get_type());
|
||||
}
|
||||
{
|
||||
// int default, no initial
|
||||
@@ -2508,6 +2516,59 @@ TEST_F(TestNode, get_parameter_types_undeclared_parameters_allowed) {
|
||||
}
|
||||
}
|
||||
|
||||
// test declare parameter with int, int64_t, float and double vector
|
||||
TEST_F(TestNode, declare_parameter_with_vector) {
|
||||
auto node = std::make_shared<rclcpp::Node>(
|
||||
"test_declare_parameter_with_vector"_unq,
|
||||
rclcpp::NodeOptions().allow_undeclared_parameters(true));
|
||||
{
|
||||
// declare parameter and then get types to check
|
||||
auto name1 = "parameter"_unq;
|
||||
auto name2 = "parameter"_unq;
|
||||
auto name3 = "parameter"_unq;
|
||||
auto name4 = "parameter"_unq;
|
||||
|
||||
node->declare_parameter(name1, std::vector<int>{});
|
||||
node->declare_parameter(name2, std::vector<int64_t>{});
|
||||
node->declare_parameter(name3, std::vector<float>{});
|
||||
node->declare_parameter(name4, std::vector<double>{});
|
||||
|
||||
EXPECT_TRUE(node->has_parameter(name1));
|
||||
EXPECT_TRUE(node->has_parameter(name2));
|
||||
EXPECT_TRUE(node->has_parameter(name3));
|
||||
EXPECT_TRUE(node->has_parameter(name4));
|
||||
|
||||
auto results = node->get_parameter_types({name1, name2, name3, name4});
|
||||
EXPECT_EQ(results.size(), 4u);
|
||||
EXPECT_EQ(results[0], rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY);
|
||||
EXPECT_EQ(results[1], rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY);
|
||||
EXPECT_EQ(results[2], rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY);
|
||||
EXPECT_EQ(results[3], rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY);
|
||||
}
|
||||
{
|
||||
// declare parameter and then get values to check
|
||||
auto name1 = "parameter"_unq;
|
||||
auto name2 = "parameter"_unq;
|
||||
auto name3 = "parameter"_unq;
|
||||
auto name4 = "parameter"_unq;
|
||||
|
||||
int64_t bigger_than_int = INT64_MAX - 42;
|
||||
double bigger_than_float = std::numeric_limits<double>::max() - 42;
|
||||
node->declare_parameter(name1, std::vector<int>{1, 2});
|
||||
node->declare_parameter(name2, std::vector<int64_t>{3, bigger_than_int});
|
||||
node->declare_parameter(name3, std::vector<float>{1.5f, 2.8f});
|
||||
node->declare_parameter(name4, std::vector<double>{3.0, bigger_than_float});
|
||||
|
||||
std::vector<rclcpp::Parameter> expected = {
|
||||
{name1, std::vector<int>{1, 2}},
|
||||
{name2, std::vector<int64_t>{3, bigger_than_int}},
|
||||
{name3, std::vector<float>{1.5f, 2.8f}},
|
||||
{name4, std::vector<double>{3.0, bigger_than_float}},
|
||||
};
|
||||
EXPECT_EQ(node->get_parameters({name1, name2, name3, name4}), expected);
|
||||
}
|
||||
}
|
||||
|
||||
void expect_qos_profile_eq(
|
||||
const rmw_qos_profile_t & qos1, const rmw_qos_profile_t & qos2, bool is_publisher)
|
||||
{
|
||||
@@ -2525,6 +2586,34 @@ void expect_qos_profile_eq(
|
||||
EXPECT_EQ(qos1.liveliness_lease_duration.nsec, qos2.liveliness_lease_duration.nsec);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr std::chrono::nanoseconds DEFAULT_EVENT_TIMEOUT = std::chrono::seconds(3);
|
||||
|
||||
constexpr std::chrono::nanoseconds DEFAULT_EVENT_SLEEP_PERIOD = std::chrono::milliseconds(100);
|
||||
|
||||
bool wait_for_event(
|
||||
std::shared_ptr<rclcpp::Node> node,
|
||||
std::function<bool()> predicate,
|
||||
std::chrono::nanoseconds timeout = DEFAULT_EVENT_TIMEOUT,
|
||||
std::chrono::nanoseconds sleep_period = DEFAULT_EVENT_SLEEP_PERIOD)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds time_slept(0);
|
||||
|
||||
bool predicate_result;
|
||||
while (!(predicate_result = predicate()) && time_slept < timeout) {
|
||||
rclcpp::Event::SharedPtr graph_event = node->get_graph_event();
|
||||
node->wait_for_graph_change(graph_event, sleep_period);
|
||||
time_slept = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now() - start);
|
||||
}
|
||||
return predicate_result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// test that calling get_publishers_info_by_topic and get_subscriptions_info_by_topic
|
||||
TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
@@ -2556,6 +2645,10 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
|
||||
};
|
||||
rclcpp::QoS qos = rclcpp::QoS(qos_initialization, rmw_qos_profile_default);
|
||||
auto publisher = node->create_publisher<test_msgs::msg::BasicTypes>(topic_name, qos);
|
||||
// Wait for the underlying RMW implementation to catch up with graph changes
|
||||
auto topic_is_published =
|
||||
[&]() {return node->get_publishers_info_by_topic(fq_topic_name).size() > 0u;};
|
||||
ASSERT_TRUE(wait_for_event(node, topic_is_published));
|
||||
// List should have one item
|
||||
auto publisher_list = node->get_publishers_info_by_topic(fq_topic_name);
|
||||
ASSERT_EQ(publisher_list.size(), (size_t)1);
|
||||
@@ -2596,7 +2689,10 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
|
||||
};
|
||||
auto subscriber =
|
||||
node->create_subscription<test_msgs::msg::BasicTypes>(topic_name, qos2, callback);
|
||||
|
||||
// Wait for the underlying RMW implementation to catch up with graph changes
|
||||
auto topic_is_subscribed =
|
||||
[&]() {return node->get_subscriptions_info_by_topic(fq_topic_name).size() > 0u;};
|
||||
ASSERT_TRUE(wait_for_event(node, topic_is_subscribed));
|
||||
// Both lists should have one item
|
||||
publisher_list = node->get_publishers_info_by_topic(fq_topic_name);
|
||||
auto subscription_list = node->get_subscriptions_info_by_topic(fq_topic_name);
|
||||
@@ -2761,9 +2857,20 @@ TEST_F(TestNode, static_and_dynamic_typing) {
|
||||
EXPECT_EQ("hello!", param);
|
||||
}
|
||||
{
|
||||
auto param = node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type());
|
||||
// Throws if not set before access
|
||||
EXPECT_THROW(
|
||||
node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER),
|
||||
rclcpp::exceptions::NoParameterOverrideProvided);
|
||||
node->get_parameter("integer_override_not_given"),
|
||||
rclcpp::exceptions::ParameterUninitializedException);
|
||||
}
|
||||
{
|
||||
auto param = node->declare_parameter("integer_set_after_declare", rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type());
|
||||
auto result = node->set_parameter(rclcpp::Parameter{"integer_set_after_declare", 44});
|
||||
ASSERT_TRUE(result.successful) << result.reason;
|
||||
auto get_param = node->get_parameter("integer_set_after_declare");
|
||||
EXPECT_EQ(44, get_param.as_int());
|
||||
}
|
||||
{
|
||||
EXPECT_THROW(
|
||||
|
||||
335
rclcpp/test/rclcpp/test_publisher_with_type_adapter.cpp
Normal file
335
rclcpp/test/rclcpp/test_publisher_with_type_adapter.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
// Copyright 2021 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 <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/loaned_message.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "../mocking_utils/patch.hpp"
|
||||
#include "../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "rclcpp/msg/string.hpp"
|
||||
|
||||
|
||||
#ifdef RMW_IMPLEMENTATION
|
||||
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
|
||||
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
|
||||
#else
|
||||
# define CLASSNAME(NAME, SUFFIX) NAME
|
||||
#endif
|
||||
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static const int g_max_loops = 200;
|
||||
static const std::chrono::milliseconds g_sleep_per_loop(10);
|
||||
|
||||
|
||||
class TestPublisher : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
if (!rclcpp::ok()) {
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns", node_options);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class CLASSNAME (test_intra_process_within_one_node, RMW_IMPLEMENTATION) : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<>
|
||||
struct TypeAdapter<std::string, rclcpp::msg::String>
|
||||
{
|
||||
using is_specialized = std::true_type;
|
||||
using custom_type = std::string;
|
||||
using ros_message_type = rclcpp::msg::String;
|
||||
|
||||
static void
|
||||
convert_to_ros_message(
|
||||
const custom_type & source,
|
||||
ros_message_type & destination)
|
||||
{
|
||||
destination.data = source;
|
||||
}
|
||||
|
||||
static void
|
||||
convert_to_custom(
|
||||
const ros_message_type & source,
|
||||
custom_type & destination)
|
||||
{
|
||||
destination = source.data;
|
||||
}
|
||||
};
|
||||
|
||||
// Throws in conversion
|
||||
template<>
|
||||
struct TypeAdapter<int, rclcpp::msg::String>
|
||||
{
|
||||
using is_specialized = std::true_type;
|
||||
using custom_type = int;
|
||||
using ros_message_type = rclcpp::msg::String;
|
||||
|
||||
static void
|
||||
convert_to_ros_message(
|
||||
const custom_type & source,
|
||||
ros_message_type & destination)
|
||||
{
|
||||
(void) source;
|
||||
(void) destination;
|
||||
throw std::runtime_error("This should happen");
|
||||
}
|
||||
|
||||
static void
|
||||
convert_to_custom(
|
||||
const ros_message_type & source,
|
||||
custom_type & destination)
|
||||
{
|
||||
(void) source;
|
||||
(void) destination;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
/*
|
||||
* Testing publisher creation signatures with a type adapter.
|
||||
*/
|
||||
TEST_F(TestPublisher, various_creation_signatures) {
|
||||
for (auto is_intra_process : {true, false}) {
|
||||
rclcpp::NodeOptions options;
|
||||
options.use_intra_process_comms(is_intra_process);
|
||||
initialize(options);
|
||||
{
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
auto publisher = node->create_publisher<StringTypeAdapter>("topic", 42);
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
using StringTypeAdapter = rclcpp::adapt_type<std::string>::as<rclcpp::msg::String>;
|
||||
auto publisher = node->create_publisher<StringTypeAdapter>("topic", 42);
|
||||
(void)publisher;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing that conversion errors are passed up.
|
||||
*/
|
||||
TEST_F(TestPublisher, conversion_exception_is_passed_up) {
|
||||
using BadStringTypeAdapter = rclcpp::TypeAdapter<int, rclcpp::msg::String>;
|
||||
for (auto is_intra_process : {true, false}) {
|
||||
rclcpp::NodeOptions options;
|
||||
options.use_intra_process_comms(is_intra_process);
|
||||
initialize(options);
|
||||
auto pub = node->create_publisher<BadStringTypeAdapter>("topic_name", 1);
|
||||
EXPECT_THROW(pub->publish(1), std::runtime_error);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing that publisher sends type adapted types and ROS message types with intra proccess communications.
|
||||
*/
|
||||
TEST_F(
|
||||
CLASSNAME(test_intra_process_within_one_node, RMW_IMPLEMENTATION),
|
||||
check_type_adapted_message_is_sent_and_received_intra_process) {
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
const std::string message_data = "Message Data";
|
||||
const std::string topic_name = "topic_name";
|
||||
bool is_received;
|
||||
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String::SharedPtr msg,
|
||||
const rclcpp::MessageInfo & message_info
|
||||
) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"test_intra_process",
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
auto pub = node->create_publisher<StringTypeAdapter>(topic_name, 10);
|
||||
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
|
||||
|
||||
auto wait_for_message_to_be_received = [&is_received, &node]() {
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
int i = 0;
|
||||
executor.add_node(node);
|
||||
executor.spin_once(std::chrono::milliseconds(0));
|
||||
while (!is_received && i < g_max_loops) {
|
||||
printf("spin_node_once() - callback (1) expected - try %d/%d\n", ++i, g_max_loops);
|
||||
executor.spin_once(g_sleep_per_loop);
|
||||
}
|
||||
};
|
||||
{
|
||||
{ // std::string passed by reference
|
||||
is_received = false;
|
||||
pub->publish(message_data);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received();
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // unique pointer to std::string
|
||||
is_received = false;
|
||||
auto pu_message = std::make_unique<std::string>(message_data);
|
||||
pub->publish(std::move(pu_message));
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received();
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // ROS message passed by reference
|
||||
is_received = false;
|
||||
rclcpp::msg::String msg;
|
||||
msg.data = message_data;
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received();
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // unique ptr to ROS message
|
||||
is_received = false;
|
||||
auto pu_msg = std::make_unique<rclcpp::msg::String>();
|
||||
pu_msg->data = message_data;
|
||||
pub->publish(std::move(pu_msg));
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received();
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
/* TODO(audrow) Enable once loaned messages are supported for intra process communication
|
||||
{ // loaned ROS message
|
||||
// is_received = false;
|
||||
// std::allocator<void> allocator;
|
||||
// rclcpp::LoanedMessage<rclcpp::msg::String> loaned_msg(*pub, allocator);
|
||||
// loaned_msg.get().data = message_data;
|
||||
// pub->publish(std::move(loaned_msg));
|
||||
// ASSERT_FALSE(is_received);
|
||||
// wait_for_message_to_be_received();
|
||||
// ASSERT_TRUE(is_received);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing that publisher sends type adapted types and ROS message types with inter proccess communications.
|
||||
*/
|
||||
TEST_F(TestPublisher, check_type_adapted_message_is_sent_and_received) {
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
|
||||
initialize();
|
||||
|
||||
const std::string message_data = "Message Data";
|
||||
const std::string topic_name = "topic_name";
|
||||
|
||||
auto do_nothing = [](std::shared_ptr<const rclcpp::msg::String>) {FAIL();};
|
||||
auto pub = node->create_publisher<StringTypeAdapter>(topic_name, 1);
|
||||
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, do_nothing);
|
||||
|
||||
auto assert_no_message_was_received_yet = [sub]() {
|
||||
rclcpp::msg::String msg;
|
||||
rclcpp::MessageInfo msg_info;
|
||||
EXPECT_FALSE(sub->take(msg, msg_info));
|
||||
};
|
||||
auto assert_message_was_received = [sub, message_data]() {
|
||||
rclcpp::msg::String msg;
|
||||
rclcpp::MessageInfo msg_info;
|
||||
bool message_received = false;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
message_received = sub->take(msg, msg_info);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (!message_received && std::chrono::steady_clock::now() - start < 10s);
|
||||
EXPECT_TRUE(message_received);
|
||||
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
|
||||
};
|
||||
|
||||
{ // std::string passed by reference
|
||||
assert_no_message_was_received_yet();
|
||||
pub->publish(message_data);
|
||||
assert_message_was_received();
|
||||
}
|
||||
{ // unique pointer to std::string
|
||||
assert_no_message_was_received_yet();
|
||||
auto pu_message = std::make_unique<std::string>(message_data);
|
||||
pub->publish(std::move(pu_message));
|
||||
assert_message_was_received();
|
||||
}
|
||||
{ // ROS message passed by reference
|
||||
assert_no_message_was_received_yet();
|
||||
rclcpp::msg::String msg;
|
||||
msg.data = message_data;
|
||||
pub->publish(msg);
|
||||
assert_message_was_received();
|
||||
}
|
||||
{ // unique ptr to ROS message
|
||||
assert_no_message_was_received_yet();
|
||||
auto pu_msg = std::make_unique<rclcpp::msg::String>();
|
||||
pu_msg->data = message_data;
|
||||
pub->publish(std::move(pu_msg));
|
||||
assert_message_was_received();
|
||||
}
|
||||
{ // loaned ROS message
|
||||
assert_no_message_was_received_yet();
|
||||
std::allocator<void> allocator;
|
||||
rclcpp::LoanedMessage<rclcpp::msg::String> loaned_msg(*pub, allocator);
|
||||
loaned_msg.get().data = message_data;
|
||||
pub->publish(std::move(loaned_msg));
|
||||
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
|
||||
assert_message_was_received();
|
||||
}
|
||||
}
|
||||
@@ -510,7 +510,7 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
TEST_F(TestSubscription, get_network_flow_endpoints_errors) {
|
||||
initialize();
|
||||
const rclcpp::QoS subscription_qos(1);
|
||||
auto subscription_callback = [](const test_msgs::msg::Empty::SharedPtr msg) {
|
||||
auto subscription_callback = [](test_msgs::msg::Empty::SharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
|
||||
563
rclcpp/test/rclcpp/test_subscription_with_type_adapter.cpp
Normal file
563
rclcpp/test/rclcpp/test_subscription_with_type_adapter.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
// Copyright 2021 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 <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/loaned_message.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "../mocking_utils/patch.hpp"
|
||||
#include "../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "rclcpp/msg/string.hpp"
|
||||
|
||||
|
||||
#ifdef RMW_IMPLEMENTATION
|
||||
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
|
||||
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
|
||||
#else
|
||||
# define CLASSNAME(NAME, SUFFIX) NAME
|
||||
#endif
|
||||
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static const int g_max_loops = 200;
|
||||
static const std::chrono::milliseconds g_sleep_per_loop(10);
|
||||
|
||||
|
||||
class TestSubscription : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
if (!rclcpp::ok()) {
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns", node_options);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class CLASSNAME (test_intra_process_within_one_node, RMW_IMPLEMENTATION) : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<>
|
||||
struct TypeAdapter<std::string, rclcpp::msg::String>
|
||||
{
|
||||
using is_specialized = std::true_type;
|
||||
using custom_type = std::string;
|
||||
using ros_message_type = rclcpp::msg::String;
|
||||
|
||||
static void
|
||||
convert_to_ros_message(
|
||||
const custom_type & source,
|
||||
ros_message_type & destination)
|
||||
{
|
||||
destination.data = source;
|
||||
}
|
||||
|
||||
static void
|
||||
convert_to_custom(
|
||||
const ros_message_type & source,
|
||||
custom_type & destination)
|
||||
{
|
||||
destination = source.data;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
void wait_for_message_to_be_received(
|
||||
bool & is_received,
|
||||
const std::shared_ptr<rclcpp::Node> & node)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
executor.spin_once(std::chrono::milliseconds(0));
|
||||
int i = 0;
|
||||
while (!is_received && i < g_max_loops) {
|
||||
printf("spin_node_once() - callback (1) expected - try %d/%d\n", ++i, g_max_loops);
|
||||
executor.spin_once(g_sleep_per_loop);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Testing publisher creation signatures with a type adapter.
|
||||
*/
|
||||
TEST_F(TestSubscription, various_creation_signatures) {
|
||||
initialize();
|
||||
{
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
auto sub =
|
||||
node->create_subscription<StringTypeAdapter>("topic", 42, [](const std::string &) {});
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
using StringTypeAdapter = rclcpp::adapt_type<std::string>::as<rclcpp::msg::String>;
|
||||
auto sub =
|
||||
node->create_subscription<StringTypeAdapter>("topic", 42, [](const std::string &) {});
|
||||
(void)sub;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing that subscriber receives type adapted types and ROS message types with intra proccess communications.
|
||||
*/
|
||||
TEST_F(
|
||||
CLASSNAME(test_intra_process_within_one_node, RMW_IMPLEMENTATION),
|
||||
check_type_adapted_messages_are_received_by_intra_process_subscription) {
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
const std::string message_data = "Message Data";
|
||||
const std::string topic_name = "topic_name";
|
||||
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"test_intra_process",
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
auto pub = node->create_publisher<rclcpp::msg::String>(topic_name, 1);
|
||||
|
||||
rclcpp::msg::String msg;
|
||||
msg.data = message_data;
|
||||
|
||||
{
|
||||
{ // const std::string &
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const std::string & msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // const std::string & with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const std::string & msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::shared_ptr<std::string>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const std::string> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // std::shared_ptr<const std::string> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const std::string> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::unique_ptr<std::string>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<std::string> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // std::unique_ptr<std::string> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<std::string> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // const rclcpp::msg::String &
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String & msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // const rclcpp::msg::String & with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String & msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::shared_ptr<rclcpp::msg::String>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const rclcpp::msg::String> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // std::shared_ptr<rclcpp::msg::String> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const rclcpp::msg::String> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::unique_ptr<rclcpp::msg::String>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<rclcpp::msg::String> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // std::unique_ptr<rclcpp::msg::String> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<rclcpp::msg::String> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing that subscriber receives type adapted types and ROS message types with inter proccess communications.
|
||||
*/
|
||||
TEST_F(
|
||||
CLASSNAME(test_intra_process_within_one_node, RMW_IMPLEMENTATION),
|
||||
check_type_adapted_messages_are_received_by_inter_process_subscription) {
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
const std::string message_data = "Message Data";
|
||||
const std::string topic_name = "topic_name";
|
||||
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"test_intra_process",
|
||||
rclcpp::NodeOptions().use_intra_process_comms(false));
|
||||
auto pub = node->create_publisher<rclcpp::msg::String>(topic_name, 1);
|
||||
|
||||
rclcpp::msg::String msg;
|
||||
msg.data = message_data;
|
||||
|
||||
{
|
||||
{ // const std::string &
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](const std::string & msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // const std::string & with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const std::string & msg, const rclcpp::MessageInfo & message_info) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.c_str());
|
||||
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::shared_ptr<std::string>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const std::string> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::shared_ptr<const std::string> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const std::string> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::unique_ptr<std::string>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<std::string> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // std::unique_ptr<std::string> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<std::string> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // const rclcpp::msg::String &
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String & msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // const rclcpp::msg::String & with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String & msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg.data.c_str());
|
||||
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::shared_ptr<rclcpp::msg::String>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const rclcpp::msg::String> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // std::shared_ptr<rclcpp::msg::String> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<const rclcpp::msg::String> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
|
||||
{ // std::unique_ptr<rclcpp::msg::String>
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<rclcpp::msg::String> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
{ // std::unique_ptr<rclcpp::msg::String> with message info
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::unique_ptr<rclcpp::msg::String> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_FALSE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
auto sub = node->create_subscription<StringTypeAdapter>(topic_name, 1, callback);
|
||||
pub->publish(msg);
|
||||
ASSERT_FALSE(is_received);
|
||||
wait_for_message_to_be_received(is_received, node);
|
||||
ASSERT_TRUE(is_received);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,25 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
11.2.0 (2021-07-21)
|
||||
-------------------
|
||||
|
||||
11.1.0 (2021-07-13)
|
||||
-------------------
|
||||
* Fixed occasionally missing goal result caused by race condition (`#1677 <https://github.com/ros2/rclcpp/issues/1677>`_)
|
||||
* Contributors: Kaven Yau
|
||||
|
||||
11.0.0 (2021-05-18)
|
||||
-------------------
|
||||
* Bump the benchmark timeout for benchmark_action_client (`#1671 <https://github.com/ros2/rclcpp/issues/1671>`_)
|
||||
* Contributors: Scott K Logan
|
||||
|
||||
10.0.0 (2021-05-11)
|
||||
-------------------
|
||||
* Returns CancelResponse::REJECT while goal handle failed to transit to CANCELING state (`#1641 <https://github.com/ros2/rclcpp/issues/1641>`_)
|
||||
* Fix action server deadlock issue that caused by other mutexes locked in CancelCallback (`#1635 <https://github.com/ros2/rclcpp/issues/1635>`_)
|
||||
* Contributors: Kaven Yau
|
||||
|
||||
9.0.2 (2021-04-14)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -356,15 +356,26 @@ protected:
|
||||
CancelResponse
|
||||
call_handle_cancel_callback(const GoalUUID & uuid) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
|
||||
std::shared_ptr<ServerGoalHandle<ActionT>> goal_handle;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(goal_handles_mutex_);
|
||||
auto element = goal_handles_.find(uuid);
|
||||
if (element != goal_handles_.end()) {
|
||||
goal_handle = element->second.lock();
|
||||
}
|
||||
}
|
||||
|
||||
CancelResponse resp = CancelResponse::REJECT;
|
||||
auto element = goal_handles_.find(uuid);
|
||||
if (element != goal_handles_.end()) {
|
||||
std::shared_ptr<ServerGoalHandle<ActionT>> goal_handle = element->second.lock();
|
||||
if (goal_handle) {
|
||||
resp = handle_cancel_(goal_handle);
|
||||
if (CancelResponse::ACCEPT == resp) {
|
||||
if (goal_handle) {
|
||||
resp = handle_cancel_(goal_handle);
|
||||
if (CancelResponse::ACCEPT == resp) {
|
||||
try {
|
||||
goal_handle->_cancel_goal();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
RCLCPP_DEBUG(
|
||||
rclcpp::get_logger("rclcpp_action"),
|
||||
"Failed to cancel goal in call_handle_cancel_callback: %s", ex.what());
|
||||
return CancelResponse::REJECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_action</name>
|
||||
<version>9.0.2</version>
|
||||
<version>11.2.0</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -500,9 +500,13 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
|
||||
result_response = create_result_response(action_msgs::msg::GoalStatus::STATUS_UNKNOWN);
|
||||
} else {
|
||||
// Goal exists, check if a result is already available
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
|
||||
auto iter = pimpl_->goal_results_.find(uuid);
|
||||
if (iter != pimpl_->goal_results_.end()) {
|
||||
result_response = iter->second;
|
||||
} else {
|
||||
// Store the request so it can be responded to later
|
||||
pimpl_->result_requests_[uuid].push_back(request_header);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,10 +518,6 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
|
||||
if (RCL_RET_OK != rcl_ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(rcl_ret);
|
||||
}
|
||||
} else {
|
||||
// Store the request so it can be responded to later
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
|
||||
pimpl_->result_requests_[uuid].push_back(request_header);
|
||||
}
|
||||
data.reset();
|
||||
}
|
||||
@@ -627,19 +627,30 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
|
||||
/**
|
||||
* NOTE: There is a potential deadlock issue if both unordered_map_mutex_ and
|
||||
* action_server_reentrant_mutex_ locked in other block scopes. Unless using
|
||||
* std::scoped_lock, locking order must be consistent with the current.
|
||||
*
|
||||
* Current locking order:
|
||||
*
|
||||
* 1. unordered_map_mutex_
|
||||
* 2. action_server_reentrant_mutex_
|
||||
*
|
||||
*/
|
||||
std::lock_guard<std::recursive_mutex> unordered_map_lock(pimpl_->unordered_map_mutex_);
|
||||
pimpl_->goal_results_[uuid] = result_msg;
|
||||
}
|
||||
|
||||
// if there are clients who already asked for the result, send it to them
|
||||
auto iter = pimpl_->result_requests_.find(uuid);
|
||||
if (iter != pimpl_->result_requests_.end()) {
|
||||
for (auto & request_header : iter->second) {
|
||||
// if there are clients who already asked for the result, send it to them
|
||||
auto iter = pimpl_->result_requests_.find(uuid);
|
||||
if (iter != pimpl_->result_requests_.end()) {
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
rcl_ret_t ret = rcl_action_send_result_response(
|
||||
pimpl_->action_server_.get(), &request_header, result_msg.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
for (auto & request_header : iter->second) {
|
||||
rcl_ret_t ret = rcl_action_send_result_response(
|
||||
pimpl_->action_server_.get(), &request_header, result_msg.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ find_package(performance_test_fixture REQUIRED)
|
||||
add_performance_test(
|
||||
benchmark_action_client
|
||||
benchmark_action_client.cpp
|
||||
TIMEOUT 120)
|
||||
TIMEOUT 240)
|
||||
if(TARGET benchmark_action_client)
|
||||
target_link_libraries(benchmark_action_client ${PROJECT_NAME})
|
||||
ament_target_dependencies(benchmark_action_client rclcpp test_msgs)
|
||||
|
||||
@@ -1306,3 +1306,15 @@ TEST_F(TestDeadlockServer, deadlock_while_canceled)
|
||||
send_goal_request(node_, uuid2_); // deadlock here
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST_F(TestDeadlockServer, deadlock_while_succeed_and_canceled)
|
||||
{
|
||||
send_goal_request(node_, uuid1_);
|
||||
std::thread t(&TestDeadlockServer::GoalSucceeded, this);
|
||||
rclcpp::sleep_for(std::chrono::milliseconds(50));
|
||||
auto response_ptr = send_cancel_request(node_, uuid1_);
|
||||
|
||||
// current goal handle is not cancelable, so it returns ERROR_REJECTED
|
||||
EXPECT_EQ(CancelResponse::ERROR_REJECTED, response_ptr->return_code);
|
||||
t.join();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
11.2.0 (2021-07-21)
|
||||
-------------------
|
||||
* Deprecate method names that use CamelCase in rclcpp_components. (`#1716 <https://github.com/ros2/rclcpp/issues/1716>`_)
|
||||
* Contributors: Rebecca Butler
|
||||
|
||||
11.1.0 (2021-07-13)
|
||||
-------------------
|
||||
* Added a hook to generate node options in ComponentManager (`#1702 <https://github.com/ros2/rclcpp/issues/1702>`_)
|
||||
* Contributors: Rebecca Butler
|
||||
|
||||
11.0.0 (2021-05-18)
|
||||
-------------------
|
||||
|
||||
10.0.0 (2021-05-11)
|
||||
-------------------
|
||||
|
||||
9.0.2 (2021-04-14)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
virtual ~ComponentManager();
|
||||
|
||||
/// Return a list of valid loadable components in a given package.
|
||||
/*
|
||||
/**
|
||||
* \param package_name name of the package
|
||||
* \param resource_index name of the executable
|
||||
* \throws ComponentManagerException if the resource was not found or a invalid resource entry
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
const std::string & resource_index = "rclcpp_components") const;
|
||||
|
||||
/// Instantiate a component from a dynamic library.
|
||||
/*
|
||||
/**
|
||||
* \param resource a component resource (class name + library path)
|
||||
* \return a NodeFactory interface
|
||||
*/
|
||||
@@ -131,8 +131,17 @@ public:
|
||||
create_component_factory(const ComponentResource & resource);
|
||||
|
||||
protected:
|
||||
/// Create node options for loaded component
|
||||
/**
|
||||
* \param request information with the node to load
|
||||
* \return node options
|
||||
*/
|
||||
RCLCPP_COMPONENTS_PUBLIC
|
||||
virtual rclcpp::NodeOptions
|
||||
create_node_options(const std::shared_ptr<LoadNode::Request> request);
|
||||
|
||||
/// Service callback to load a new node in the component
|
||||
/*
|
||||
/**
|
||||
* This function allows to add parameters, remap rules, a specific node, name a namespace
|
||||
* and/or additional arguments.
|
||||
*
|
||||
@@ -146,13 +155,27 @@ protected:
|
||||
*/
|
||||
RCLCPP_COMPONENTS_PUBLIC
|
||||
virtual void
|
||||
OnLoadNode(
|
||||
on_load_node(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<LoadNode::Request> request,
|
||||
std::shared_ptr<LoadNode::Response> response);
|
||||
|
||||
/**
|
||||
* \deprecated Use on_load_node() instead
|
||||
*/
|
||||
[[deprecated("Use on_load_node() instead")]]
|
||||
RCLCPP_COMPONENTS_PUBLIC
|
||||
virtual void
|
||||
OnLoadNode(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<LoadNode::Request> request,
|
||||
std::shared_ptr<LoadNode::Response> response)
|
||||
{
|
||||
on_load_node(request_header, request, response);
|
||||
}
|
||||
|
||||
/// Service callback to unload a node in the component
|
||||
/*
|
||||
/**
|
||||
* \param request_header unused
|
||||
* \param request unique identifier to remove from the component
|
||||
* \param response true on the success field if the node unload was succefully, otherwise false
|
||||
@@ -160,13 +183,27 @@ protected:
|
||||
*/
|
||||
RCLCPP_COMPONENTS_PUBLIC
|
||||
virtual void
|
||||
OnUnloadNode(
|
||||
on_unload_node(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<UnloadNode::Request> request,
|
||||
std::shared_ptr<UnloadNode::Response> response);
|
||||
|
||||
/**
|
||||
* \deprecated Use on_unload_node() instead
|
||||
*/
|
||||
[[deprecated("Use on_unload_node() instead")]]
|
||||
RCLCPP_COMPONENTS_PUBLIC
|
||||
virtual void
|
||||
OnUnloadNode(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<UnloadNode::Request> request,
|
||||
std::shared_ptr<UnloadNode::Response> response)
|
||||
{
|
||||
on_unload_node(request_header, request, response);
|
||||
}
|
||||
|
||||
/// Service callback to get the list of nodes in the component
|
||||
/*
|
||||
/**
|
||||
* Return a two list: one with the unique identifiers and other with full name of the nodes.
|
||||
*
|
||||
* \param request_header unused
|
||||
@@ -175,11 +212,25 @@ protected:
|
||||
*/
|
||||
RCLCPP_COMPONENTS_PUBLIC
|
||||
virtual void
|
||||
OnListNodes(
|
||||
on_list_nodes(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<ListNodes::Request> request,
|
||||
std::shared_ptr<ListNodes::Response> response);
|
||||
|
||||
/**
|
||||
* \deprecated Use on_list_nodes() instead
|
||||
*/
|
||||
[[deprecated("Use on_list_nodes() instead")]]
|
||||
RCLCPP_COMPONENTS_PUBLIC
|
||||
virtual void
|
||||
OnListNodes(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<ListNodes::Request> request,
|
||||
std::shared_ptr<ListNodes::Response> response)
|
||||
{
|
||||
on_list_nodes(request_header, request, response);
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<rclcpp::Executor> executor_;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_components</name>
|
||||
<version>9.0.2</version>
|
||||
<version>11.2.0</version>
|
||||
<description>Package containing tools for dynamically loadable components</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -39,13 +39,13 @@ ComponentManager::ComponentManager(
|
||||
{
|
||||
loadNode_srv_ = create_service<LoadNode>(
|
||||
"~/_container/load_node",
|
||||
std::bind(&ComponentManager::OnLoadNode, this, _1, _2, _3));
|
||||
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3));
|
||||
unloadNode_srv_ = create_service<UnloadNode>(
|
||||
"~/_container/unload_node",
|
||||
std::bind(&ComponentManager::OnUnloadNode, this, _1, _2, _3));
|
||||
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3));
|
||||
listNodes_srv_ = create_service<ListNodes>(
|
||||
"~/_container/list_nodes",
|
||||
std::bind(&ComponentManager::OnListNodes, this, _1, _2, _3));
|
||||
std::bind(&ComponentManager::on_list_nodes, this, _1, _2, _3));
|
||||
}
|
||||
|
||||
ComponentManager::~ComponentManager()
|
||||
@@ -121,8 +121,53 @@ ComponentManager::create_component_factory(const ComponentResource & resource)
|
||||
return {};
|
||||
}
|
||||
|
||||
rclcpp::NodeOptions
|
||||
ComponentManager::create_node_options(const std::shared_ptr<LoadNode::Request> request)
|
||||
{
|
||||
std::vector<rclcpp::Parameter> parameters;
|
||||
for (const auto & p : request->parameters) {
|
||||
parameters.push_back(rclcpp::Parameter::from_parameter_msg(p));
|
||||
}
|
||||
|
||||
std::vector<std::string> remap_rules;
|
||||
remap_rules.reserve(request->remap_rules.size() * 2 + 1);
|
||||
remap_rules.push_back("--ros-args");
|
||||
for (const std::string & rule : request->remap_rules) {
|
||||
remap_rules.push_back("-r");
|
||||
remap_rules.push_back(rule);
|
||||
}
|
||||
|
||||
if (!request->node_name.empty()) {
|
||||
remap_rules.push_back("-r");
|
||||
remap_rules.push_back("__node:=" + request->node_name);
|
||||
}
|
||||
|
||||
if (!request->node_namespace.empty()) {
|
||||
remap_rules.push_back("-r");
|
||||
remap_rules.push_back("__ns:=" + request->node_namespace);
|
||||
}
|
||||
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.use_global_arguments(false)
|
||||
.parameter_overrides(parameters)
|
||||
.arguments(remap_rules);
|
||||
|
||||
for (const auto & a : request->extra_arguments) {
|
||||
const rclcpp::Parameter extra_argument = rclcpp::Parameter::from_parameter_msg(a);
|
||||
if (extra_argument.get_name() == "use_intra_process_comms") {
|
||||
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
|
||||
throw ComponentManagerException(
|
||||
"Extra component argument 'use_intra_process_comms' must be a boolean");
|
||||
}
|
||||
options.use_intra_process_comms(extra_argument.get_value<bool>());
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
void
|
||||
ComponentManager::OnLoadNode(
|
||||
ComponentManager::on_load_node(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<LoadNode::Request> request,
|
||||
std::shared_ptr<LoadNode::Response> response)
|
||||
@@ -142,45 +187,7 @@ ComponentManager::OnLoadNode(
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Parameter> parameters;
|
||||
for (const auto & p : request->parameters) {
|
||||
parameters.push_back(rclcpp::Parameter::from_parameter_msg(p));
|
||||
}
|
||||
|
||||
std::vector<std::string> remap_rules;
|
||||
remap_rules.reserve(request->remap_rules.size() * 2 + 1);
|
||||
remap_rules.push_back("--ros-args");
|
||||
for (const std::string & rule : request->remap_rules) {
|
||||
remap_rules.push_back("-r");
|
||||
remap_rules.push_back(rule);
|
||||
}
|
||||
|
||||
if (!request->node_name.empty()) {
|
||||
remap_rules.push_back("-r");
|
||||
remap_rules.push_back("__node:=" + request->node_name);
|
||||
}
|
||||
|
||||
if (!request->node_namespace.empty()) {
|
||||
remap_rules.push_back("-r");
|
||||
remap_rules.push_back("__ns:=" + request->node_namespace);
|
||||
}
|
||||
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.use_global_arguments(false)
|
||||
.parameter_overrides(parameters)
|
||||
.arguments(remap_rules);
|
||||
|
||||
for (const auto & a : request->extra_arguments) {
|
||||
const rclcpp::Parameter extra_argument = rclcpp::Parameter::from_parameter_msg(a);
|
||||
if (extra_argument.get_name() == "use_intra_process_comms") {
|
||||
if (extra_argument.get_type() != rclcpp::ParameterType::PARAMETER_BOOL) {
|
||||
throw ComponentManagerException(
|
||||
"Extra component argument 'use_intra_process_comms' must be a boolean");
|
||||
}
|
||||
options.use_intra_process_comms(extra_argument.get_value<bool>());
|
||||
}
|
||||
}
|
||||
|
||||
auto options = create_node_options(request);
|
||||
auto node_id = unique_id_++;
|
||||
|
||||
if (0 == node_id) {
|
||||
@@ -230,7 +237,7 @@ ComponentManager::OnLoadNode(
|
||||
}
|
||||
|
||||
void
|
||||
ComponentManager::OnUnloadNode(
|
||||
ComponentManager::on_unload_node(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<UnloadNode::Request> request,
|
||||
std::shared_ptr<UnloadNode::Response> response)
|
||||
@@ -255,7 +262,7 @@ ComponentManager::OnUnloadNode(
|
||||
}
|
||||
|
||||
void
|
||||
ComponentManager::OnListNodes(
|
||||
ComponentManager::on_list_nodes(
|
||||
const std::shared_ptr<rmw_request_id_t> request_header,
|
||||
const std::shared_ptr<ListNodes::Request> request,
|
||||
std::shared_ptr<ListNodes::Response> response)
|
||||
|
||||
@@ -3,6 +3,22 @@ Changelog for package rclcpp_lifecycle
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
11.2.0 (2021-07-21)
|
||||
-------------------
|
||||
|
||||
11.1.0 (2021-07-13)
|
||||
-------------------
|
||||
|
||||
11.0.0 (2021-05-18)
|
||||
-------------------
|
||||
* Fix destruction order in lifecycle benchmark (`#1675 <https://github.com/ros2/rclcpp/issues/1675>`_)
|
||||
* Contributors: Scott K Logan
|
||||
|
||||
10.0.0 (2021-05-11)
|
||||
-------------------
|
||||
* [rclcpp] Type Adaptation feature (`#1557 <https://github.com/ros2/rclcpp/issues/1557>`_)
|
||||
* Contributors: Audrow Nash, William Woodall
|
||||
|
||||
9.0.2 (2021-04-14)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -230,13 +230,8 @@ public:
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
const std::string & topic_name,
|
||||
|
||||
@@ -61,7 +61,6 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_lifecycle</name>
|
||||
<version>9.0.2</version>
|
||||
<version>11.2.0</version>
|
||||
<description>Package containing a prototype for lifecycle implementation</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -212,8 +212,9 @@ public:
|
||||
performance_test_fixture::PerformanceTest::TearDown(state);
|
||||
executor->cancel();
|
||||
spinner_.join();
|
||||
lifecycle_node.reset();
|
||||
executor.reset();
|
||||
lifecycle_client.reset();
|
||||
lifecycle_node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user