ros2_control - rolling
Loading...
Searching...
No Matches
lexical_casts.hpp
1// Copyright 2023 ros2_control Development Team
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#ifndef HARDWARE_INTERFACE__LEXICAL_CASTS_HPP_
16#define HARDWARE_INTERFACE__LEXICAL_CASTS_HPP_
17
18#include <cstdint>
19#include <limits>
20#include <regex>
21#include <sstream>
22#include <stdexcept>
23#include <string>
24#include <type_traits>
25#include <vector>
26
27namespace hardware_interface
28{
29
35double stod(const std::string & s);
36
40float stof(const std::string & s);
41
46template <typename T>
47T stoi_generic(const std::string & s)
48{
49 static_assert(std::is_integral_v<T> && std::is_signed_v<T>, "T must be a signed integral type");
50
51 size_t pos;
52 const auto v = std::stol(s, &pos); // stol returns long
53 if (pos != s.length())
54 {
55 throw std::invalid_argument("Invalid characters in string");
56 }
57 if (v < std::numeric_limits<T>::min() || v > std::numeric_limits<T>::max())
58 {
59 throw std::out_of_range("value not in range of target type");
60 }
61
62 return static_cast<T>(v);
63}
64
65// Explicit instantiations for common signed integer types
66inline int8_t stoi8(const std::string & s) { return stoi_generic<int8_t>(s); }
67inline int16_t stoi16(const std::string & s) { return stoi_generic<int16_t>(s); }
68inline int32_t stoi32(const std::string & s) { return stoi_generic<int32_t>(s); }
69
74template <typename T>
75T stoui_generic(const std::string & s)
76{
77 static_assert(
78 std::is_integral_v<T> && std::is_unsigned_v<T>, "T must be an unsigned integral type");
79
80 size_t pos;
81 const auto v = std::stoul(s, &pos); // stoul returns unsigned long
82 if (pos != s.length())
83 {
84 throw std::invalid_argument("Invalid characters in string");
85 }
86 if (v > std::numeric_limits<T>::max())
87 {
88 throw std::out_of_range("value not in range of target type");
89 }
90
91 return static_cast<T>(v);
92}
93
94// Explicit instantiations for common unsigned integer types
95inline uint8_t stoui8(const std::string & s) { return stoui_generic<uint8_t>(s); }
96inline uint16_t stoui16(const std::string & s) { return stoui_generic<uint16_t>(s); }
97inline uint32_t stoui32(const std::string & s) { return stoui_generic<uint32_t>(s); }
98
104std::string to_lower_case(const std::string & string);
105
112bool parse_bool(const std::string & bool_string);
113
114template <typename T>
115std::vector<T> parse_array(const std::string & array_string)
116{
117 // Use regex to check for a flat array: starts with [, ends with ], no nested brackets
118 const std::regex array_regex(R"(^\[\s*([^\[\]]*\s*(,\s*[^\[\]]+\s*)*)?\]$)");
119 if (!std::regex_match(array_string, array_regex))
120 {
121 throw std::invalid_argument(
122 "String must be a flat array: starts with '[' and ends with ']', no nested arrays");
123 }
124
125 // Use regex for the expression that either empty or contains only spaces
126 const std::regex empty_or_spaces_regex(R"(^\[\s*\]$)");
127 if (std::regex_match(array_string, empty_or_spaces_regex))
128 {
129 return {}; // Return empty array if input is "[]"
130 }
131
132 // Use regex to find cases of comma-separated but only whitespaces or no spaces between them like
133 // "[,]" "[a,b,,c]"
134 const std::regex comma_separated_regex(R"(^\[\s*([^,\s]+(\s*,\s*[^,\s]+)*)?\s*\]$)");
135 if (!std::regex_match(array_string, comma_separated_regex))
136 {
137 throw std::invalid_argument(
138 "String must be a flat array with comma-separated values and no spaces between them");
139 }
140
141 std::vector<T> result = {};
142 if (array_string == "[]")
143 {
144 return result; // Return empty array if input is "[]"
145 }
146
147 // regex for comma separated values and no spaces between them or just content like "[a,b,c]" or
148 // "[a]" or "[a, b, c]"
149 const std::regex value_regex(R"([^\s,\[\]]+)");
150 auto begin = std::sregex_iterator(array_string.begin(), array_string.end(), value_regex);
151 auto end = std::sregex_iterator();
152
153 for (auto it = begin; it != end; ++it)
154 {
155 const std::string value_str = it->str(); // Get the first capturing group
156 if constexpr (std::is_same_v<T, std::string>)
157 {
158 result.push_back(value_str);
159 }
160 else if constexpr (std::is_same_v<T, bool>)
161 {
162 result.push_back(parse_bool(value_str));
163 }
164 else if constexpr (std::is_floating_point_v<T> || std::is_integral_v<T>)
165 {
166 try
167 {
168 const T value = static_cast<T>(hardware_interface::stod(value_str));
169 result.push_back(value);
170 }
171 catch (const std::exception &)
172 {
173 throw std::invalid_argument(
174 "Failed converting string to floating point or integer: " + value_str);
175 }
176 }
177 else
178 {
179 throw std::invalid_argument("Unsupported type for parsing: " + std::string(typeid(T).name()));
180 }
181 }
182 return result;
183}
184
185std::vector<std::string> parse_string_array(const std::string & string_array_string);
186
187} // namespace hardware_interface
188
189#endif // HARDWARE_INTERFACE__LEXICAL_CASTS_HPP_
Definition mujoco_system_interface.hpp:69
T stoui_generic(const std::string &s)
Overflow-safe conversion from string to uint32_t.
Definition lexical_casts.hpp:75
std::string to_lower_case(const std::string &string)
Convert a string to lower case.
Definition lexical_casts.cpp:103
T stoi_generic(const std::string &s)
Overflow-safe conversion from string to int32_t.
Definition lexical_casts.hpp:47
float stof(const std::string &s)
Helper function to convert a std::string to float in a locale-independent way.
Definition lexical_casts.cpp:94
double stod(const std::string &s)
Helper function to convert a std::string to double in a locale-independent way.
Definition lexical_casts.cpp:85
bool parse_bool(const std::string &bool_string)
Parse a boolean value from a string.
Definition lexical_casts.cpp:112