22#ifndef EASYNAV_COMMON__TYPES__NAVSTATE_HPP_
23#define EASYNAV_COMMON__TYPES__NAVSTATE_HPP_
30#include <unordered_map>
31#include <unordered_set>
53 char * p = abi::__cxa_demangle(name,
nullptr,
nullptr, &status);
54 std::string out = (status == 0 && p) ? p : name;
63inline std::string
stacktrace(std::size_t skip = 1, std::size_t max_frames = 64)
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";
109 void set(
const std::string & key,
const T & value)
111 std::lock_guard<std::mutex> lock(state_mutex_);
112 auto it = values_.find(key);
114 if (it == values_.end()) {
115 values_[key] = std::make_shared<T>(value);
116 types_[key] =
typeid(T).hash_code();
117 type_names_[key] =
demangle(
typeid(T).name());
119 if (types_[key] !=
typeid(T).hash_code()) {
120 std::ostringstream oss;
121 oss <<
"Type mismatch in set(\"" << key <<
"\")\n"
122 <<
" stored type : " << type_names_[key] <<
"\n"
123 <<
" provided type: " <<
demangle(
typeid(T).name()) <<
"\n"
125 throw std::runtime_error(oss.str());
128 auto ptr = std::static_pointer_cast<T>(it->second);
144 void set(
const std::string & key,
const std::shared_ptr<T> value_ptr)
146 std::lock_guard<std::mutex> lock(state_mutex_);
147 auto it = values_.find(key);
149 if (it == values_.end()) {
150 values_[key] = std::shared_ptr<T>(value_ptr);
151 types_[key] =
typeid(T).hash_code();
152 type_names_[key] =
demangle(
typeid(T).name());
154 if (types_[key] !=
typeid(T).hash_code()) {
155 std::ostringstream oss;
156 oss <<
"Type mismatch in set(\"" << key <<
"\")\n"
157 <<
" stored type : " << type_names_[key] <<
"\n"
158 <<
" provided type: " <<
demangle(
typeid(T).name()) <<
"\n"
160 throw std::runtime_error(oss.str());
163 it->second = value_ptr;
177 void set_group(
const std::string & key,
const std::vector<std::string> & group_keys)
179 std::lock_guard<std::mutex> lock(group_mutex_);
180 groups_[key] = group_keys;
195 const T &
get(
const std::string & key)
const
197 std::lock_guard<std::mutex> lock(state_mutex_);
198 auto it = values_.find(key);
200 if (it == values_.end()) {
201 throw std::runtime_error(
"Key not found in get: " + key);
204 if (types_.at(key) !=
typeid(T).hash_code()) {
205 std::ostringstream oss;
206 oss <<
"Type mismatch in get(\"" << key <<
"\")\n"
207 <<
" stored type : " << type_names_.at(key) <<
"\n"
208 <<
" requested type: " <<
demangle(
typeid(T).name());
209 throw std::runtime_error(oss.str());
212 auto ptr = std::static_pointer_cast<T>(it->second);
237 std::lock_guard<std::mutex> lock(state_mutex_);
238 auto it = values_.find(key);
240 if (it == values_.end()) {
241 throw std::runtime_error(
"Key not found in get_safe: " + key);
244 if (types_.at(key) !=
typeid(T).hash_code()) {
245 std::ostringstream oss;
246 oss <<
"Type mismatch in get_safe(\"" << key <<
"\")\n"
247 <<
" stored type : " << type_names_.at(key) <<
"\n"
248 <<
" requested type: " <<
demangle(
typeid(T).name());
249 throw std::runtime_error(oss.str());
252 auto ptr = std::static_pointer_cast<T>(it->second);
265 const std::shared_ptr<T>
get_ptr(
const std::string & key)
const
267 std::lock_guard<std::mutex> lock(state_mutex_);
268 auto it = values_.find(key);
270 if (it == values_.end()) {
271 throw std::runtime_error(
"Key not found in get: " + key);
274 if (types_.at(key) !=
typeid(T).hash_code()) {
275 std::ostringstream oss;
276 oss <<
"Type mismatch in get_ptr(\"" << key <<
"\")\n"
277 <<
" stored type : " << type_names_.at(key) <<
"\n"
278 <<
" requested type: " <<
demangle(
typeid(T).name());
279 throw std::runtime_error(oss.str());
282 return std::static_pointer_cast<T>(it->second);
294 const std::vector<std::shared_ptr<T>>
get_group(
const std::string & group_key)
const
296 std::lock_guard<std::mutex> lock(group_mutex_);
297 auto group_it = groups_.find(group_key);
299 if (group_it == groups_.end()) {
300 throw std::runtime_error(
"Group key not found in get_group: " + group_key);
303 const auto group_keys = group_it->second;
305 std::vector<std::shared_ptr<T>> out;
306 out.reserve(group_keys.size());
308 for (
const auto & group_key : group_keys) {
311 }
catch (
const std::runtime_error & e) {
312 std::cerr <<
"Error retrieving key '" << group_key <<
"' from group '" << group_key <<
313 "': " << e.what() << std::endl;
331 std::lock_guard<std::mutex> lock(state_mutex_);
332 std::vector<std::shared_ptr<T>> out;
333 const size_t target_hash =
typeid(T).hash_code();
334 for (
const auto & kv : values_) {
335 if (types_.at(kv.first) == target_hash) {
336 out.push_back(std::static_pointer_cast<T>(kv.second));
353 std::vector<std::shared_ptr<T>>
get_to_vector(
const std::string & key)
const
355 std::vector<std::shared_ptr<T>> out;
356 std::lock_guard<std::mutex> lock(state_mutex_);
357 auto it = values_.find(key);
358 if (it == values_.end()) {
361 if (types_.at(key) !=
typeid(T).hash_code()) {
364 out.push_back(std::static_pointer_cast<T>(it->second));
381 std::unordered_set<std::string> grouped_keys;
383 std::lock_guard<std::mutex> glock(group_mutex_);
384 for (
const auto & g : groups_) {
385 for (
const auto & k : g.second) {
386 grouped_keys.insert(k);
392 std::lock_guard<std::mutex> slock(state_mutex_);
393 std::vector<std::shared_ptr<T>> out;
394 const size_t target_hash =
typeid(T).hash_code();
395 for (
const auto & kv : values_) {
396 if (types_.at(kv.first) == target_hash &&
397 grouped_keys.find(kv.first) == grouped_keys.end())
399 out.push_back(std::static_pointer_cast<T>(kv.second));
408 bool has(
const std::string & key)
const
410 return values_.find(key) != values_.end();
418 return groups_.find(key) != groups_.end();
423 void add_to_group(
const std::string & group_key,
const std::string & key)
425 std::lock_guard<std::mutex> lock(group_mutex_);
426 auto & members = groups_[group_key];
427 if (std::find(members.begin(), members.end(), key) != members.end()) {
430 members.push_back(key);
439 std::lock_guard<std::mutex> lock(group_mutex_);
440 auto it = groups_.find(group_key);
441 if (it == groups_.end()) {
451 using AnyPrinter = std::function<std::string(std::shared_ptr<void>)>;
460 auto wrapper = [printer](std::shared_ptr<void> base_ptr) -> std::string {
461 auto typed_ptr = std::static_pointer_cast<T>(base_ptr);
462 return printer(*typed_ptr);
464 auto & registry = printer_registry();
465 std::lock_guard<std::mutex> lock(registry.mutex);
466 registry.printers[
typeid(T).hash_code()] = wrapper;
480 std::shared_ptr<void> ptr;
481 std::optional<size_t> type;
483 std::vector<Entry> entries;
485 std::lock_guard<std::mutex> lock(state_mutex_);
486 entries.reserve(values_.size());
487 for (
const auto & [key, ptr] : values_) {
488 const auto type_it = types_.find(key);
490 {key, ptr, type_it != types_.end() ? std::optional(type_it->second) : std::nullopt});
495 entries.begin(), entries.end(),
496 [](
const Entry & a,
const Entry & b) {return a.key < b.key;});
498 std::stringstream ss;
499 for (
const auto & [key, ptr, type] : entries) {
505 auto & registry = printer_registry();
506 std::lock_guard<std::mutex> lock(registry.mutex);
507 const auto printer_it = registry.printers.find(*type);
508 if (printer_it != registry.printers.end()) {
509 printer = printer_it->second;
513 ss <<
"[" << ptr.get() <<
"] : " << printer(ptr);
515 ss <<
"[" << ptr.get() <<
"] : " << *type <<
"]";
518 ss <<
"[" << ptr.get() <<
"] : unknown]";
533 int size = backtrace(array, 50);
534 char ** strings = backtrace_symbols(array, size);
535 std::cerr <<
"\nStack trace:\n";
536 for (
int i = 0; i < size; ++i) {
537 std::cerr << strings[i] << std::endl;
539 std::cerr << std::endl;
555 [](
const std::vector<std::string> & v) {
556 std::ostringstream oss;
557 oss <<
" " << v.size() <<
" [";
558 for (std::size_t i = 0; i < v.size(); ++i) {
559 if (i > 0) {oss <<
", ";}
568 mutable std::mutex state_mutex_;
569 mutable std::mutex group_mutex_;
572 mutable std::unordered_map<std::string, std::shared_ptr<void>> values_;
576 mutable std::unordered_map<std::string, std::vector<std::string>> groups_;
579 mutable std::unordered_map<std::string, size_t> types_;
582 mutable std::unordered_map<std::string, std::string> type_names_;
585 struct PrinterRegistry
588 std::unordered_map<size_t, AnyPrinter> printers;
597 static PrinterRegistry & printer_registry()
599 static PrinterRegistry *
const registry =
new PrinterRegistry();
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:378
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:177
const T & get(const std::string &key) const
Retrieves a const reference to the stored value of type T for key.
Definition NavState.hpp:195
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:353
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:265
std::function< std::string(std::shared_ptr< void >)> AnyPrinter
Type alias for a generic printer functor used by debug_string().
Definition NavState.hpp:451
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:144
static void print_stacktrace()
Prints the current C++ stack trace to std::cerr.
Definition NavState.hpp:530
static void register_basic_printers()
Registers default printers for common scalar types.
Definition NavState.hpp:546
void set(const std::string &key, const T &value)
Stores a value of type T associated with key (by copy).
Definition NavState.hpp:109
bool has_group(const std::string &key) const
Checks whether key exists in the state.
Definition NavState.hpp:416
std::vector< std::string > get_group_keys(const std::string &group_key) const
Member keys of group_key, as recorded by set_group() (without dereferencing them).
Definition NavState.hpp:437
void add_to_group(const std::string &group_key, const std::string &key)
Adds key to the group group_key (created if missing), atomically: safe when several threads add membe...
Definition NavState.hpp:423
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:458
std::vector< std::shared_ptr< T > > get_by_type() const
Retrieves all stored values of type T, regardless of their key.
Definition NavState.hpp:329
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:408
std::string debug_string() const
Generates a human-readable dump of all stored keys and values.
Definition NavState.hpp:474
T get_safe(const std::string &key) const
Retrieves a snapshot copy of the stored value of type T for key.
Definition NavState.hpp:235
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:294
Definition CircularBuffer.hpp:24
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