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