Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions rosidl_buffer_backend_registry/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
cmake_minimum_required(VERSION 3.5)
project(rosidl_buffer_backend_registry)

# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# Find dependencies
find_package(ament_cmake REQUIRED)
find_package(rosidl_buffer REQUIRED)
find_package(rosidl_buffer_backend REQUIRED)
find_package(rmw REQUIRED)
find_package(rosidl_runtime_cpp REQUIRED)
find_package(pluginlib REQUIRED)

# Library
add_library(${PROJECT_NAME} SHARED
src/buffer_backend_registry.cpp
)

target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>
)

target_compile_definitions(${PROJECT_NAME}
PRIVATE "ROSIDL_BUFFER_BACKEND_REGISTRY_BUILDING_DLL"
)

target_link_libraries(${PROJECT_NAME} PUBLIC
rosidl_buffer_backend::rosidl_buffer_backend
rmw::rmw
rosidl_runtime_cpp::rosidl_runtime_cpp
pluginlib::pluginlib
)

# Install headers
install(
DIRECTORY include/
DESTINATION include/${PROJECT_NAME}
)

# Install library
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include/${PROJECT_NAME}
)

# Export dependencies
ament_export_targets(${PROJECT_NAME} HAS_LIBRARY_TARGET)
ament_export_dependencies(rosidl_buffer_backend rmw rosidl_runtime_cpp pluginlib)

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()

find_package(ament_cmake_gtest REQUIRED)

# Test backend registry and dummy backend (direct instantiation, not via pluginlib)
ament_add_gtest(test_buffer_backend_registry
test/test_buffer_backend_registry.cpp
)
if(TARGET test_buffer_backend_registry)
target_include_directories(test_buffer_backend_registry PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/test
)
target_link_libraries(test_buffer_backend_registry
${PROJECT_NAME}
rosidl_buffer::rosidl_buffer
rosidl_buffer_backend::rosidl_buffer_backend
rmw::rmw
rosidl_runtime_cpp::rosidl_runtime_cpp
pluginlib::pluginlib
)
endif()
endif()

ament_package()

Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2026 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 ROSIDL_BUFFER_BACKEND_REGISTRY__BACKEND_UTILS_HPP_
#define ROSIDL_BUFFER_BACKEND_REGISTRY__BACKEND_UTILS_HPP_

#include <algorithm>
#include <cstdint>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include "rosidl_buffer_backend/buffer_backend.hpp"
#include "rmw/topic_endpoint_info.h"

namespace rosidl_buffer_backend_registry
{

/// Collect metadata strings from every loaded backend instance.
/// @param[in] backend_instances Map of backend name to backend instance.
/// @return Map of backend name to its metadata string (null backends are skipped).
inline std::unordered_map<std::string, std::string> get_all_backend_metadata(
const std::unordered_map<std::string, std::shared_ptr<rosidl::BufferBackend>> & backend_instances)
{
std::unordered_map<std::string, std::string> backend_metadata;
for (const auto & [backend_name, backend] : backend_instances) {
if (backend) {
backend_metadata[backend_name] = backend->get_backend_metadata();
}
}
return backend_metadata;
}

/// Notify every loaded backend that a local endpoint has been created.
/// @param[in] backend_instances Map of backend name to backend instance.
/// @param[in] endpoint_info Endpoint that was just created.
inline void notify_endpoint_created(
const std::unordered_map<std::string, std::shared_ptr<rosidl::BufferBackend>> & backend_instances,
const rmw_topic_endpoint_info_t & endpoint_info)
{
for (const auto & [_, backend] : backend_instances) {
if (backend) {
backend->on_creating_endpoint(endpoint_info);
}
}
}

/// Notify every loaded backend that a remote endpoint has been discovered and
/// collect compatibility / grouping results.
/// @param[in] backend_instances Map of backend name to backend instance.
/// @param[in] endpoint_info Information about the discovered endpoint.
/// @param[in] existing_endpoints Endpoints already known on this topic.
/// @param[out] backend_endpoint_groups Updated per-backend endpoint GID-hash
/// groupings (cleared to empty for null backends).
/// @param[in] endpoint_supported_backends Backend-type-to-metadata map
/// advertised by the discovered endpoint.
/// @return Map of backend name to compatibility flag (false for null backends).
inline std::unordered_map<std::string, bool> notify_endpoint_discovered(
const std::unordered_map<std::string, std::shared_ptr<rosidl::BufferBackend>> & backend_instances,
const rmw_topic_endpoint_info_t & endpoint_info,
const std::vector<rmw_topic_endpoint_info_t> & existing_endpoints,
std::unordered_map<std::string, std::vector<std::set<uint32_t>>> & backend_endpoint_groups,
const std::unordered_map<std::string, std::string> & endpoint_supported_backends)
{
std::unordered_map<std::string, bool> backend_compatibility;
for (const auto & [backend_name, backend] : backend_instances) {
if (!backend) {
backend_compatibility[backend_name] = false;
backend_endpoint_groups[backend_name] = {};
continue;
}
auto result = backend->on_discovering_endpoint(
endpoint_info, existing_endpoints, endpoint_supported_backends);
backend_compatibility[backend_name] = result.first;
backend_endpoint_groups[backend_name] = std::move(result.second);
}
return backend_compatibility;
}

/// Check whether two backend name lists share at least one common entry.
/// @return true if any backend name appears in both \p a and \p b.
inline bool backends_compatible(
const std::vector<std::string> & a,
const std::vector<std::string> & b)
{
for (const auto & entry : a) {
if (std::find(b.begin(), b.end(), entry) != b.end()) {
return true;
}
}
return false;
}

/// Return the de-duplicated intersection of two backend name lists,
/// preserving the order in which names appear in \p a.
inline std::vector<std::string> get_common_backends(
const std::vector<std::string> & a,
const std::vector<std::string> & b)
{
std::vector<std::string> common;
for (const auto & entry : a) {
if (std::find(b.begin(), b.end(), entry) != b.end() &&
std::find(common.begin(), common.end(), entry) == common.end())
{
common.push_back(entry);
}
}
return common;
}

} // namespace rosidl_buffer_backend_registry

