Skip to content

Commit

Permalink
Add C++ conversion APIs to proto2ros (#118)
Browse files Browse the repository at this point in the history
Signed-off-by: Michel Hidalgo <[email protected]>
  • Loading branch information
mhidalgo-bdai authored Sep 17, 2024
1 parent d452350 commit e93bfc9
Show file tree
Hide file tree
Showing 24 changed files with 1,935 additions and 21 deletions.
15 changes: 15 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- yaml -*-

# This file determines clang-format's style settings; for details, refer to
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html

# See https://google.github.io/styleguide/cppguide.html for more info
BasedOnStyle: Google

Language: Cpp

SortIncludes: false

# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#columnlimit
# Maximum line with is 120 characters (default: 80)
ColumnLimit : 120
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ repos:
- id: check-merge-conflict
- id: check-executables-have-shebangs
- id: check-shebang-scripts-are-executable
- repo: https://github.com/ssciwr/clang-format-hook.git
rev: v14.0.0
hooks:
- id: clang-format
types_or: [c++, c]
- repo: https://github.com/cpplint/cpplint.git
rev: 1.6.1
hooks:
- id: cpplint
args: ['--quiet', '--filter=-whitespace/comments', '--linelength=120']
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.2.0
hooks:
Expand Down
69 changes: 68 additions & 1 deletion proto2ros/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Copyright (c) 2023 Boston Dynamics AI Institute LLC. All rights reserved.
cmake_minimum_required(VERSION 3.8)
cmake_minimum_required(VERSION 3.9)
if(POLICY CMP0148)
cmake_policy(SET CMP0148 OLD) # to accommodate rosidl pipeline
endif()
Expand All @@ -9,23 +9,90 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()

set(DEFAULT_BUILD_TYPE "Release")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of the build." FORCE)
# Set the possible values of the build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)

if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_supported OUTPUT output)
if(ipo_supported)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
message(STATUS "We are in release - Successfully enabled IPO")
else()
message(WARNING "IPO not supported - Skipping reason: ${output}")
endif()
endif()

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rclcpp REQUIRED)

find_package(Protobuf REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
msg/Any.msg msg/AnyProto.msg msg/Bytes.msg msg/List.msg
msg/Struct.msg msg/StructEntry.msg msg/Value.msg
)

add_library(${PROJECT_NAME}_conversions SHARED src/conversions.cpp)
set_target_properties(${PROJECT_NAME}_conversions PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON)
target_compile_options(${PROJECT_NAME}_conversions PUBLIC "$<$<CONFIG:DEBUG>:-O0>")
target_compile_definitions(${PROJECT_NAME}_conversions
PUBLIC
"$<$<CONFIG:DEBUG>:DEBUG>"
"$<$<OR:$<CONFIG:RELWITHDEBINFO>,$<CONFIG:RELEASE>>:NDEBUG>"
)
target_include_directories(${PROJECT_NAME}_conversions PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>"
)
rosidl_get_typesupport_target(${PROJECT_NAME}_cpp_msgs ${PROJECT_NAME} "rosidl_typesupport_cpp")
target_link_libraries(${PROJECT_NAME}_conversions ${${PROJECT_NAME}_cpp_msgs} protobuf::libprotobuf)
ament_target_dependencies(${PROJECT_NAME}_conversions builtin_interfaces rclcpp std_msgs)

find_program(CLANG_TIDY_EXECUTABLE NAMES "clang-tidy" REQUIRED)
set(CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE}"
"-header-filter=.*proto2ros/.*conversions.hpp"
"-checks=-clang-diagnostic-ignored-optimization-argument")
set_target_properties(${PROJECT_NAME}_conversions PROPERTIES CXX_CLANG_TIDY "${CXX_CLANG_TIDY}")

include(cmake/rosidl_helpers.cmake)
rosidl_generated_python_package_add(
${PROJECT_NAME}_additional_modules
PACKAGES ${PROJECT_NAME}
DESTINATION ${PROJECT_NAME}
)

install(
DIRECTORY include/
DESTINATION include/${PROJECT_NAME}
)

