Skip to content

Commit b709bf1

Browse files
committed
Added a method to get the actual underlying message from a compound message.
Usage: using geometry_msgs::msg::Point; Point point = compound["position"].as<CompoundMessage>().message<Point>(); (cherry picked from commit b89cce1)
1 parent 13cbfbf commit b709bf1

File tree

6 files changed

+83
-14
lines changed

6 files changed

+83
-14
lines changed

ros_babel_fish/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ endif ()
1717

1818
find_package(ament_cmake REQUIRED)
1919
find_package(ament_index_cpp REQUIRED)
20+
find_package(geometry_msgs REQUIRED)
2021
find_package(rclcpp REQUIRED)
2122
find_package(rclcpp_action REQUIRED)
2223
find_package(rcpputils REQUIRED)
@@ -69,6 +70,7 @@ target_include_directories(any_subscriber PRIVATE
6970
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
7071
$<INSTALL_INTERFACE:include>
7172
)
73+
ament_target_dependencies(any_subscriber geometry_msgs)
7274

7375
add_executable(troll_node examples/troll_node.cpp)
7476
target_link_libraries(troll_node ${PROJECT_NAME})

ros_babel_fish/examples/any_subscriber.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
#include <chrono>
5+
#include <geometry_msgs/msg/point.hpp>
6+
#include <geometry_msgs/msg/point32.hpp>
57
#include <rclcpp/rclcpp.hpp>
68
#include <ros_babel_fish/babel_fish.hpp>
79
#include <ros_babel_fish/macros.hpp>
8-
#include <thread>
9-
#include <utility>
1010

1111
/*!
1212
* The following example demonstrates how to subscribe to a topic of any type,
@@ -28,9 +28,12 @@ class AnySubscriber : public rclcpp::Node
2828
void init()
2929
{
3030
fish = ros_babel_fish::BabelFish::make_shared();
31+
RCLCPP_INFO( this->get_logger(), "Started. Waiting for topic '%s' to become available.",
32+
topic_.c_str() );
3133
subscription_ = fish->create_subscription(
3234
*this, topic_, 10,
3335
[this]( ros_babel_fish::CompoundMessage::SharedPtr msg ) { topic_callback( msg ); } );
36+
RCLCPP_INFO( this->get_logger(), "Subscribed to topic '%s'", topic_.c_str() );
3437
}
3538

3639
private:
@@ -43,7 +46,7 @@ class AnySubscriber : public rclcpp::Node
4346
try {
4447
std::cout << "Type: " << msg->name() << std::endl;
4548
dumpMessageContent( *msg );
46-
std::cout << std::endl << "---" << std::endl;
49+
std::cout << "---" << std::endl;
4750
} catch ( ros_babel_fish::BabelFishException &ex ) {
4851
RCLCPP_ERROR( this->get_logger(), "Got a BabelFishException during translation: %s", ex.what() );
4952
}
@@ -70,6 +73,24 @@ int main( int argc, char *argv[] )
7073
}
7174

7275
// Here's where the dumping happens
76+
void printPoint( const ros_babel_fish::CompoundMessage &point )
77+
{
78+
// This method demonstrates how to get a known message type out of a CompoundMessage
79+
// Note that this is only possible if the CompoundMessage actually contains a message of the given type
80+
// Otherwise, a BabelFishException will be thrown
81+
// Also, this message is a reference to the original message, so any modifications to either will affect the other
82+
auto point_msg = point.message<geometry_msgs::msg::Point>();
83+
std::cout << "Point { x: " << point_msg->x << ", y: " << point_msg->y << ", z: " << point_msg->z
84+
<< " }" << std::endl;
85+
}
86+
87+
void printPoint32( const ros_babel_fish::CompoundMessage &point )
88+
{
89+
// Again for Point32 so it can be tested with geometry_msgs/msg/Polygon
90+
auto point_msg = point.message<geometry_msgs::msg::Point32>();
91+
std::cout << "Point { x: " << point_msg->x << ", y: " << point_msg->y << ", z: " << point_msg->z
92+
<< " }" << std::endl;
93+
}
7394

7495
template<typename T>
7596
void printToStdOut( const T &val )
@@ -134,20 +155,27 @@ void dumpMessageContent( const ros_babel_fish::Message &message, const std::stri
134155
// We can either just dump the message or demonstrate the time conversion
135156
if ( compound.isTime() ) {
136157
rclcpp::Time time = compound.value<rclcpp::Time>();
137-
std::cout << "Time { seconds: " << time.seconds() << " }";
158+
std::cout << "Time { seconds: " << time.seconds() << " }" << std::endl;
138159
return;
139160
}
140161
if ( compound.isDuration() ) {
141162
auto duration = compound.value<rclcpp::Duration>();
142-
std::cout << "Duration { seconds: " << duration.seconds() << " }";
163+
std::cout << "Duration { seconds: " << duration.seconds() << " }" << std::endl;
164+
return;
165+
}
166+
if ( compound.name() == "geometry_msgs/msg/Point" ) {
167+
printPoint( compound );
168+
return;
169+
}
170+
if ( compound.name() == "geometry_msgs/msg/Point32" ) {
171+
printPoint32( compound );
143172
return;
144173
}
145174
std::cout << std::endl;
146175
for ( size_t i = 0; i < compound.keys().size(); ++i ) {
147176
std::cout << prefix << compound.keys()[i] << ": ";
148177
dumpMessageContent( *compound.values()[i], prefix + " " );
149-
if ( i != compound.keys().size() - 1 )
150-
std::cout << std::endl;
178+
std::cout << std::endl;
151179
}
152180
} else if ( message.type() == MessageTypes::Array ) {
153181
auto &base = message.as<ArrayMessageBase>();

ros_babel_fish/include/ros_babel_fish/messages/compound_message.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,41 @@ class CompoundMessage final : public Message
8282

8383
std::shared_ptr<void> type_erased_message();
8484

85+
/*!
86+
* Cast this compound message to a message type.
87+
* Example:
88+
* \code{.cpp}
89+
* using geometry_msgs::msg::Point;
90+
* Point point = compound.message&lt;Point&gt;();
91+
* \endcode
92+
*
93+
* @tparam T The type of the message to cast to.
94+
* @throws BabelFishException If the message is not of the target type.
95+
* @return The message casted to the target type.
96+
*/
97+
template<typename T>
98+
std::shared_ptr<T> message()
99+
{
100+
if ( rosidl_generator_traits::name<T>() != name() ) {
101+
throw BabelFishException( "Tried to cast compound message of type " + name() +
102+
" to incompatible message type " +
103+
rosidl_generator_traits::name<T>() + "!" );
104+
}
105+
return std::static_pointer_cast<T>( type_erased_message() );
106+
}
107+
108+
//! @copydoc message()
109+
template<typename T>
110+
std::shared_ptr<const T> message() const
111+
{
112+
if ( rosidl_generator_traits::name<T>() != name() ) {
113+
throw BabelFishException( "Tried to cast compound message of type " + name() +
114+
" to incompatible message type " +
115+
rosidl_generator_traits::name<T>() + "!" );
116+
}
117+
return std::static_pointer_cast<const T>( type_erased_message() );
118+
}
119+
85120
//! Creates a copy of this compound message
86121
CompoundMessage clone() const;
87122