#endif // ROSIDL_BUFFER_BACKEND_REGISTRY__BACKEND_UTILS_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2026 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 ROSIDL_BUFFER_BACKEND_REGISTRY__BUFFER_BACKEND_REGISTRY_HPP_
#define ROSIDL_BUFFER_BACKEND_REGISTRY__BUFFER_BACKEND_REGISTRY_HPP_

#include <memory>
#include <string>
#include <vector>

#include "rosidl_buffer_backend/buffer_backend.hpp"
#include "rosidl_buffer_backend_registry/visibility_control.h"

// Forward declare pluginlib ClassLoader to avoid header dependency
namespace pluginlib
{
template<class T>
class ClassLoader;
} // namespace pluginlib

namespace rosidl_buffer_backend_registry
{

/// Registry for discovering and managing buffer backend plugins.
/// Uses pluginlib for dynamic plugin discovery and loading.
class BufferBackendRegistry
{
public:
ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC
BufferBackendRegistry();

ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC
~BufferBackendRegistry();

/// Create a backend instance by plugin class name.
/// Backends loaded through pluginlib are instantiated per call.
ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC
std::shared_ptr<rosidl::BufferBackend> create_backend_instance(const std::string & name);

/// Get names of all registered backends.
ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC
std::vector<std::string> get_backend_names() const;

// Non-copyable, non-movable
BufferBackendRegistry(const BufferBackendRegistry &) = delete;
BufferBackendRegistry & operator=(const BufferBackendRegistry &) = delete;
BufferBackendRegistry(BufferBackendRegistry &&) = delete;
BufferBackendRegistry & operator=(BufferBackendRegistry &&) = delete;

private:
/// Query pluginlib for declared backend classes and populate plugin_backend_classes_.
void load_plugins();

std::vector<std::string> plugin_backend_classes_;
std::unique_ptr<pluginlib::ClassLoader<rosidl::BufferBackend>> loader_;
};

} // namespace rosidl_buffer_backend_registry

#endif // ROSIDL_BUFFER_BACKEND_REGISTRY__BUFFER_BACKEND_REGISTRY_HPP_
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2026 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 ROSIDL_BUFFER_BACKEND_REGISTRY__VISIBILITY_CONTROL_H_
#define ROSIDL_BUFFER_BACKEND_REGISTRY__VISIBILITY_CONTROL_H_

#ifdef __cplusplus
extern "C"
{
#endif

// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
// https://gcc.gnu.org/wiki/Visibility

#if defined _WIN32 || defined __CYGWIN__
#ifdef __GNUC__
#define ROSIDL_BUFFER_BACKEND_REGISTRY_EXPORT __attribute__((dllexport))
#define ROSIDL_BUFFER_BACKEND_REGISTRY_IMPORT __attribute__((dllimport))
#else
#define ROSIDL_BUFFER_BACKEND_REGISTRY_EXPORT __declspec(dllexport)
#define ROSIDL_BUFFER_BACKEND_REGISTRY_IMPORT __declspec(dllimport)
#endif
#ifdef ROSIDL_BUFFER_BACKEND_REGISTRY_BUILDING_DLL
#define ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC ROSIDL_BUFFER_BACKEND_REGISTRY_EXPORT
#else
#define ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC ROSIDL_BUFFER_BACKEND_REGISTRY_IMPORT
#endif
#define ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC_TYPE ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC
#define ROSIDL_BUFFER_BACKEND_REGISTRY_LOCAL
#else
#define ROSIDL_BUFFER_BACKEND_REGISTRY_EXPORT __attribute__((visibility("default")))
#define ROSIDL_BUFFER_BACKEND_REGISTRY_IMPORT
#if __GNUC__ >= 4
#define ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC __attribute__((visibility("default")))
#define ROSIDL_BUFFER_BACKEND_REGISTRY_LOCAL __attribute__((visibility("hidden")))
#else
#define ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC
#define ROSIDL_BUFFER_BACKEND_REGISTRY_LOCAL
#endif
#define ROSIDL_BUFFER_BACKEND_REGISTRY_PUBLIC_TYPE
#endif

#ifdef __cplusplus
}
#endif

#endif // ROSIDL_BUFFER_BACKEND_REGISTRY__VISIBILITY_CONTROL_H_
29 changes: 29 additions & 0 deletions rosidl_buffer_backend_registry/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>rosidl_buffer_backend_registry</name>
<version>5.1.2</version>
<description>Backend discovery and plugin loading for ROS2 buffer types</description>

<maintainer email="cyc@nvidia.com">CY Chen</maintainer>

<license>Apache License 2.0</license>

<author email="cyc@nvidia.com">CY Chen</author>

<buildtool_depend>ament_cmake</buildtool_depend>

<depend>rosidl_buffer_backend</depend>
<depend>rmw</depend>
<depend>rosidl_runtime_cpp</depend>
<depend>pluginlib</depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>rosidl_buffer</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>

Loading