Easy Navigation
Loading...
Searching...
No Matches
NavState.hpp
Go to the documentation of this file.
1// Copyright 2025 Intelligent Robotics Lab
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
21
22#ifndef EASYNAV__TYPES__NAVSTATE_HPP_
23#define EASYNAV__TYPES__NAVSTATE_HPP_
24
25#include <string>
26#include <unordered_map>
27#include <unordered_set>
28#include <memory>
29#include <mutex>
30#include <stdexcept>
31#include <sstream>
32#include <type_traits>
33#include <iostream>
34#include <functional>
35#include <execinfo.h>
36#include <typeinfo>
37#include <cxxabi.h>
38#include <execinfo.h>
39
40namespace easynav
41{
42
46inline std::string demangle(const char * name)
47{
48 int status = 0;
49 char * p = abi::__cxa_demangle(name, nullptr, nullptr, &status);
50 std::string out = (status == 0 && p) ? p : name;
51 std::free(p);
52 return out;
53}
54
59inline std::string stacktrace(std::size_t skip = 1, std::size_t max_frames = 64)
60{
61 void * buf[128];
62 const int n = backtrace(buf, static_cast<int>(std::min<std::size_t>(max_frames, 128)));
63 char ** syms = backtrace_symbols(buf, n);
64 std::ostringstream oss;
65 for (int i = static_cast<int>(skip); i < n; ++i) {
66 oss << "#" << (i - static_cast<int>(skip)) << " " << syms[i] << "\n";
67 }
68 std::free(syms);
69 return oss.str();
70}
71
83{
84public:
90
92 virtual ~NavState() = default;
93
103 template<typename T>
104 void set(const std::string & key, const T & value)
105 {
106 std::lock_guard<std::mutex> lock(state_mutex_);
107 auto it = values_.find(key);
108
109 if (it == values_.end()) {
110 values_[key] = std::make_shared<T>(value);
111 types_[key] = typeid(T).hash_code();
112 type_names_[key] = demangle(typeid(T).name());
113 } else {
114 if (types_[key] != typeid(T).hash_code()) {
115 std::ostringstream oss;
116 oss << "Type mismatch in set(\"" << key << "\")\n"
117 << " stored type : " << type_names_[key] << "\n"
118 << " provided type: " << demangle(typeid(T).name()) << "\n"
119 << "Backtrace:\n" << stacktrace(1);
120 throw std::runtime_error(oss.str());
121 }
122
123 auto ptr = std::static_pointer_cast<T>(it->second);
124 *ptr = value;
125 }
126 }
127
137 template<typename T>
138 void set(const std::string & key, const std::shared_ptr<T> value_ptr)
139 {
140 std::lock_guard<std::mutex> lock(state_mutex_);
141 auto it = values_.find(key);
142
143 if (it == values_.end()) {
144 values_[key] = std::shared_ptr<T>(value_ptr);
145 types_[key] = typeid(T).hash_code();
146 type_names_[key] = demangle(typeid(T).name());
147 } else {
148 if (types_[key] != typeid(T).hash_code()) {
149 std::ostringstream oss;
150 oss << "Type mismatch in set(\"" << key << "\")\n"
151 << " stored type : " << type_names_[key] << "\n"
152 << " provided type: " << demangle(typeid(T).name()) << "\n"
153 << "Backtrace:\n" << stacktrace(1);
154 throw std::runtime_error(oss.str());
155 }
156
157 it->second = value_ptr;
158 }
159 }
160
169 void set_group(const std::string & key, const std::vector<std::string> & group_keys)
170 {
171 std::lock_guard<std::mutex> lock(group_mutex_);
172 groups_[key] = group_keys;
173
174 // Also store the group keys as a value for introspection/debugging purposes.
175 set<std::vector<std::string>>(key, group_keys);
176 }
177
186 template<typename T>
187 const T & get(const std::string & key) const
188 {
189 std::lock_guard<std::mutex> lock(state_mutex_);
190 auto it = values_.find(key);
191
192 if (it == values_.end()) {
193 throw std::runtime_error("Key not found in get: " + key);
194 }
195
196 if (types_.at(key) != typeid(T).hash_code()) {
197 std::ostringstream oss;
198 oss << "Type mismatch in get(\"" << key << "\")\n"
199 << " stored type : " << type_names_.at(key) << "\n"
200 << " requested type: " << demangle(typeid(T).name());
201 throw std::runtime_error(oss.str());
202 }
203
204 auto ptr = std::static_pointer_cast<T>(it->second);
205 return *ptr;
206 }
207
216 template<typename T>
217 const std::shared_ptr<T> get_ptr(const std::string & key) const
218 {
219 std::lock_guard<std::mutex> lock(state_mutex_);
220 auto it = values_.find(key);
221
222 if (it == values_.end()) {
223 throw std::runtime_error("Key not found in get: " + key);
224 }
225
226 if (types_.at(key) != typeid(T).hash_code()) {
227 std::ostringstream oss;
228 oss << "Type mismatch in get_ptr(\"" << key << "\")\n"
229 << " stored type : " << type_names_.at(key) << "\n"
230 << " requested type: " << demangle(typeid(T).name());
231 throw std::runtime_error(oss.str());
232 }
233
234 return std::static_pointer_cast<T>(it->second);
235 }
236
245 template<typename T>
246 const std::vector<std::shared_ptr<T>> get_group(const std::string & group_key) const
247 {
248 std::lock_guard<std::mutex> lock(group_mutex_);
249 auto group_it = groups_.find(group_key);
250
251 if (group_it == groups_.end()) {
252 throw std::runtime_error("Group key not found in get_group: " + group_key);
253 }
254
255 const auto group_keys = group_it->second;
256
257 std::vector<std::shared_ptr<T>> out;
258 out.reserve(group_keys.size());
259
260 for (const auto & group_key : group_keys) {
261 try {
262 out.push_back(get_ptr<T>(group_key));
263 } catch (const std::runtime_error & e) {
264 std::cerr << "Error retrieving key '" << group_key << "' from group '" << group_key <<
265 "': " << e.what() << std::endl;
266 }
267 }
268
269 return out;
270 }
271
280 template<typename T>
281 std::vector<std::shared_ptr<T>> get_by_type() const
282 {
283 std::lock_guard<std::mutex> lock(state_mutex_);
284 std::vector<std::shared_ptr<T>> out;
285 const size_t target_hash = typeid(T).hash_code();
286 for (const auto & kv : values_) {
287 if (types_.at(kv.first) == target_hash) {
288 out.push_back(std::static_pointer_cast<T>(kv.second));
289 }
290 }
291 return out;
292 }
293
304 template<typename T>
305 std::vector<std::shared_ptr<T>> get_to_vector(const std::string & key) const
306 {
307 std::vector<std::shared_ptr<T>> out;
308 std::lock_guard<std::mutex> lock(state_mutex_);
309 auto it = values_.find(key);
310 if (it == values_.end()) {
311 return out;
312 }
313 if (types_.at(key) != typeid(T).hash_code()) {
314 return out;
315 }
316 out.push_back(std::static_pointer_cast<T>(it->second));
317 return out;
318 }
319
329 template<typename T>
330 std::vector<std::shared_ptr<T>> get_no_group() const
331 {
332 // Snapshot the set of all keys that belong to any group (under group lock).
333 std::unordered_set<std::string> grouped_keys;
334 {
335 std::lock_guard<std::mutex> glock(group_mutex_);
336 for (const auto & g : groups_) {
337 for (const auto & k : g.second) {
338 grouped_keys.insert(k);
339 }
340 }
341 }
342
343 // Iterate values and return those of type T whose key is not grouped.
344 std::lock_guard<std::mutex> slock(state_mutex_);
345 std::vector<std::shared_ptr<T>> out;
346 const size_t target_hash = typeid(T).hash_code();
347 for (const auto & kv : values_) {
348 if (types_.at(kv.first) == target_hash &&
349 grouped_keys.find(kv.first) == grouped_keys.end())
350 {
351 out.push_back(std::static_pointer_cast<T>(kv.second));
352 }
353 }
354 return out;
355 }
356
360 bool has(const std::string & key) const
361 {
362 return values_.find(key) != values_.end();
363 }
364
368 bool has_group(const std::string & key) const
369 {
370 return groups_.find(key) != groups_.end();
371 }
372
377 using AnyPrinter = std::function<std::string(std::shared_ptr<void>)>;
378
383 template<typename T>
384 static void register_printer(std::function<std::string(const T &)> printer)
385 {
386 auto wrapper = [printer](std::shared_ptr<void> base_ptr) -> std::string {
387 auto typed_ptr = std::static_pointer_cast<T>(base_ptr);
388 return printer(*typed_ptr);
389 };
390 type_printers_[typeid(T).hash_code()] = wrapper;
391 }
392
398 std::string debug_string() const
399 {
400 std::stringstream ss;
401 for (const auto & kv : values_) {
402 ss << kv.first << " = ";
403 auto ptr = kv.second;
404 if (ptr) {
405 auto type_it = types_.find(kv.first);
406 if (type_it != types_.end()) {
407 auto printer_it = type_printers_.find(type_it->second);
408 if (printer_it != type_printers_.end()) {
409 ss << "[" << ptr.get() << "] : " << printer_it->second(ptr);
410 } else {
411 ss << "[" << ptr.get() << "] : " << type_it->second << "]";
412 }
413 } else {
414 ss << "[" << ptr.get() << "] : unknown]";
415 }
416 } else {
417 ss << "[null]";
418 }
419 ss << std::endl;
420 }
421 return ss.str();
422 }
423
426 static void print_stacktrace()
427 {
428 void *array[50];
429 int size = backtrace(array, 50);
430 char **strings = backtrace_symbols(array, size);
431 std::cerr << "\nStack trace:\n";
432 for (int i = 0; i < size; ++i) {
433 std::cerr << strings[i] << std::endl;
434 }
435 std::cerr << std::endl;
436 free(strings);
437 }
438
443 {
444 register_printer<int>([](const int & v) {return std::to_string(v);});
445 register_printer<float>([](const float & v) {return std::to_string(v);});
446 register_printer<double>([](const double & v) {return std::to_string(v);});
447 register_printer<std::string>([](const std::string & v) {return v;});
448 register_printer<bool>([](const bool & v) {return v ? "true" : "false";});
449 register_printer<char>([](const char & v) {return std::string(1, v);});
451 [](const std::vector<std::string> & v) {
452 std::ostringstream oss;
453 oss << " " << v.size() << " [";
454 for (std::size_t i = 0; i < v.size(); ++i) {
455 if (i > 0) {oss << ", ";}
456 oss << v[i];
457 }
458 oss << "]";
459 return oss.str();
460 });
461 }
462
463private:
464 mutable std::mutex state_mutex_;
465 mutable std::mutex group_mutex_;
466
468 mutable std::unordered_map<std::string, std::shared_ptr<void>> values_;
469
471 mutable std::unordered_map<std::string, std::vector<std::string>> groups_;
472
474 mutable std::unordered_map<std::string, size_t> types_;
475
477 mutable std::unordered_map<std::string, std::string> type_names_;
478
480 static inline std::unordered_map<size_t, AnyPrinter> type_printers_;
481};
482
483} // namespace easynav
484
485#endif // EASYNAV__TYPES__NAVSTATE_HPP_
std::vector< std::shared_ptr< T > > get_no_group() const
Retrieves all stored values of type T that are NOT a member of any group.
Definition NavState.hpp:330
void set_group(const std::string &key, const std::vector< std::string > &group_keys)
Sets a new group of values as a list of strings.
Definition NavState.hpp:169
const T & get(const std::string &key) const
Retrieves a const reference to the stored value of type T for key.
Definition NavState.hpp:187
std::vector< std::shared_ptr< T > > get_to_vector(const std::string &key) const
Wraps a single stored value of type T in a one-element vector.
Definition NavState.hpp:305
const std::shared_ptr< T > get_ptr(const std::string &key) const
Retrieves the shared_ptr to the stored value of type T for key.
Definition NavState.hpp:217
std::function< std::string(std::shared_ptr< void >)> AnyPrinter
Type alias for a generic printer functor used by debug_string().
Definition NavState.hpp:377
void set(const std::string &key, const std::shared_ptr< T > value_ptr)
Stores a value of type T associated with key (by shared pointer).
Definition NavState.hpp:138
static void print_stacktrace()
Prints the current C++ stack trace to std::cerr.
Definition NavState.hpp:426
static void register_basic_printers()
Registers default printers for common scalar types.
Definition NavState.hpp:442
void set(const std::string &key, const T &value)
Stores a value of type T associated with key (by copy).
Definition NavState.hpp:104
bool has_group(const std::string &key) const
Checks whether key exists in the state.
Definition NavState.hpp:368
static void register_printer(std::function< std::string(const T &)> printer)
Registers a pretty-printer for type T used by debug_string().
Definition NavState.hpp:384
std::vector< std::shared_ptr< T > > get_by_type() const
Retrieves all stored values of type T, regardless of their key.
Definition NavState.hpp:281
NavState()
Constructs an empty NavState and registers basic type printers.
Definition NavState.hpp:86
virtual ~NavState()=default
Destructor.
bool has(const std::string &key) const
Checks whether key exists in the state.
Definition NavState.hpp:360
std::string debug_string() const
Generates a human-readable dump of all stored keys and values.
Definition NavState.hpp:398
const std::vector< std::shared_ptr< T > > get_group(const std::string &group_key) const
Retrieves a vector of values (pointers) from a group by the group key.
Definition NavState.hpp:246
Definition CircularBuffer.hpp:23
std::string stacktrace(std::size_t skip=1, std::size_t max_frames=64)
Captures a C/C++ stack trace as a string.
Definition NavState.hpp:59
std::string demangle(const char *name)
Demangles a C++ RTTI type name if possible.
Definition NavState.hpp:46