metadata 0.2.0
Modern C++23 header-only metadata container (JSON-like)
Loading...
Searching...
No Matches
object.hpp
1#pragma once
2
3#include <md/error.hpp>
4#include <md/value.hpp>
5
6#include <cstddef>
7#include <functional>
8#include <initializer_list>
9#include <memory>
10#include <string>
11#include <string_view>
12#include <type_traits>
13#include <unordered_map>
14#include <utility>
15#include <variant>
16
17namespace md {
18
21class Object {
22public:
24 using map_type =
25 std::unordered_map<std::string, Value, detail::TransparentStringHash, std::equal_to<>>;
26
28 using key_type = map_type::key_type;
30 using mapped_type = map_type::mapped_type;
32 using value_type = map_type::value_type;
34 using size_type = map_type::size_type;
36 using difference_type = map_type::difference_type;
38 using hasher = map_type::hasher;
40 using key_equal = map_type::key_equal;
42 using reference = map_type::reference;
44 using const_reference = map_type::const_reference;
46 using pointer = map_type::pointer;
48 using const_pointer = map_type::const_pointer;
50 using iterator = map_type::iterator;
52 using const_iterator = map_type::const_iterator;
53
55 Object() = default;
56
58 Object(const std::initializer_list<value_type> il) : map_(il) {}
59
61 template <class InputIt>
62 Object(InputIt first, InputIt last) : map_(first, last) {}
63
64 // --- map-like surface ---------------------------------------------
65
67 iterator begin() noexcept {
68 return map_.begin();
69 }
71 const_iterator begin() const noexcept {
72 return map_.begin();
73 }
75 const_iterator cbegin() const noexcept {
76 return map_.cbegin();
77 }
79 iterator end() noexcept {
80 return map_.end();
81 }
83 const_iterator end() const noexcept {
84 return map_.end();
85 }
87 const_iterator cend() const noexcept {
88 return map_.cend();
89 }
90
92 [[nodiscard]] bool empty() const noexcept {
93 return map_.empty();
94 }
96 [[nodiscard]] size_type size() const noexcept {
97 return map_.size();
98 }
99
101 void clear() noexcept {
102 map_.clear();
103 }
105 void reserve(const size_type n) {
106 map_.reserve(n);
107 }
108
110 Value& operator[](const std::string_view key) {
111 if (const auto it = map_.find(key); it != map_.end()) {
112 return it->second;
113 }
114 return map_.emplace(std::string(key), Value{}).first->second;
115 }
117 Value& operator[](const std::string& key) {
118 return map_[key];
119 }
121 Value& operator[](std::string&& key) {
122 return map_[std::move(key)];
123 }
125 Value& operator[](const char* key) {
126 return (*this)[std::string_view{key}];
127 }
128
130 Value& at(const std::string_view key) {
131 if (const auto it = map_.find(key); it != map_.end()) {
132 return it->second;
133 }
134 throw std::out_of_range("Object::at: key not found");
135 }
137 const Value& at(const std::string_view key) const {
138 if (const auto it = map_.find(key); it != map_.end()) {
139 return it->second;
140 }
141 throw std::out_of_range("Object::at: key not found");
142 }
143
145 iterator find(const std::string_view key) {
146 return map_.find(key);
147 }
149 const_iterator find(const std::string_view key) const {
150 return map_.find(key);
151 }
152
154 [[nodiscard]] size_type count(const std::string_view key) const {
155 return map_.count(key);
156 }
157
159 template <class K, class V>
160 std::pair<iterator, bool> insert_or_assign(K&& k, V&& v) {
161 return map_.insert_or_assign(std::forward<K>(k), std::forward<V>(v));
162 }
163
165 template <class K, class V>
166 std::pair<iterator, bool> emplace(K&& k, V&& v) {
167 return map_.emplace(std::forward<K>(k), std::forward<V>(v));
168 }
169
171 std::pair<iterator, bool> insert(const value_type& v) {
172 return map_.insert(v);
173 }
175 std::pair<iterator, bool> insert(value_type&& v) {
176 return map_.insert(std::move(v));
177 }
178
180 size_type erase(const std::string_view key) {
181 if (const auto it = map_.find(key); it != map_.end()) {
182 map_.erase(it);
183 return 1;
184 }
185 return 0;
186 }
189 return map_.erase(pos);
190 }
191
192 // --- metadata helpers (methods) -----------------------------------
193
195 [[nodiscard]] bool contains(const std::string_view key) const {
196 return map_.contains(key);
197 }
198
200 [[nodiscard]] Value* find_ptr(const std::string_view key) {
201 if (const auto it = map_.find(key); it != map_.end()) {
202 return &it->second;
203 }
204 return nullptr;
205 }
207 [[nodiscard]] const Value* find_ptr(const std::string_view key) const {
208 if (const auto it = map_.find(key); it != map_.end()) {
209 return &it->second;
210 }
211 return nullptr;
212 }
213
215 Value& require(const std::string_view key) {
216 if (auto* p = find_ptr(key); p != nullptr) {
217 return *p;
218 }
219 throw missing_key_error("Object::require: missing key '" + std::string(key) + "'");
220 }
222 const Value& require(const std::string_view key) const {
223 if (const auto* p = find_ptr(key); p != nullptr) {
224 return *p;
225 }
226 throw missing_key_error("Object::require: missing key '" + std::string(key) + "'");
227 }
228
230 std::string& require_string(const std::string_view key) {
231 Value& v = require(key);
232 if (auto* p = v.as_string_if(); p != nullptr) {
233 return *p;
234 }
235 throw type_error("Object::require_string: '" + std::string(key) + "' is not a string");
236 }
238 const std::string& require_string(const std::string_view key) const {
239 const Value& v = require(key);
240 if (const auto* p = v.as_string_if(); p != nullptr) {
241 return *p;
242 }
243 throw type_error("Object::require_string: '" + std::string(key) + "' is not a string");
244 }
245
247 Array& require_array(const std::string_view key) {
248 Value& v = require(key);
249 if (auto* p = v.as_array_if(); p != nullptr) {
250 return *p;
251 }
252 throw type_error("Object::require_array: '" + std::string(key) + "' is not an array");
253 }
255 const Array& require_array(const std::string_view key) const {
256 const Value& v = require(key);
257 if (const auto* p = v.as_array_if(); p != nullptr) {
258 return *p;
259 }
260 throw type_error("Object::require_array: '" + std::string(key) + "' is not an array");
261 }
262
264 Object& require_object(const std::string_view key) {
265 Value& v = require(key);
266 if (auto* p = v.as_object_if(); p != nullptr) {
267 return *p;
268 }
269 throw type_error("Object::require_object: '" + std::string(key) + "' is not an object");
270 }
272 const Object& require_object(const std::string_view key) const {
273 const Value& v = require(key);
274 if (const auto* p = v.as_object_if(); p != nullptr) {
275 return *p;
276 }
277 throw type_error("Object::require_object: '" + std::string(key) + "' is not an object");
278 }
279
281 [[nodiscard]] const std::string* get_string_if(const std::string_view key) const {
282 if (const auto* v = find_ptr(key); v != nullptr) {
283 return v->as_string_if();
284 }
285 return nullptr;
286 }
288 [[nodiscard]] const Array* get_array_if(const std::string_view key) const {
289 if (const auto* v = find_ptr(key); v != nullptr) {
290 return v->as_array_if();
291 }
292 return nullptr;
293 }
295 [[nodiscard]] const Object* get_object_if(const std::string_view key) const {
296 if (const auto* v = find_ptr(key); v != nullptr) {
297 return v->as_object_if();
298 }
299 return nullptr;
300 }
301
302 // --- path helpers (definitions live in path.hpp) ------------------
303
305 [[nodiscard]] Value* find_path(std::string_view path);
307 [[nodiscard]] const Value* find_path(std::string_view path) const;
309 [[nodiscard]] const Value& require_path(std::string_view path) const;
311 [[nodiscard]] Value& require_path(std::string_view path);
313 [[nodiscard]] bool contains_path(std::string_view path) const;
314
315 // --- deep merge: source wins on non-object conflicts; arrays replaced ---
316
319 void merge(const Object& source);
320
322 [[nodiscard]] map_type& raw() noexcept {
323 return map_;
324 }
326 [[nodiscard]] const map_type& raw() const noexcept {
327 return map_;
328 }
329
331 friend bool operator==(const Object& a, const Object& b) noexcept {
332 return a.map_ == b.map_;
333 }
335 friend bool operator!=(const Object& a, const Object& b) noexcept {
336 return !(a == b);
337 }
338
339private:
340 map_type map_;
341};
342
344using Metadata = Object;
345
346// =====================================================================
347// Value out-of-line definitions — defined here so that Object is
348// complete by the time we touch ~unique_ptr<Object>, *Object, etc.
349// =====================================================================
350
351inline Value::Value() noexcept : v_(nullptr) {}
352inline Value::Value(std::nullptr_t) noexcept : v_(nullptr) {}
353
354template <class B>
355 requires std::same_as<B, bool>
356inline Value::Value(B b) noexcept : v_(b) {}
357
358template <detail::SignedIntLike T>
359inline Value::Value(T x) noexcept : v_(static_cast<std::int64_t>(x)) {}
360
361template <detail::UnsignedIntLike T>
362inline Value::Value(T x) noexcept : v_(static_cast<std::uint64_t>(x)) {}
363
364template <detail::FloatLike T>
365inline Value::Value(T x) noexcept {
366 if constexpr (std::is_same_v<std::remove_cv_t<T>, float>) {
367 v_ = x;
368 } else {
369 v_ = static_cast<double>(x);
370 }
371}
372
373inline Value::Value(std::string s) : v_(std::move(s)) {}
374inline Value::Value(const std::string_view s) : v_(std::string(s)) {}
375inline Value::Value(const char* s) : v_(std::string(s)) {}
376
377inline Value::Value(Array a) : v_(std::move(a)) {}
378
379inline Value::Value(Object o) : v_(std::make_unique<Object>(std::move(o))) {}
380
381inline Value::Value(const std::initializer_list<std::pair<const std::string, Value>> il)
382 : v_(std::make_unique<Object>(Object(il))) {}
383
384inline Value::Value(Value&& other) noexcept : v_(std::move(other.v_)) {}
385
386inline Value& Value::operator=(Value&& other) noexcept {
387 v_ = std::move(other.v_);
388 return *this;
389}
390
391inline Value::Value(const Value& other) {
392 std::visit(
393 [&](const auto& x) {
394 using T = std::decay_t<decltype(x)>;
395 if constexpr (std::is_same_v<T, std::unique_ptr<Object>>) {
396 v_ = std::make_unique<Object>(*x);
397 } else {
398 v_ = x;
399 }
400 },
401 other.v_);
402}
403
404inline Value& Value::operator=(const Value& other) {
405 if (this == &other) {
406 return *this;
407 }
408 Value tmp(other);
409 v_ = std::move(tmp.v_);
410 return *this;
411}
412
413inline Value& Value::operator=(const std::initializer_list<Value> il) {
414 v_ = Array(il);
415 return *this;
416}
417
418inline Value&
419Value::operator=(const std::initializer_list<std::pair<const std::string, Value>> il) {
420 v_ = std::make_unique<Object>(Object(il));
421 return *this;
422}
423
424// NOLINTNEXTLINE(readability-redundant-inline-specifier)
425inline Value::~Value() = default;
426
427inline bool Value::is_null() const noexcept {
428 return std::holds_alternative<std::nullptr_t>(v_);
429}
430inline bool Value::is_bool() const noexcept {
431 return std::holds_alternative<bool>(v_);
432}
433inline bool Value::is_int() const noexcept {
434 return std::holds_alternative<std::int64_t>(v_);
435}
436inline bool Value::is_uint() const noexcept {
437 return std::holds_alternative<std::uint64_t>(v_);
438}
439inline bool Value::is_float() const noexcept {
440 return std::holds_alternative<float>(v_);
441}
442inline bool Value::is_double() const noexcept {
443 return std::holds_alternative<double>(v_);
444}
445inline bool Value::is_number() const noexcept {
446 return is_int() || is_uint() || is_float() || is_double();
447}
448inline bool Value::is_string() const noexcept {
449 return std::holds_alternative<std::string>(v_);
450}
451inline bool Value::is_array() const noexcept {
452 return std::holds_alternative<Array>(v_);
453}
454inline bool Value::is_object() const noexcept {
455 return std::holds_alternative<std::unique_ptr<Object>>(v_);
456}
457
458inline bool Value::as_bool() const {
459 return std::get<bool>(v_);
460}
461inline std::int64_t Value::as_int() const {
462 return std::get<std::int64_t>(v_);
463}
464inline std::uint64_t Value::as_uint() const {
465 return std::get<std::uint64_t>(v_);
466}
467
468inline float Value::as_float() const {
469 return std::get<float>(v_);
470}
471
472inline double Value::as_double() const {
473 if (const auto* p = std::get_if<double>(&v_); p != nullptr) {
474 return *p;
475 }
476 if (const auto* p = std::get_if<float>(&v_); p != nullptr) {
477 return static_cast<double>(*p);
478 }
479 if (const auto* p = std::get_if<std::int64_t>(&v_); p != nullptr) {
480 return static_cast<double>(*p);
481 }
482 if (const auto* p = std::get_if<std::uint64_t>(&v_); p != nullptr) {
483 return static_cast<double>(*p);
484 }
485 throw type_error("Value::as_double: value is not a number");
486}
487
488inline std::string& Value::as_string() {
489 return std::get<std::string>(v_);
490}
491inline const std::string& Value::as_string() const {
492 return std::get<std::string>(v_);
493}
494inline Array& Value::as_array() {
495 return std::get<Array>(v_);
496}
497inline const Array& Value::as_array() const {
498 return std::get<Array>(v_);
499}
500
502 return *std::get<std::unique_ptr<Object>>(v_);
503}
504inline const Object& Value::as_object() const {
505 return *std::get<std::unique_ptr<Object>>(v_);
506}
507
508inline bool* Value::as_bool_if() noexcept {
509 return std::get_if<bool>(&v_);
510}
511inline const bool* Value::as_bool_if() const noexcept {
512 return std::get_if<bool>(&v_);
513}
514inline std::int64_t* Value::as_int_if() noexcept {
515 return std::get_if<std::int64_t>(&v_);
516}
517inline const std::int64_t* Value::as_int_if() const noexcept {
518 return std::get_if<std::int64_t>(&v_);
519}
520inline std::uint64_t* Value::as_uint_if() noexcept {
521 return std::get_if<std::uint64_t>(&v_);
522}
523inline const std::uint64_t* Value::as_uint_if() const noexcept {
524 return std::get_if<std::uint64_t>(&v_);
525}
526inline float* Value::as_float_if() noexcept {
527 return std::get_if<float>(&v_);
528}
529inline const float* Value::as_float_if() const noexcept {
530 return std::get_if<float>(&v_);
531}
532inline double* Value::as_double_if() noexcept {
533 return std::get_if<double>(&v_);
534}
535inline const double* Value::as_double_if() const noexcept {
536 return std::get_if<double>(&v_);
537}
538
539inline std::string* Value::as_string_if() noexcept {
540 return std::get_if<std::string>(&v_);
541}
542inline const std::string* Value::as_string_if() const noexcept {
543 return std::get_if<std::string>(&v_);
544}
545inline Array* Value::as_array_if() noexcept {
546 return std::get_if<Array>(&v_);
547}
548inline const Array* Value::as_array_if() const noexcept {
549 return std::get_if<Array>(&v_);
550}
551
552inline Object* Value::as_object_if() noexcept {
553 if (const auto* p = std::get_if<std::unique_ptr<Object>>(&v_); p != nullptr) {
554 return p->get();
555 }
556 return nullptr;
557}
558inline const Object* Value::as_object_if() const noexcept {
559 if (const auto* p = std::get_if<std::unique_ptr<Object>>(&v_); p != nullptr) {
560 return p->get();
561 }
562 return nullptr;
563}
564
565inline bool operator==(const Value& a, const Value& b) noexcept {
566 if (a.v_.index() != b.v_.index()) {
567 return false;
568 }
569 return std::visit(
570 [&](const auto& x) -> bool {
571 using T = std::decay_t<decltype(x)>;
572 if constexpr (std::is_same_v<T, std::unique_ptr<Object>>) {
573 const auto& y = std::get<std::unique_ptr<Object>>(b.v_);
574 return *x == *y;
575 } else {
576 const auto& y = std::get<T>(b.v_);
577 return x == y;
578 }
579 },
580 a.v_);
581}
582
583inline bool operator!=(const Value& a, const Value& b) noexcept {
584 return !(a == b);
585}
586
587template <class T>
588inline T* Value::get_if() noexcept {
589 return std::get_if<T>(&v_);
590}
591template <class T>
592inline const T* Value::get_if() const noexcept {
593 return std::get_if<T>(&v_);
594}
595
596template <class T>
597inline T Value::value_or(T fallback) const {
598 if (auto* p = std::get_if<T>(&v_)) {
599 return *p;
600 }
601 return fallback;
602}
603
604inline Value::variant_type& Value::raw() noexcept {
605 return v_;
606}
607inline const Value::variant_type& Value::raw() const noexcept {
608 return v_;
609}
610
611inline std::size_t Value::index() const noexcept {
612 return v_.index();
613}
614
615// --- Value factory helpers (out-of-line because string ones touch std::string) ---
616
617inline Value null() noexcept {
618 return Value{};
619}
620inline Value boolean(const bool b) noexcept {
621 return Value{b}; // routes through the constrained <same_as<bool>> template
622}
623inline Value string(std::string s) {
624 return Value{std::move(s)};
625}
626inline Value string(const std::string_view s) {
627 return Value{s};
628}
629inline Value string(const char* s) {
630 return Value{s};
631}
632
633// --- object() factory helpers ---
634
636[[nodiscard]] inline Object object() {
637 return Object{};
638}
640[[nodiscard]] inline Object object(const std::initializer_list<Object::value_type> il) {
641 return Object(il);
642}
643
644// --- Object::merge (defined after Value is complete) ---
645
646inline void Object::merge(const Object& source) {
647 for (const auto& [k, v] : source.map_) {
648 auto it = map_.find(k);
649 if (it == map_.end()) {
650 map_.emplace(k, v);
651 continue;
652 }
653 Object* dst_obj = it->second.as_object_if();
654 if (const Object* src_obj = v.as_object_if(); dst_obj != nullptr && src_obj != nullptr) {
655 dst_obj->merge(*src_obj);
656 } else {
657 it->second = v;
658 }
659 }
660}
661
662} // namespace md
Ordered-by-insertion-time-ish string-keyed map of Values, with transparent string-view lookup and JSO...
Definition object.hpp:21
map_type::reference reference
Reference to a stored value_type.
Definition object.hpp:42
const Value * find_ptr(const std::string_view key) const
Return a pointer to the value for key, or nullptr if absent.
Definition object.hpp:207
iterator begin() noexcept
Iterator to the first element.
Definition object.hpp:67
Value * find_path(std::string_view path)
Find a value by dotted path (e.g. "a.b[0].c"), or nullptr on miss.
Definition path.hpp:162
size_type count(const std::string_view key) const
Return 1 if key is present, otherwise 0.
Definition object.hpp:154
void merge(const Object &source)
Deep-merge source into this object; nested objects recurse, other alternatives are overwritten,...
Definition object.hpp:646
const Object * get_object_if(const std::string_view key) const
Pointer to the nested object at key, or nullptr if absent or wrong type.
Definition object.hpp:295
const Value & require(const std::string_view key) const
Return the value for key; throws missing_key_error if absent.
Definition object.hpp:222
iterator erase(const const_iterator pos)
Erase the entry at pos; returns the next iterator.
Definition object.hpp:188
map_type::key_type key_type
Key type (always std::string).
Definition object.hpp:28
iterator end() noexcept
Iterator past the last element.
Definition object.hpp:79
Value & operator[](std::string &&key)
Access (and default-insert if missing) the value for key.
Definition object.hpp:121
void clear() noexcept
Remove all entries.
Definition object.hpp:101
std::pair< iterator, bool > emplace(K &&k, V &&v)
Construct an entry in place; no effect if k already exists.
Definition object.hpp:166
Object()=default
Construct an empty Object.
map_type::pointer pointer
Pointer to a stored value_type.
Definition object.hpp:46
friend bool operator!=(const Object &a, const Object &b) noexcept
Negation of operator==.
Definition object.hpp:335
const_iterator cend() const noexcept
Const iterator past the last element.
Definition object.hpp:87
const std::string * get_string_if(const std::string_view key) const
Pointer to the string at key, or nullptr if absent or wrong type.
Definition object.hpp:281
const std::string & require_string(const std::string_view key) const
Return the string at key; throws on missing key or type mismatch.
Definition object.hpp:238
const map_type & raw() const noexcept
Access the underlying std::unordered_map.
Definition object.hpp:326
std::unordered_map< std::string, Value, detail::TransparentStringHash, std::equal_to<> > map_type
Underlying associative container type.
Definition object.hpp:25
bool contains_path(std::string_view path) const
True if path resolves to a value in the object.
Definition path.hpp:191
const_iterator begin() const noexcept
Const iterator to the first element.
Definition object.hpp:71
Array & require_array(const std::string_view key)
Return the array at key; throws on missing key or type mismatch.
Definition object.hpp:247
const_iterator cbegin() const noexcept
Const iterator to the first element.
Definition object.hpp:75
Object(InputIt first, InputIt last)
Construct from an iterator range of key/value pairs.
Definition object.hpp:62
const Value & at(const std::string_view key) const
Access the value for key; throws std::out_of_range if missing.
Definition object.hpp:137
map_type::key_equal key_equal
Key-equality functor type.
Definition object.hpp:40
size_type erase(const std::string_view key)
Erase the entry for key; returns 1 if removed, otherwise 0.
Definition object.hpp:180
map_type::difference_type difference_type
Signed difference type for iterators.
Definition object.hpp:36
std::string & require_string(const std::string_view key)
Return the string at key; throws on missing key or type mismatch.
Definition object.hpp:230
iterator find(const std::string_view key)
Find an entry by key, returning end() on miss.
Definition object.hpp:145
map_type::hasher hasher
Hash functor type.
Definition object.hpp:38
Value & require(const std::string_view key)
Return the value for key; throws missing_key_error if absent.
Definition object.hpp:215
Object & require_object(const std::string_view key)
Return the nested object at key; throws on missing key or type mismatch.
Definition object.hpp:264
bool empty() const noexcept
True if the object has no entries.
Definition object.hpp:92
size_type size() const noexcept
Number of entries.
Definition object.hpp:96
const Object & require_object(const std::string_view key) const
Return the nested object at key; throws on missing key or type mismatch.
Definition object.hpp:272
Value & operator[](const std::string_view key)
Access (and default-insert if missing) the value for key.
Definition object.hpp:110
std::pair< iterator, bool > insert(const value_type &v)
Insert an entry; no effect if the key already exists.
Definition object.hpp:171
Value & operator[](const std::string &key)
Access (and default-insert if missing) the value for key.
Definition object.hpp:117
Object(const std::initializer_list< value_type > il)
Construct from a braced list of key/value pairs.
Definition object.hpp:58
std::pair< iterator, bool > insert(value_type &&v)
Insert an entry; no effect if the key already exists.
Definition object.hpp:175
map_type::mapped_type mapped_type
Mapped value type (always Value).
Definition object.hpp:30
map_type::const_iterator const_iterator
Const iterator type.
Definition object.hpp:52
Value & at(const std::string_view key)
Access the value for key; throws std::out_of_range if missing.
Definition object.hpp:130
const Array & require_array(const std::string_view key) const
Return the array at key; throws on missing key or type mismatch.
Definition object.hpp:255
const Array * get_array_if(const std::string_view key) const
Pointer to the array at key, or nullptr if absent or wrong type.
Definition object.hpp:288
map_type::size_type size_type
Unsigned size type.
Definition object.hpp:34
const_iterator end() const noexcept
Const iterator past the last element.
Definition object.hpp:83
map_type::const_pointer const_pointer
Const pointer to a stored value_type.
Definition object.hpp:48
const Value & require_path(std::string_view path) const
Return the value at path; throws on miss or malformed path.
Definition path.hpp:167
void reserve(const size_type n)
Reserve storage for at least n entries.
Definition object.hpp:105
map_type::const_reference const_reference
Const reference to a stored value_type.
Definition object.hpp:44
std::pair< iterator, bool > insert_or_assign(K &&k, V &&v)
Insert or overwrite the entry for k.
Definition object.hpp:160
map_type & raw() noexcept
Access the underlying std::unordered_map.
Definition object.hpp:322
map_type::value_type value_type
std::pair<const key_type, mapped_type>.
Definition object.hpp:32
map_type::iterator iterator
Mutable iterator type.
Definition object.hpp:50
Value * find_ptr(const std::string_view key)
Return a pointer to the value for key, or nullptr if absent.
Definition object.hpp:200
friend bool operator==(const Object &a, const Object &b) noexcept
Deep value-equality compare two Objects.
Definition object.hpp:331
const_iterator find(const std::string_view key) const
Find an entry by key, returning end() on miss.
Definition object.hpp:149
bool contains(const std::string_view key) const
True if key is present in the object.
Definition object.hpp:195
Value & operator[](const char *key)
Access (and default-insert if missing) the value for key.
Definition object.hpp:125
Discriminated union holding one of the JSON-like alternatives (null, bool, signed/unsigned integer,...
Definition value.hpp:67
std::string * as_string_if() noexcept
Return a pointer to the string, or nullptr if not held.
Definition object.hpp:539
bool is_int() const noexcept
True if the value holds a signed integer (std::int64_t).
Definition object.hpp:433
bool is_bool() const noexcept
True if the value holds a bool.
Definition object.hpp:430
bool is_uint() const noexcept
True if the value holds an unsigned integer (std::uint64_t).
Definition object.hpp:436
bool is_object() const noexcept
True if the value holds a nested Object.
Definition object.hpp:454
std::string & as_string()
Access the string alternative; throws on type mismatch.
Definition object.hpp:488
bool is_null() const noexcept
True if the value holds nullptr.
Definition object.hpp:427
std::int64_t * as_int_if() noexcept
Return a pointer to the signed integer, or nullptr if not held.
Definition object.hpp:514
double as_double() const
Return as double, widening from int64/uint64/float as needed.
Definition object.hpp:472
Value & operator=(Value &&other) noexcept
Move-assign from another Value.
Definition object.hpp:386
Value() noexcept
Construct a null-valued Value.
Definition object.hpp:351
bool is_double() const noexcept
True if the value holds a double.
Definition object.hpp:442
std::int64_t as_int() const
Return the signed integer; throws on type mismatch.
Definition object.hpp:461
std::size_t index() const noexcept
Return the zero-based index of the active alternative.
Definition object.hpp:611
float as_float() const
Return the float; strict — throws if the value isn't a float.
Definition object.hpp:468
bool is_float() const noexcept
True if the value holds a float.
Definition object.hpp:439
T value_or(T fallback) const
Return the held T by value, or fallback if a different alternative is active.
Definition object.hpp:597
bool is_array() const noexcept
True if the value holds an Array.
Definition object.hpp:451
std::uint64_t as_uint() const
Return the unsigned integer; throws on type mismatch.
Definition object.hpp:464
Object * as_object_if() noexcept
Return a pointer to the nested object, or nullptr if not held.
Definition object.hpp:552
std::variant< std::nullptr_t, bool, std::int64_t, std::uint64_t, float, double, std::string, Array, std::unique_ptr< Object > > variant_type
Underlying std::variant type that stores the active alternative.
Definition value.hpp:82
T * get_if() noexcept
Return a pointer to the alternative of type T, or nullptr if not held.
Definition object.hpp:588
Array & as_array()
Access the array alternative; throws on type mismatch.
Definition object.hpp:494
std::uint64_t * as_uint_if() noexcept
Return a pointer to the unsigned integer, or nullptr if not held.
Definition object.hpp:520
~Value()
Destructor.
variant_type & raw() noexcept
Return the underlying std::variant for advanced access.
Definition object.hpp:604
bool is_number() const noexcept
True if the value holds any numeric alternative.
Definition object.hpp:445
bool is_string() const noexcept
True if the value holds a std::string.
Definition object.hpp:448
bool as_bool() const
Return the bool; throws std::bad_variant_access on type mismatch.
Definition object.hpp:458
Array * as_array_if() noexcept
Return a pointer to the array, or nullptr if not held.
Definition object.hpp:545
bool * as_bool_if() noexcept
Return a pointer to the bool, or nullptr if the value isn't a bool.
Definition object.hpp:508
double * as_double_if() noexcept
Return a pointer to the double, or nullptr if not held.
Definition object.hpp:532
Object & as_object()
Access the nested object; throws on type mismatch.
Definition object.hpp:501
float * as_float_if() noexcept
Return a pointer to the float, or nullptr if not held.
Definition object.hpp:526
Thrown when a required key or path is not present in an Object.
Definition error.hpp:14
Thrown when a Value holds a different alternative than the caller demanded.
Definition error.hpp:19