ros_babel_fish/include/ros_babel_fish/messages/message.hpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ class Message
4848
template<typename T>
4949
T value() const
5050
{
51-
auto result = std::dynamic_pointer_cast<T>( data_ );
52-
if ( !result )
53-
throw BabelFishException( "Invalid cast!" );
54-
return *result;
51+
// Fallback to the default implementation which throws an exception if no specialization is available
52+
static_assert( false && "Invalid type for value!" );
5553
}
5654

5755
/*!
@@ -69,7 +67,7 @@ class Message
6967
template<typename T>
7068
T &as()
7169
{
72-
T *result = dynamic_cast<T *>( this );
70+
auto result = dynamic_cast<T *>( this );
7371
if ( result == nullptr )
7472
throw BabelFishException( "Tried to cast message to incompatible type!" );
7573
return *result;
@@ -80,8 +78,9 @@ class Message
8078
const T &as() const
8179
{
8280
auto result = dynamic_cast<const T *>( this );
83-
if ( result == nullptr )
81+
if ( result == nullptr ) {
8482
throw BabelFishException( "Tried to cast message to incompatible type!" );
83+
}
8584
return *result;
8685
}
8786

ros_babel_fish/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
<buildtool_depend>ament_cmake</buildtool_depend>
1717

1818
<depend>ament_index_cpp</depend>
19+
<depend>geometry_msgs</depend>
1920
<depend>rclcpp</depend>
2021
<depend>rclcpp_action</depend>
2122
<depend>rcpputils</depend>
2223
<depend>rosidl_runtime_cpp</depend>
2324
<depend>rosidl_typesupport_cpp</depend>
2425
<depend>rosidl_typesupport_introspection_cpp</depend>
2526
<build_depend>action_tutorials_interfaces</build_depend>
26-
<build_depend>geometry_msgs</build_depend>
2727
<test_depend>ament_cmake_gtest</test_depend>
2828
<test_depend>ament_cmake_clang_format</test_depend>
2929
<test_depend>ament_cmake_cppcheck</test_depend>

ros_babel_fish/test/message.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,11 @@ TEST( MessageTest, compoundMessage )
285285
}
286286
ASSERT_TRUE( shared_pointer_alive );
287287

288+
// Obtain a known subtype
289+
EXPECT_THROW( auto pose = msg->message<geometry_msgs::msg::Pose>(), BabelFishException );
290+
EXPECT_NO_THROW( auto pose =
291+
( *msg )["pose"].as<CompoundMessage>().message<geometry_msgs::msg::Pose>() );
292+
288293
{
289294
auto pose = ( *msg )["pose"].as<CompoundMessage>();
290295
msg.reset();

0 commit comments

Comments
 (0)