Easy Navigation
Loading...
Searching...
No Matches
NavState.hpp
Go to the documentation of this file.
1// Copyright 2025 Intelligent Robotics Lab
2//
3// This file is part of the project Easy Navigation (EasyNav in short)
4// licensed under the GNU General Public License v3.0.
5// See <http://www.gnu.org/licenses/> for details.
6//
7// Easy Navigation program is free software: you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program. If not, see <http://www.gnu.org/licenses/>.
19
26
27#ifndef EASYNAV__TYPES__NAVSTATE_HPP_
28#define EASYNAV__TYPES__NAVSTATE_HPP_
29
30#include <string>
31#include <unordered_map>
32#include <memory>
33#include <mutex>
34#include <stdexcept>
35#include <sstream>
36#include <type_traits>
37#include <iostream>
38#include <functional>
39#include <execinfo.h>
40#include <typeinfo>
41#include <cxxabi.h>
42#include <execinfo.h>
43
44namespace easynav
45{
46
50inline std::string demangle(const char * name)
51{
52 int status = 0;
53 char * p = abi::__cxa_demangle(name, nullptr, nullptr, &status);
54 std::string out = (status == 0 && p) ? p : name;
55 std::free(p);
56 return out;
57}
58
63inline std::string stacktrace(std::size_t skip = 1, std::size_t max_frames = 64)
64{
65 void * buf[128];
66 const int n = backtrace(buf, static_cast<int>(std::min<std::size_t>(max_frames, 128)));
67 char ** syms = backtrace_symbols(buf, n);
68 std::ostringstream oss;
69 for (int i = static_cast<int>(skip); i < n; ++i) {
70 oss << "#" << (i - static_cast<int>(skip)) << " " << syms[i] << "\n";
71 }
72 std::free(syms);
73 return oss.str();
74}
75
87{
88public:
94
96 virtual ~NavState() = default;
97
107 template<typename T>
108 void set(const std::string & key, const T & value)
109 {
110 std::lock_guard<std::mutex> lock(mutex_);
111 auto it = values_.find(key);
112
113 if (it == values_.end()) {
114 values_[key] = std::make_shared<T>(value);
115 types_[key] = typeid(T).hash_code();
116 } else {
117 if (types_[key] != typeid(T).hash_code()) {
118 std::ostringstream oss;
119 oss << "Type mismatch in set(\"" << key << "\")\n"
120 << " expected(hash): " << types_[key] << "\n"
121 << " provided : " << demangle(typeid(T).name()) << "\n"
122 << "Backtrace:\n" << stacktrace(1);
123 throw std::runtime_error(oss.str());
124 }
125
126 auto ptr = std::static_pointer_cast<T>(it->second);
127 *ptr = value;
128 }
129 }
130
140 template<typename T>
141 void set(const std::string & key, const std::shared_ptr<T> value_ptr)
142 {
143 std::lock_guard<std::mutex> lock(mutex_);
144 auto it = values_.find(key);
145
146 if (it == values_.end()) {
147 values_[key] = std::shared_ptr<T>(value_ptr);
148 types_[key] = typeid(T).hash_code();
149 } else {
150 if (types_[key] != typeid(T).hash_code()) {
151 std::ostringstream oss;
152 oss << "Type mismatch in set(\"" << key << "\")\n"
153 << " expected(hash): " << types_[key] << "\n"
154 << " provided : " << demangle(typeid(T).name()) << "\n"
155 << "Backtrace:\n" << stacktrace(1);
156 throw std::runtime_error(oss.str());
157 }
158
159 auto ptr = std::static_pointer_cast<T>(it->second);
160 ptr = std::shared_ptr<T>(value_ptr);
161 }
162 }
163
172 template<typename T>
173 const T & get(const std::string & key) const
174 {
175 std::lock_guard<std::mutex> lock(mutex_);
176 auto it = values_.find(key);
177
178 if (it == values_.end()) {
179 throw std::runtime_error("Key not found in get: " + key);
180 }
181
182 if (types_.at(key) != typeid(T).hash_code()) {
183 throw std::runtime_error("Type mismatch in get for key: " + key);
184 }
185
186 auto ptr = std::static_pointer_cast<T>(it->second);
187 return *ptr;
188 }
189
198 template<typename T>
199 const std::shared_ptr<T> get_ptr(const std::string & key) const
200 {
201 std::lock_guard<std::mutex> lock(mutex_);
202 auto it = values_.find(key);
203
204 if (it == values_.end()) {
205 throw std::runtime_error("Key not found in get: " + key);
206 }
207
208 if (types_.at(key) != typeid(T).hash_code()) {
209 throw std::runtime_error("Type mismatch in get for key: " + key);
210 }
211
212 return std::static_pointer_cast<T>(it->second);
213 }
214
218 bool has(const std::string & key) const
219 {
220 return values_.find(key) != values_.end();
221 }
222
227 using AnyPrinter = std::function<std::string(std::shared_ptr<void>)>;
228
233 template<typename T>
234 static void register_printer(std::function<std::string(const T &)> printer)
235 {
236 auto wrapper = [printer](std::shared_ptr<void> base_ptr) -> std::string {
237 auto typed_ptr = std::static_pointer_cast<T>(base_ptr);
238 return printer(*typed_ptr);
239 };
240 type_printers_[typeid(T).hash_code()] = wrapper;
241 }
242
248 std::string debug_string() const
249 {
250 std::stringstream ss;
251 for (const auto & kv : values_) {
252 ss << kv.first << " = ";
253 auto ptr = kv.second;
254 if (ptr) {
255 auto type_it = types_.find(kv.first);
256 if (type_it != types_.end()) {
257 auto printer_it = type_printers_.find(type_it->second);
258 if (printer_it != type_printers_.end()) {
259 ss << "[" << ptr.get() << "] : " << printer_it->second(ptr);
260 } else {
261 ss << "[" << ptr.get() << "] : " << type_it->second << "]";
262 }
263 } else {
264 ss << "[" << ptr.get() << "] : unknown]";
265 }
266 } else {
267 ss << "[null]";
268 }
269 ss << std::endl;
270 }
271 return ss.str();
272 }
273
276 static void print_stacktrace()
277 {
278 void *array[50];
279 int size = backtrace(array, 50);
280 char **strings = backtrace_symbols(array, size);
281 std::cerr << "\nStack trace:\n";
282 for (int i = 0; i < size; ++i) {
283 std::cerr << strings[i] << std::endl;
284 }
285 std::cerr << std::endl;
286 free(strings);
287 }
288
293 {
294 register_printer<int>([](const int & v) {return std::to_string(v);});
295 register_printer<float>([](const float & v) {return std::to_string(v);});
296 register_printer<double>([](const double & v) {return std::to_string(v);});
297 register_printer<std::string>([](const std::string & v) {return v;});
298 register_printer<bool>([](const bool & v) {return v ? "true" : "false";});
299 register_printer<char>([](const char & v) {return std::string(1, v);});
300 }
301
302private:
303 mutable std::mutex mutex_;
304
306 mutable std::unordered_map<std::string, std::shared_ptr<void>> values_;
307
309 mutable std::unordered_map<std::string, size_t> types_;
310
312 static inline std::unordered_map<size_t, AnyPrinter> type_printers_;
313};
314
315} // namespace easynav
316
317#endif // EASYNAV__TYPES__NAVSTATE_HPP_
const T & get(const std::string &key) const
Retrieves a const reference to the stored value of type T for key.
Definition NavState.hpp:173
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:199
std::function< std::string(std::shared_ptr< void >)> AnyPrinter
Type alias for a generic printer functor used by debug_string().
Definition NavState.hpp:227
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:141
static void print_stacktrace()
Prints the current C++ stack trace to std::cerr.
Definition NavState.hpp:276
static void register_basic_printers()
Registers default printers for common scalar types.
Definition NavState.hpp:292
void set(const std::string &key, const T &value)
Stores a value of type T associated with key (by copy).
Definition NavState.hpp:108
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:234
NavState()
Constructs an empty NavState and registers basic type printers.
Definition NavState.hpp:90
virtual ~NavState()=default
Destructor.
bool has(const std::string &key) const
Checks whether key exists in the state.
Definition NavState.hpp:218
std::string debug_string() const
Generates a human-readable dump of all stored keys and values.
Definition NavState.hpp:248
Definition CircularBuffer.hpp:28
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:63
std::string demangle(const char *name)
Demangles a C++ RTTI type name if possible.
Definition NavState.hpp:50