ROS 2 rclcpp + rcl - kilted  kilted
ROS 2 C++ Client Library with ROS Client Library
expand_topic_or_service_name.cpp
1 // Copyright 2017 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "rclcpp/expand_topic_or_service_name.hpp"
16 
17 #include <string>
18 
19 #include "rcl/expand_topic_name.h"
21 #include "rclcpp/exceptions.hpp"
22 #include "rcutils/logging_macros.h"
23 #include "rcutils/types/string_map.h"
24 #include "rmw/error_handling.h"
25 #include "rmw/validate_namespace.h"
26 #include "rmw/validate_node_name.h"
27 #include "rmw/validate_full_topic_name.h"
28 
29 using rclcpp::exceptions::throw_from_rcl_error;
30 
31 std::string
33  const std::string & name,
34  const std::string & node_name,
35  const std::string & namespace_,
36  bool is_service)
37 {
38  char * expanded_topic = nullptr;
40  rcutils_allocator_t rcutils_allocator = rcutils_get_default_allocator();
41  rcutils_string_map_t substitutions_map = rcutils_get_zero_initialized_string_map();
42 
43  rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions_map, 0, rcutils_allocator);
44  if (rcutils_ret != RCUTILS_RET_OK) {
45  if (rcutils_ret == RCUTILS_RET_BAD_ALLOC) {
46  throw_from_rcl_error(RCL_RET_BAD_ALLOC, "", rcutils_get_error_state(), rcutils_reset_error);
47  } else {
48  throw_from_rcl_error(RCL_RET_ERROR, "", rcutils_get_error_state(), rcutils_reset_error);
49  }
50  }
51  rcl_ret_t ret = rcl_get_default_topic_name_substitutions(&substitutions_map);
52  if (ret != RCL_RET_OK) {
53  const rcutils_error_state_t * error_state = rcl_get_error_state();
54  // finalize the string map before throwing
55  rcutils_ret = rcutils_string_map_fini(&substitutions_map);
56  if (rcutils_ret != RCUTILS_RET_OK) {
57  RCUTILS_LOG_ERROR_NAMED(
58  "rclcpp",
59  "failed to fini string_map (%d) during error handling: %s",
60  rcutils_ret,
61  rcutils_get_error_string().str);
62  rcutils_reset_error();
63  }
64  throw_from_rcl_error(ret, "", error_state);
65  }
66 
68  name.c_str(),
69  node_name.c_str(),
70  namespace_.c_str(),
71  &substitutions_map,
72  allocator,
73  &expanded_topic);
74 
75  std::string result;
76  if (ret == RCL_RET_OK) {
77  result = expanded_topic;
78  allocator.deallocate(expanded_topic, allocator.state);
79  }
80 
81  rcutils_ret = rcutils_string_map_fini(&substitutions_map);
82  if (rcutils_ret != RCUTILS_RET_OK) {
83  throw_from_rcl_error(RCL_RET_ERROR, "", rcutils_get_error_state(), rcutils_reset_error);
84  }
85 
86  // expansion failed
87  if (ret != RCL_RET_OK) {
88  // if invalid topic or unknown substitution
90  rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
91  int validation_result;
92  size_t invalid_index;
93  rcl_ret_t ret = rcl_validate_topic_name(name.c_str(), &validation_result, &invalid_index);
94  if (ret != RCL_RET_OK) {
95  throw_from_rcl_error(ret);
96  }
97 
98  if (validation_result != RCL_TOPIC_NAME_VALID) {
99  const char * validation_message =
100  rcl_topic_name_validation_result_string(validation_result);
101  if (is_service) {
103  throw InvalidServiceNameError(name.c_str(), validation_message, invalid_index);
104  } else {
106  throw InvalidTopicNameError(name.c_str(), validation_message, invalid_index);
107  }
108  } else {
109  throw std::runtime_error("topic name unexpectedly valid");
110  }
111 
112  // if invalid node name
113  } else if (ret == RCL_RET_NODE_INVALID_NAME) {
114  rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
115  int validation_result;
116  size_t invalid_index;
117  rmw_ret_t rmw_ret =
118  rmw_validate_node_name(node_name.c_str(), &validation_result, &invalid_index);
119  if (rmw_ret != RMW_RET_OK) {
120  if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
121  throw_from_rcl_error(
122  RCL_RET_INVALID_ARGUMENT, "failed to validate node name",
123  rmw_get_error_state(), rmw_reset_error);
124  }
125  throw_from_rcl_error(
126  RCL_RET_ERROR, "failed to validate node name",
127  rmw_get_error_state(), rmw_reset_error);
128  }
129 
130  if (validation_result != RMW_NODE_NAME_VALID) {
132  node_name.c_str(),
133  rmw_node_name_validation_result_string(validation_result),
134  invalid_index);
135  } else {
136  throw std::runtime_error("invalid rcl node name but valid rmw node name");
137  }
138 
139  // if invalid namespace
140  } else if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
141  rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
142  int validation_result;
143  size_t invalid_index;
144  rmw_ret_t rmw_ret =
145  rmw_validate_namespace(namespace_.c_str(), &validation_result, &invalid_index);
146  if (rmw_ret != RMW_RET_OK) {
147  if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
148  throw_from_rcl_error(
149  RCL_RET_INVALID_ARGUMENT, "failed to validate namespace",
150  rmw_get_error_state(), rmw_reset_error);
151  }
152  throw_from_rcl_error(
153  RCL_RET_ERROR, "failed to validate namespace",
154  rmw_get_error_state(), rmw_reset_error);
155  }
156 
157  if (validation_result != RMW_NAMESPACE_VALID) {
159  namespace_.c_str(),
160  rmw_namespace_validation_result_string(validation_result),
161  invalid_index);
162  } else {
163  throw std::runtime_error("invalid rcl namespace but valid rmw namespace");
164  }
165  // something else happened
166  } else {
167  throw_from_rcl_error(ret);
168  }
169  }
170 
171  // expand succeeded, but full name validation may fail still
172  int validation_result;
173  size_t invalid_index;
174  rmw_ret_t rmw_ret =
175  rmw_validate_full_topic_name(result.c_str(), &validation_result, &invalid_index);
176  if (rmw_ret != RMW_RET_OK) {
177  if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
178  throw_from_rcl_error(
179  RCL_RET_INVALID_ARGUMENT, "failed to validate full topic name",
180  rmw_get_error_state(), rmw_reset_error);
181  }
182  throw_from_rcl_error(
183  RCL_RET_ERROR, "failed to validate full topic name",
184  rmw_get_error_state(), rmw_reset_error);
185  }
186 
187  if (validation_result != RMW_TOPIC_VALID) {
188  if (is_service) {
190  result.c_str(),
191  rmw_full_topic_name_validation_result_string(validation_result),
192  invalid_index);
193  } else {
195  result.c_str(),
196  rmw_full_topic_name_validation_result_string(validation_result),
197  invalid_index);
198  }
199  }
200 
201  return result;
202 }
#define rcl_get_default_allocator
Return a properly initialized rcl_allocator_t with default values.
Definition: allocator.h:37
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
Thrown when a node namespace is invalid.
Definition: exceptions.hpp:78
Thrown when a node name is invalid.
Definition: exceptions.hpp:69
Thrown when a service name is invalid.
Definition: exceptions.hpp:96
Thrown when a topic name is invalid.
Definition: exceptions.hpp:87
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_expand_topic_name(const char *input_topic_name, const char *node_name, const char *node_namespace, const rcutils_string_map_t *substitutions, rcl_allocator_t allocator, char **output_topic_name)
Expand a given topic name into a fully-qualified topic name.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_default_topic_name_substitutions(rcutils_string_map_t *string_map)
Fill a given string map with the default substitution pairs.
RCLCPP_PUBLIC std::string expand_topic_or_service_name(const std::string &name, const std::string &node_name, const std::string &namespace_, bool is_service=false)
Expand a topic or service name and throw if it is not valid.
#define RCL_RET_NODE_INVALID_NAMESPACE
Invalid node namespace return code.
Definition: types.h:63
#define RCL_RET_UNKNOWN_SUBSTITUTION
Topic name substitution is unknown.
Definition: types.h:51
#define RCL_RET_OK
Success return code.
Definition: types.h:27
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:33
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:35
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:29
#define RCL_RET_TOPIC_NAME_INVALID
Topic name does not pass validation.
Definition: types.h:47
#define RCL_RET_NODE_INVALID_NAME
Invalid node name return code.
Definition: types.h:61
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_validate_topic_name(const char *topic_name, int *validation_result, size_t *invalid_index)
Validate a given topic name.
#define RCL_TOPIC_NAME_VALID
The topic name is valid.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_topic_name_validation_result_string(int validation_result)
Return a validation result description, or NULL if unknown or RCL_TOPIC_NAME_VALID.