param_utils.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 // SPDX-License-Identifier: BSD-3-Clause
4 // SPDX-FileCopyrightText: Czech Technical University in Prague
5 
12 #include <functional>
13 #include <list>
14 #include <map>
15 #include <memory>
16 #include <set>
17 #include <string>
18 #include <unordered_set>
19 #include <unordered_map>
20 #include <utility>
21 #include <vector>
22 
31 
32 namespace cras
33 {
34 
39 class GetParamException : public ::std::runtime_error
40 {
41 public:
46  explicit GetParamException(const ::cras::GetParamResultInfo& info) : ::std::runtime_error(info.message), info(info) {}
47 
50 };
51 
60 template <typename ResultType, typename ParamServerType>
61 using check_get_param_types = typename std::enable_if_t<
62  // getParam() cannot handle cras::optional types
64  // C strings are handled via overloads as GetParamOptions is undefined for them
67 >;
68 
93 template<typename ResultType, typename ParamServerType = typename ::cras::DefaultParamServerType<ResultType>::type,
95 inline ::cras::GetParamResult<ResultType> getParamVerbose(
96  const ::cras::GetParamAdapter& param, const ::std::string& name,
97  const ::cras::optional<ResultType>& defaultValue = ResultType(),
98  const ::std::string& unit = "",
99  const ::cras::GetParamOptions<ResultType, ParamServerType>& options = {},
100  const ::cras::LogHelper* const logger = nullptr)
101 {
103  ParamServerType value;
104  bool shouldThrow {false};
105  bool useDefault {false};
106  const bool isRequired = !defaultValue.has_value();
107  ::std::list<::std::string> errors;
108 
109  info.convertFailed = false;
110  info.requiredMissing = false;
111  const auto origNs = options.origNamespace.empty() ? param.getNamespace() : options.origNamespace;
112  const auto origParamName = options.origParamName.empty() ? name : options.origParamName;
113 
114  ::XmlRpc::XmlRpcValue xmlValue;
115  if (param.getParam(name, xmlValue)) // try getting the parameter as XmlRpcValue
116  {
117  if (!options.toParam(xmlValue, value, !options.throwIfConvertFails, &errors)) // try converting to ParamServerType
118  {
119  // if conversion failed, report appropriate error
121  {
122  info.message = ::cras::format(
123  "%s: Parameter %s found, but it has wrong XmlRpc type. Expected type %s, got type %s with value %s.",
124  origNs.c_str(), origParamName.c_str(),
126  ::cras::to_cstring(xmlValue.getType()),
127  ::cras::to_string(xmlValue).c_str());
128  }
129  else
130  {
131  ::std::list<::std::string> uniqueErrors;
132  for (const auto& error : errors)
133  {
134  if (std::find(uniqueErrors.begin(), uniqueErrors.end(), error) == uniqueErrors.end())
135  uniqueErrors.push_back(error);
136  }
137  info.message = ::cras::format(
138  "%s: Parameter %s found with correct XmlRpc type %s and value %s, "
139  "but its conversion to type %s has failed due to the following errors: %s.",
140  origNs.c_str(), origParamName.c_str(),
142  ::cras::to_string(xmlValue).c_str(),
143  ::cras::getTypeName<ParamServerType>().c_str(),
144  ::cras::to_string(uniqueErrors).c_str());
145  }
146  info.messageLevel = ::ros::console::Level::Error;
147 
148  if (isRequired || options.throwIfConvertFails)
149  shouldThrow = true;
150  else
151  useDefault = true;
152 
153  info.convertFailed = true;
154  if (isRequired)
155  info.requiredMissing = true;
156  }
157  }
158  else // param does not exist on param server
159  {
160  // nested parameters contain a slash and the name of the parameter can be split by the slashes
161  // and searched for recursively
162  if (options.allowNestedParams && ::cras::contains(name, '/'))
163  {
164  const auto parts = ::cras::split(name, "/", 1);
165  if (parts.size() == 2 && !parts[0].empty() && !parts[1].empty())
166  {
167  const auto& head = parts[0];
168  const auto& tail = parts[1];
169  try
170  {
171  auto nsParams = param.getNamespaced(head);
172  auto nestedOptions = options;
173  nestedOptions.origNamespace = origNs;
174  nestedOptions.origParamName = origParamName;
175  return ::cras::getParamVerbose(*nsParams, tail, defaultValue, unit, nestedOptions, logger);
176  }
177  catch (const ::std::runtime_error& e)
178  {
179  // ignore the error, we just couldn't find the nested param
180  }
181  }
182  }
183  // nested param processing calls return on success, so if we got here, it failed
184  info.message = ::cras::format("%s: Cannot find value for parameter: %s.",
185  origNs.c_str(), origParamName.c_str());
186  if (!isRequired)
187  {
188  info.messageLevel = options.printDefaultAsWarn ? ::ros::console::Level::Warn : ::ros::console::Level::Info;
189  useDefault = true;
190  }
191  else
192  {
193  info.messageLevel = ::ros::console::Level::Error;
194  info.requiredMissing = true;
195  shouldThrow = true;
196  }
197  }
198 
199  ::std::string defaultUsedMessage {};
200  if (defaultValue.has_value())
201  defaultUsedMessage = ::cras::format(" Assigning default: %s%s.",
202  options.resultToStr(defaultValue.value()).c_str(), ::cras::prependIfNonEmpty(unit, " ").c_str());
203 
204  if (useDefault)
205  info.message += defaultUsedMessage;
206 
207  info.defaultUsed = useDefault;
208 
209  if (shouldThrow)
210  {
211  if (logger != nullptr && options.printMessages)
212  CRAS_LOG(logger, info.messageLevel, ROSCONSOLE_DEFAULT_NAME, info.message);
213  throw GetParamException(info);
214  }
215 
216  // using a pointer allows using ResultType without a no-arg constructor (copy/move constructor is enough)
217  ::std::unique_ptr<ResultType> resultValue;
218  if (useDefault)
219  {
220  resultValue = ::std::make_unique<ResultType>(defaultValue.value());
221  }
222  else
223  {
224  try
225  {
226  resultValue = ::std::make_unique<ResultType>(options.toResult(value)); // try converting to ResultType
227 
228  info.message = ::cras::format("%s: Found parameter: %s, value: %s%s.",
229  origNs.c_str(), origParamName.c_str(), options.resultToStr(*resultValue).c_str(),
230  ::cras::prependIfNonEmpty(unit, " ").c_str());
231  if (errors.empty())
232  {
233  info.messageLevel = ros::console::Level::Info;
234  }
235  else
236  {
237  ::std::list<::std::string> uniqueErrors;
238  for (const auto& error : errors)
239  {
240  if (std::find(uniqueErrors.begin(), uniqueErrors.end(), error) == uniqueErrors.end())
241  uniqueErrors.push_back(error);
242  }
243  info.message += " Some parts of the value were skipped because of the following conversion errors: " +
244  ::cras::to_string(uniqueErrors);
245  info.messageLevel = ::ros::console::Level::Warn;
246  }
247  }
248  catch (const std::runtime_error& e) // conversion from ParamServerType to ResultType failed
249  {
250  info.message = ::cras::format(
251  "%s: Cannot convert value '%s' of parameter %s to requested type %s (error: %s).",
252  origNs.c_str(), options.paramToStr(value).c_str(),
253  origParamName.c_str(),
254  ::cras::getTypeName<ResultType>().c_str(),
255  e.what());
256  info.messageLevel = ::ros::console::Level::Error;
257 
258  if (isRequired || options.throwIfConvertFails)
259  {
260  shouldThrow = true;
261  }
262  else // use default if provided
263  {
264  info.message += defaultUsedMessage;
265  resultValue = ::std::make_unique<ResultType>(defaultValue.value());
266  info.defaultUsed = true;
267  }
268 
269  info.convertFailed = true;
270  if (isRequired)
271  info.requiredMissing = true;
272  }
273  }
274 
275  if (logger != nullptr && options.printMessages)
276  CRAS_LOG(logger, info.messageLevel, ROSCONSOLE_DEFAULT_NAME, info.message);
277 
278  if (shouldThrow)
279  throw GetParamException(info);
280 
281  return {*resultValue, info};
282 }
283 
302 template<typename ResultType, typename ParamServerType = typename ::cras::DefaultParamServerType<ResultType>::type,
304 inline ::cras::GetParamResult<ResultType> getParamVerbose(
305  const ::cras::GetParamAdapter& param, const ::std::string &name,
306  const ResultType& defaultValue = ResultType(),
307  const ::std::string& unit = "",
308  const ::cras::GetParamOptions<ResultType, ParamServerType>& options = {},
309  const ::cras::LogHelper* const logger = nullptr)
310 {
311  return ::cras::getParamVerbose(param, name, ::cras::optional<ResultType>(defaultValue), unit, options, logger);
312 }
313 
333 template<typename ResultType, typename ParamServerType = typename ::cras::DefaultParamServerType<ResultType>::type,
335 inline ResultType getParam(
336  const ::cras::GetParamAdapter& param, const ::std::string &name,
337  const ::cras::optional<ResultType>& defaultValue = ResultType(),
338  const ::std::string& unit = "",
339  const ::cras::GetParamOptions<ResultType, ParamServerType>& options = {},
340  const ::cras::LogHelper* const logger = nullptr)
341 {
342  return ::cras::getParamVerbose(param, name, defaultValue, unit, options, logger).value;
343 }
344 
363 template<typename ResultType, typename ParamServerType = typename ::cras::DefaultParamServerType<ResultType>::type,
365 inline ResultType getParam(
366  const ::cras::GetParamAdapter& param, const ::std::string &name,
367  const ResultType& defaultValue = ResultType(),
368  const ::std::string& unit = "",
369  const ::cras::GetParamOptions<ResultType, ParamServerType>& options = {},
370  const ::cras::LogHelper* const logger = nullptr)
371 {
372  return ::cras::getParamVerbose(param, name, defaultValue, unit, options, logger).value;
373 }
374 
375 // std::string - char interop specializations
376 
392 inline ::cras::GetParamResult<::std::string> getParamVerbose(
393  const ::cras::GetParamAdapter& param, const ::std::string &name,
394  const ::cras::optional<const char *>& defaultValue, const ::std::string &unit = "",
395  const ::cras::GetParamOptions<::std::string>& options = {}, const ::cras::LogHelper* const logger = nullptr)
396 {
397  ::cras::optional<::std::string> defaultStr;
398  if (defaultValue.has_value())
399  defaultStr = defaultValue.value();
400  return ::cras::getParamVerbose(param, name, defaultStr, unit, options, logger);
401 }
402 
417 inline ::cras::GetParamResult<::std::string> getParamVerbose(
418  const ::cras::GetParamAdapter& param, const ::std::string &name,
419  const char* defaultValue, const ::std::string &unit = "",
420  const ::cras::GetParamOptions<::std::string>& options = {}, const ::cras::LogHelper* const logger = nullptr)
421 {
422  ::cras::optional<::std::string> defaultStr(defaultValue);
423  return ::cras::getParamVerbose(param, name, defaultStr, unit, options, logger);
424 }
425 
441 inline ::std::string getParam(
442  const ::cras::GetParamAdapter& param, const ::std::string &name,
443  const ::cras::optional<const char *>& defaultValue, const ::std::string &unit = "",
444  const ::cras::GetParamOptions<::std::string>& options = {}, const ::cras::LogHelper* const logger = nullptr)
445 {
446  return ::cras::getParamVerbose(param, name, defaultValue, unit, options, logger).value;
447 }
448 
463 inline ::std::string getParam(
464  const ::cras::GetParamAdapter& param, const ::std::string &name,
465  const char* defaultValue, const ::std::string &unit = "",
466  const ::cras::GetParamOptions<::std::string>& options = {}, const ::cras::LogHelper* const logger = nullptr)
467 {
468  return ::cras::getParamVerbose(param, name, defaultValue, unit, options, logger).value;
469 }
470 
484 #define DEFINE_CONVERTING_GET_PARAM(resultType, paramServerType, defaultUnit, convertToResultFn) \
485 template<>\
486 struct ParamToStringFn<resultType>\
487 {\
488  static ::std::string to_string(const resultType& v){ return ::cras::to_string(v); }\
489 };\
490 \
491 template<>\
492 struct DefaultToResultFn<resultType, paramServerType>\
493 {\
494  static resultType toResult(const paramServerType& v){ return convertToResultFn(v); }\
495 };\
496 \
497 template<>\
498 struct DefaultParamServerType<resultType>\
499 {\
500  typedef paramServerType type;\
501 };
502 
512 #define DEFINE_CONVERTING_GET_PARAM_WITH_CONSTRUCTOR(resultType, paramServerType, defaultUnit) \
513 DEFINE_CONVERTING_GET_PARAM(resultType, paramServerType, defaultUnit, \
514  ([](const paramServerType& v) { return resultType(v); }))
515 
525 #define DEFINE_CONVERTING_GET_PARAM_WITH_CAST(resultType, paramServerType, defaultUnit) \
526 DEFINE_CONVERTING_GET_PARAM(resultType, paramServerType, defaultUnit, \
527  ([](const paramServerType& v) { return static_cast<resultType>(v); }))
528 }
529 
530 #if __has_include(<ros/duration.h>)
532 #endif
533 
534 #if __has_include(<geometry_msgs/Vector3.h>)
536 #endif
537 
538 #if __has_include(<tf2/LinearMath/Vector3.h>)
540 #endif
541 
542 #if __has_include(<Eigen/Core>)
544 #endif
cras::GetParamResultInfo::message
::std::string message
The log message (returned even if option printMessages is false).
Definition: get_param_result.hpp:34
cras::is_c_string
Type trait for dynamic-sized and constant-sized C strings.
Definition: string_traits.hpp:23
cras::prependIfNonEmpty
::std::string prependIfNonEmpty(const ::std::string &str, const ::std::string &prefix)
If str is nonempty, returns prefix + str, otherwise empty string.
cras::contains
bool contains(const ::std::string &str, char c)
Check whether str contains character c.
cras::to_cstring
constexpr const char * to_cstring(const ::XmlRpc::XmlRpcValue::Type &value)
Return a string representation of the XmlRpcValue type.
Definition: xmlrpc_value_traits.hpp:33
cras::is_optional
Type trait determining whether type T is cras::optional or not.
Definition: optional.hpp:38
cras
Definition: any.hpp:15
ros.hpp
Specializations of getParam() for ROS basic types.
cras::GetParamResultInfo::messageLevel
::ros::console::Level messageLevel
Severity of the log message.
Definition: get_param_result.hpp:37
cras::GetParamException::GetParamException
GetParamException(const ::cras::GetParamResultInfo &info)
Construct the exception.
Definition: param_utils.hpp:46
cras::XmlRpcValueTraits
Type traits for XmlRpcValue.
Definition: xmlrpc_value_traits.hpp:70
cras::format
inline ::std::string format(::std::string format,...)
Definition: string_utils.hpp:280
string_utils.hpp
Utils for working with strings.
cras::GetParamResultInfo::convertFailed
bool convertFailed
Whether a value conversion failed.
Definition: get_param_result.hpp:28
get_param_adapter.hpp
An adapter that allows getting ROS parameters from various sources.
tf2.hpp
Specializations of getParam() for tf2 types.
cras::GetParamResultInfo::defaultUsed
bool defaultUsed
Whether the default value has been used.
Definition: get_param_result.hpp:25
cras::check_get_param_types
typename std::enable_if_t< !::cras::is_optional< ResultType >::value &&!::cras::is_c_string< ResultType >::value &&!::cras::is_c_string< ParamServerType >::value > check_get_param_types
This type is a TrueType if the combination of ResultType and ParamServerType is valid.
Definition: param_utils.hpp:67
cras::getParamVerbose
GetParamResult< ResultType > getParamVerbose(const ::ros::NodeHandle &node, const ::std::string &name, const ::cras::optional< ResultType > &defaultValue=ResultType(), const ::std::string &unit="", const ::cras::GetParamOptions< ResultType, ParamServerType > &options={})
Get the value of the given ROS parameter, falling back to the specified default value (if not nullopt...
Definition: node_utils.hpp:51
type_utils.hpp
Utilities for working with C++ types.
geometry_msgs.hpp
Specializations of getParam() for geometry_msgs.
cras::getParam
ResultType getParam(const ::ros::NodeHandle &node, const ::std::string &name, const ::cras::optional< ResultType > &defaultValue=ResultType(), const ::std::string &unit="", const ::cras::GetParamOptions< ResultType, ParamServerType > &options={})
Get the value of the given ROS parameter, falling back to the specified default value (if not nullopt...
Definition: node_utils.hpp:110
cras::getParamVerbose
inline ::cras::GetParamResult<::std::string > getParamVerbose(const ::cras::GetParamAdapter &param, const ::std::string &name, const char *defaultValue, const ::std::string &unit="", const ::cras::GetParamOptions<::std::string > &options={}, const ::cras::LogHelper *const logger=nullptr)
Get the value of the given ROS parameter, falling back to the specified default value,...
Definition: param_utils.hpp:417
cras::split
::std::vector<::std::string > split(const ::std::string &str, const ::std::string &delimiter, int maxSplits=-1)
Split the given string by the given delimiter.
cras::GetParamResultInfo
Detailed information about the executed getParam() call.
Definition: get_param_result.hpp:22
get_param_result.hpp
Wrapper of getParam() call results.
log_utils.h
ROS logging helpers.
XmlRpc::XmlRpcValue::getType
const Type & getType() const
cras::GetParamException::info
::cras::GetParamResultInfo info
Details about getParam() execution.
Definition: param_utils.hpp:49
eigen.hpp
Specializations of getParam() for Eigen vectors and matrices.
cras::GetParamException
Exception thrown when conversion of a parameter fails during getParam() if option throwIfConvertFails...
Definition: param_utils.hpp:39
get_param_options.hpp
Options for getParam() calls.
std
cras::GetParamResultInfo::requiredMissing
bool requiredMissing
Whether a required parameter was found missing or could not be read.
Definition: get_param_result.hpp:31
param
T param(const std::string &param_name, const T &default_val)
cras::to_string
inline ::std::string to_string(const ::Eigen::Matrix< Scalar, Rows, Cols, Options, MaxRows, MaxCols > &value)
Definition: string_utils/eigen.hpp:21
xmlrpc_value_utils.hpp
Utilities for working with XmlRpcValues.
XmlRpc::XmlRpcValue
CRAS_LOG
#define CRAS_LOG(logger, level, name,...)
Log to a given named logger at a given verbosity level, with printf-style formatting.
Definition: macros.h:320
optional.hpp
A C++11 shim for std::optional. Uses std::optional when used in C++17 mode.


cras_cpp_common
Author(s): Martin Pecka
autogenerated on Tue Nov 26 2024 03:49:04