install(
TARGETS ${PROJECT_NAME}_conversions
EXPORT ${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
ament_export_targets(${PROJECT_NAME})

install(
DIRECTORY cmake
DESTINATION share/${PROJECT_NAME}
Expand Down
14 changes: 12 additions & 2 deletions proto2ros/cmake/proto2ros_generate.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
function(proto2ros_generate target)
cmake_parse_arguments(
ARG "NO_LINT"
"PACKAGE_NAME;CONFIG_FILE;INTERFACES_OUT_VAR;PYTHON_OUT_VAR"
"PACKAGE_NAME;CONFIG_FILE;INTERFACES_OUT_VAR;PYTHON_OUT_VAR;CPP_OUT_VAR;INCLUDE_OUT_VAR"
"PROTOS;PROTO_DESCRIPTORS;IMPORT_DIRS;CONFIG_OVERLAYS;APPEND_PYTHONPATH" ${ARGN})
if(NOT ARG_PACKAGE_NAME)
set(ARG_PACKAGE_NAME ${PROJECT_NAME})
Expand All @@ -33,7 +33,13 @@ function(proto2ros_generate target)
set(ARG_INTERFACES_OUT_VAR ${target}_interfaces)
endif()
if(NOT ARG_PYTHON_OUT_VAR)
set(ARG_INTERFACES_OUT_VAR ${target}_python_sources)
set(ARG_PYTHON_OUT_VAR ${target}_python_sources)
endif()
if(NOT ARG_CPP_OUT_VAR)
set(ARG_CPP_OUT_VAR ${target}_cpp_sources)
endif()
if(NOT ARG_INCLUDE_OUT_VAR)
set(ARG_INCLUDE_OUT_VAR ${target}_cpp_include)
endif()
list(APPEND ARG_APPEND_PYTHONPATH "${PROJECT_SOURCE_DIR}")
string(REPLACE ";" ":" APPEND_PYTHONPATH "${ARG_APPEND_PYTHONPATH}")
Expand Down Expand Up @@ -142,6 +148,8 @@ function(proto2ros_generate target)
string(REPLACE "${OUTPUT_PATH}/" "${OUTPUT_PATH}:" interface_tuples "${interface_files}")
set(python_sources ${output_files})
list(FILTER python_sources INCLUDE REGEX ".*\.py$")
set(cpp_sources ${output_files})
list(FILTER cpp_sources INCLUDE REGEX ".*\.cpp$")

if(BUILD_TESTING AND NOT ARG_NO_LINT AND ament_cmake_mypy_FOUND)
set(MYPY_PATH "${APPEND_PYTHONPATH}:$ENV{PYTHONPATH}")
Expand All @@ -156,4 +164,6 @@ function(proto2ros_generate target)
endif()
set(${ARG_INTERFACES_OUT_VAR} ${interface_tuples} PARENT_SCOPE)
set(${ARG_PYTHON_OUT_VAR} ${python_sources} PARENT_SCOPE)
set(${ARG_CPP_OUT_VAR} ${cpp_sources} PARENT_SCOPE)
set(${ARG_INCLUDE_OUT_VAR} ${BASE_PATH} PARENT_SCOPE)
endfunction()
25 changes: 25 additions & 0 deletions proto2ros/cmake/proto2ros_vendor_package.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ macro(proto2ros_vendor_package target)
CONFIG_OVERLAYS ${ARG_CONFIG_OVERLAYS}
INTERFACES_OUT_VAR ros_messages
PYTHON_OUT_VAR py_sources
CPP_OUT_VAR cpp_sources
${proto2ros_generate_OPTIONS}
)

Expand All @@ -55,4 +56,28 @@ macro(proto2ros_vendor_package target)
PACKAGES ${ARG_PYTHON_PACKAGES}
DESTINATION ${target}
)

find_package(rclcpp REQUIRED)
add_library(${target}_conversions ${cpp_sources})
rosidl_get_typesupport_target(cpp_interfaces ${target} "rosidl_typesupport_cpp")
target_link_libraries(${target}_conversions ${cpp_interfaces})
ament_target_dependencies(${target}_conversions
${ARG_ROS_DEPENDENCIES} builtin_interfaces proto2ros rclcpp)

set(header_files ${cpp_sources})
list(FILTER header_files INCLUDE REGEX ".*\.hpp$")
install(
FILES ${header_files}
DESTINATION include/${PROJECT_NAME}
)
ament_export_include_directories("include/${PROJECT_NAME}")

install(
TARGETS ${target}_conversions
EXPORT export_${target}_conversions
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
ament_export_targets(export_${target}_conversions)
endmacro()
153 changes: 153 additions & 0 deletions proto2ros/include/proto2ros/conversions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright (c) 2024 Boston Dynamics AI Institute LLC. All rights reserved.

/// This module provides basic conversion APIs, applicable to any proto2ros generated packages.

#pragma once

#include <google/protobuf/any.pb.h>
#include <google/protobuf/duration.pb.h>
#include <google/protobuf/struct.pb.h>
#include <google/protobuf/timestamp.pb.h>
#include <google/protobuf/wrappers.pb.h>

#include <stdexcept>

#include <builtin_interfaces/msg/duration.hpp>
#include <builtin_interfaces/msg/time.hpp>
#include <proto2ros/msg/any.hpp>
#include <proto2ros/msg/any_proto.hpp>
#include <proto2ros/msg/bytes.hpp>
#include <proto2ros/msg/list.hpp>
#include <proto2ros/msg/struct.hpp>
#include <proto2ros/msg/value.hpp>
#include <std_msgs/msg/bool.hpp>
#include <std_msgs/msg/float32.hpp>
#include <std_msgs/msg/float64.hpp>
#include <std_msgs/msg/int32.hpp>
#include <std_msgs/msg/int64.hpp>
#include <std_msgs/msg/string.hpp>
#include <std_msgs/msg/u_int32.hpp>
#include <std_msgs/msg/u_int64.hpp>

namespace proto2ros {
namespace conversions {

/// Unpacks a proto2ros/AnyProto ROS message into any Protobuf message.
///
/// \throws std::runtime_error if the given ROS message cannot be unpacked onto the given Protobuf message.
template <typename T>
void convert(const proto2ros::msg::AnyProto& ros_msg, T* proto_msg) {
proto_msg->Clear();
auto wrapper = google::protobuf::Any();
wrapper.set_type_url(ros_msg.type_url);
auto* value = wrapper.mutable_value();
value->reserve(ros_msg.value.size());
value->assign(ros_msg.value.begin(), ros_msg.value.end());
if (!wrapper.UnpackTo(proto_msg)) {
throw std::runtime_error("failed to unpack AnyProto message");
}
}

/// Packs any Protobuf message into a proto2ros/AnyProto ROS message.
template <typename T>
void convert(const T& proto_msg, proto2ros::msg::AnyProto* ros_msg) {
auto wrapper = google::protobuf::Any();
wrapper.PackFrom(proto_msg);
ros_msg->type_url = wrapper.type_url();
const auto& value = wrapper.value();
ros_msg->value.reserve(value.size());
ros_msg->value.assign(value.begin(), value.end());
}

/// Converts from proto2ros/AnyProto ROS message to google.protobuf.Any Protobuf messages.
void convert(const proto2ros::msg::AnyProto& ros_msg, google::protobuf::Any* proto_msg);

/// Converts from google.protobuf.Any Protobuf messages to proto2ros/AnyProto ROS messages.
void convert(const google::protobuf::Any& proto_msg, proto2ros::msg::AnyProto* ros_msg);

/// Converts from google.protobuf.Any Protobuf messages to proto2ros/AnyProto ROS messages.
void convert(const builtin_interfaces::msg::Duration& ros_msg, google::protobuf::Duration* proto_msg);

/// Converts from google.protobuf.Duration Protobuf messages to builtin_interfaces/Duration ROS messages.
void convert(const google::protobuf::Duration& proto_msg, builtin_interfaces::msg::Duration* ros_msg);

/// Converts from builtin_interfaces/Time ROS messages to google.protobuf.Timestamp Protobuf messages.
void convert(const builtin_interfaces::msg::Time& ros_msg, google::protobuf::Timestamp* proto_msg);

/// Converts from google.protobuf.Timestamp Protobuf messages to builtin_interfaces/Time ROS messages.
void convert(const google::protobuf::Timestamp& proto_msg, builtin_interfaces::msg::Time* ros_msg);

/// Converts from std_msgs/Float64 ROS messages to google.protobuf.DoubleValue Protobuf messages.
void convert(const std_msgs::msg::Float64& ros_msg, google::protobuf::DoubleValue* proto_msg);

/// Converts from google.protobuf.DoubleValue Protobuf messages to std_msgs/Float64 ROS messages.
void convert(const google::protobuf::DoubleValue& proto_msg, std_msgs::msg::Float64* ros_msg);

/// Converts from std_msgs/Float32 ROS messages to google.protobuf.FloatValue Protobuf messages.
void convert(const std_msgs::msg::Float32& ros_msg, google::protobuf::FloatValue* proto_msg);

/// Converts from google.protobuf.FloatValue Protobuf messages to std_msgs/Float32 ROS messages.
void convert(const google::protobuf::FloatValue& proto_msg, std_msgs::msg::Float32* ros_msg);

/// Converts from std_msgs/Int64 ROS messages to google.protobuf.Int64Value Protobuf messages.
void convert(const std_msgs::msg::Int64& ros_msg, google::protobuf::Int64Value* proto_msg);

/// Converts from google.protobuf.Int64Value Protobuf messages to std_msgs/Int64 ROS messages.
void convert(const google::protobuf::Int64Value& proto_msg, std_msgs::msg::Int64* ros_msg);

/// Converts from std_msgs/Int32 ROS messages to google.protobuf.Int32Value Protobuf messages.
void convert(const std_msgs::msg::Int32& ros_msg, google::protobuf::Int32Value* proto_msg);

/// Converts from google.protobuf.Int32Value Protobuf messages to std_msgs/Int32 ROS messages.
void convert(const google::protobuf::Int32Value& proto_msg, std_msgs::msg::Int32* ros_msg);

/// Converts from std_msgs/UInt64 ROS messages to google.protobuf.UInt64Value Protobuf messages.
void convert(const std_msgs::msg::UInt64& ros_msg, google::protobuf::UInt64Value* proto_msg);

/// Converts from google.protobuf.UInt64Value Protobuf messages to std_msgs/UInt64 ROS messages.
void convert(const google::protobuf::UInt64Value& proto_msg, std_msgs::msg::UInt64* ros_msg);

/// Converts from std_msgs/UInt32 ROS messages to google.protobuf.UInt32Value Protobuf messages.
void convert(const std_msgs::msg::UInt32& ros_msg, google::protobuf::UInt32Value* proto_msg);

/// Converts from google.protobuf.UInt32Value Protobuf messages to std_msgs/UInt32 ROS messages.
void convert(const google::protobuf::UInt32Value& proto_msg, std_msgs::msg::UInt32* ros_msg);

/// Converts from std_msgs/Bool ROS messages to google.protobuf.BoolValue Protobuf messages.
void convert(const std_msgs::msg::Bool& ros_msg, google::protobuf::BoolValue* proto_msg);

/// Converts from google.protobuf.BoolValue Protobuf messages to std_msgs/Bool ROS messages.
void convert(const google::protobuf::BoolValue& proto_msg, std_msgs::msg::Bool* ros_msg);

/// Converts from std_msgs/String ROS messages to google.protobuf.StringValue Protobuf messages.
void convert(const std_msgs::msg::String& ros_msg, google::protobuf::StringValue* proto_msg);

/// Converts from google.protobuf.StringValue Protobuf messages to std_msgs/String ROS messages.
void convert(const google::protobuf::StringValue& proto_msg, std_msgs::msg::String* ros_msg);

/// Converts from proto2ros/Bytes ROS messages to google.protobuf.BytesValue Protobuf messages.
void convert(const proto2ros::msg::Bytes& ros_msg, google::protobuf::BytesValue* proto_msg);

/// Converts from google.protobuf.BytesValue Protobuf messages to proto2ros/Bytes ROS messages.
void convert(const google::protobuf::BytesValue& proto_msg, proto2ros::msg::Bytes* ros_msg);

/// Converts from proto2ros/Value ROS messages to google.protobuf.Value Protobuf messages.
void convert(const proto2ros::msg::Value& ros_msg, google::protobuf::Value* proto_msg);

/// Converts from google.protobuf.Value Protobuf messages to proto2ros/Value ROS messages.
void convert(const google::protobuf::Value& proto_msg, proto2ros::msg::Value* ros_msg);

/// Converts from proto2ros/List ROS messages to google.protobuf.ListValue Protobuf messages.
void convert(const proto2ros::msg::List& ros_msg, google::protobuf::ListValue* proto_msg);

/// Converts from google.protobuf.ListValue Protobuf messages to proto2ros/List ROS messages.
void convert(const google::protobuf::ListValue& proto_msg, proto2ros::msg::List* ros_msg);

/// Converts from proto2ros/Struct ROS messages to google.protobuf.Struct Protobuf messages.
void convert(const proto2ros::msg::Struct& ros_msg, google::protobuf::Struct* proto_msg);

/// Converts from google.protobuf.Struct Protobuf messages to proto2ros/Struct ROS messages.
void convert(const google::protobuf::Struct& proto_msg, proto2ros::msg::Struct* ros_msg);

} // namespace conversions
} // namespace proto2ros
11 changes: 6 additions & 5 deletions proto2ros/msg/Value.msg
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
# See https://protobuf.dev/reference/protobuf/google.protobuf/#value for further reference.

int8 NO_VALUE_SET=0
int8 NUMBER_VALUE_SET=1
int8 STRING_VALUE_SET=2
int8 BOOL_VALUE_SET=3
int8 STRUCT_VALUE_SET=4
int8 LIST_VALUE_SET=5
int8 NULL_VALUE_SET=1
int8 NUMBER_VALUE_SET=2
int8 STRING_VALUE_SET=3
int8 BOOL_VALUE_SET=4
int8 STRUCT_VALUE_SET=5
int8 LIST_VALUE_SET=6

int8 kind 0

Expand Down
Loading

0 comments on commit e93bfc9

Please sign in to comment.