Container System 0.1.0
High-performance C++20 type-safe container framework with SIMD-accelerated serialization
Loading...
Searching...
No Matches
kcenon::container::value Class Reference

Enhanced type-safe value with perfect legacy compatibility. More...

#include <value.h>

Collaboration diagram for kcenon::container::value:
Collaboration graph

Public Member Functions

 value ()
 Default constructor - creates null value.
 
 value (std::string_view name)
 Construct with name (null value)
 
template<typename T >
 value (std::string_view name, T &&value)
 Construct with name and typed value.
 
 value (std::string_view name, value_types type, const std::vector< uint8_t > &raw_data)
 Construct from value_types and raw data (legacy compatibility)
 
 value (const value &other)
 Copy constructor (thread-safe)
 
 value (value &&other) noexcept
 Move constructor.
 
valueoperator= (const value &other)
 Copy assignment (thread-safe)
 
valueoperator= (value &&other) noexcept
 Move assignment.
 
std::string_view name () const noexcept
 Get the name of this value (lock-free, immutable)
 
value_types type () const
 Get the value_types enum (NOT variant::index()!)
 
size_t variant_index () const
 Get variant index (for internal use only)
 
bool is_null () const
 Check if null value.
 
template<concepts::ValueVariantType T>
std::optional< T > get () const
 Type-safe getter with optional return.
 
template<concepts::ValueVariantType T>
void set (T &&value)
 Type-safe setter.
 
template<concepts::ValueVisitor Visitor>
auto visit (Visitor &&vis) const
 Apply visitor to contained value (const)
 
template<concepts::ValueVisitor Visitor>
auto visit_mut (Visitor &&vis)
 Apply visitor to contained value (mutable)
 
std::string to_string () const
 Convert to string representation.
 
std::string to_json () const
 Convert to JSON representation.
 
std::vector< uint8_t > serialize () const
 Serialize to binary format (legacy compatible)
 
size_t read_count () const
 Get read/write statistics.
 
size_t write_count () const
 
bool operator== (const value &other) const
 Comparison operators (thread-safe)
 
bool operator!= (const value &other) const
 
bool operator< (const value &other) const
 

Static Public Member Functions

static std::optional< valuedeserialize (const std::vector< uint8_t > &data)
 Deserialize from binary format (legacy compatible)
 

Private Member Functions

void serialize_data (std::vector< uint8_t > &result) const
 Serialize data portion based on type.
 

Static Private Member Functions

static bool deserialize_data (value &result, value_types type, const std::vector< uint8_t > &data, size_t &offset)
 Deserialize data portion based on type.
 

Private Attributes

const std::string name_
 
ValueVariant data_
 
std::shared_mutex mutex_
 
std::atomic< size_t > read_count_ {0}
 
std::atomic< size_t > write_count_ {0}
 

Detailed Description

Enhanced type-safe value with perfect legacy compatibility.

Key improvements over variant_value:

  1. Type indices match value_types enum exactly
  2. Support for array_value (type 15)
  3. Platform-aware llong/ullong handling
  4. Zero data loss on serialization round-trips
  5. Backward compatible with legacy value system
Examples
value_store_example.cpp.

Definition at line 128 of file value.h.

Constructor & Destructor Documentation

◆ value() [1/6]

kcenon::container::value::value ( )
inline

Default constructor - creates null value.

Definition at line 134 of file value.h.

134: name_(""), data_(std::in_place_index<0>) {}
ValueVariant data_
Definition value.h:313
const std::string name_
Definition value.h:312

Referenced by deserialize_data(), to_json(), and to_string().

Here is the caller graph for this function:

◆ value() [2/6]

kcenon::container::value::value ( std::string_view name)
inlineexplicit

Construct with name (null value)

Definition at line 139 of file value.h.

140 : name_(name), data_(std::in_place_index<0>) {}
std::string_view name() const noexcept
Get the name of this value (lock-free, immutable)
Definition value.h:177

◆ value() [3/6]

template<typename T >
kcenon::container::value::value ( std::string_view name,
T && value )
inline

Construct with name and typed value.

Definition at line 146 of file value.h.

147 : name_(name), data_(std::forward<T>(value)) {}
value()
Default constructor - creates null value.
Definition value.h:134

◆ value() [4/6]

kcenon::container::value::value ( std::string_view name,
value_types type,
const std::vector< uint8_t > & raw_data )

Construct from value_types and raw data (legacy compatibility)

Definition at line 87 of file value.cpp.

90 : name_(name), data_(std::in_place_index<0>)
91 {
92 // Construct variant from legacy type and raw data
93 size_t offset = 0;
94 if (!deserialize_data(*this, type, raw_data, offset)) {
95 // Failed to deserialize, reset to null
96 data_.emplace<0>();
97 }
98 }
static bool deserialize_data(value &result, value_types type, const std::vector< uint8_t > &data, size_t &offset)
Deserialize data portion based on type.
Definition value.cpp:359
value_types type() const
Get the value_types enum (NOT variant::index()!)
Definition value.cpp:138

References data_, deserialize_data(), and type().

Here is the call graph for this function:

◆ value() [5/6]

kcenon::container::value::value ( const value & other)

Copy constructor (thread-safe)

Definition at line 100 of file value.cpp.

101 : name_(other.name_)
102 {
103 std::shared_lock lock(other.mutex_);
104 data_ = other.data_;
105 }

References data_, and mutex_.

◆ value() [6/6]

kcenon::container::value::value ( value && other)
noexcept

Move constructor.

Definition at line 107 of file value.cpp.

108 : name_(other.name_)
109 {
110 std::unique_lock lock(other.mutex_);
111 data_ = std::move(other.data_);
112 read_count_ = other.read_count_.load();
113 write_count_ = other.write_count_.load();
114 other.read_count_ = 0;
115 other.write_count_ = 0;
116 }
std::atomic< size_t > write_count_
Definition value.h:316
std::atomic< size_t > read_count_
Definition value.h:315

Member Function Documentation

◆ deserialize()

std::optional< value > kcenon::container::value::deserialize ( const std::vector< uint8_t > & data)
static

Deserialize from binary format (legacy compatible)

Supports both:

  • New value serialized data
  • Legacy value class serialized data
Returns
std::optional containing value if deserialization succeeds

Definition at line 555 of file value.cpp.

555 {
556 if (data.size() < sizeof(uint32_t) + 1) {
557 return std::nullopt; // Too small: need at least name_len + type
558 }
559
560 size_t offset = 0;
561
562 // Read name length
563 uint32_t name_len;
564 std::memcpy(&name_len, data.data() + offset, sizeof(name_len));
565 offset += sizeof(name_len);
566
567 if (offset + name_len + 1 > data.size()) {
568 return std::nullopt; // Invalid name length
569 }
570
571 // Read name
572 std::string name(data.begin() + offset, data.begin() + offset + name_len);
573 offset += name_len;
574
575 // Read type byte
576 uint8_t type_byte = data[offset++];
577 value_types type = static_cast<value_types>(type_byte);
578
579 // Validate type range
580 if (type_byte > 15) {
581 return std::nullopt; // Invalid type
582 }
583
584 // Create result with name
586
587 // Deserialize data based on type
588 if (!deserialize_data(result, type, data, offset)) {
589 return std::nullopt;
590 }
591
592 return result;
593 }
Enhanced type-safe value with perfect legacy compatibility.
Definition value.h:129

References deserialize_data(), name(), and type().

Referenced by kcenon::container::clone_with_name(), kcenon::container::thread_safe_container::deserialize(), and deserialize_data().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ deserialize_data()

bool kcenon::container::value::deserialize_data ( value & result,
value_types type,
const std::vector< uint8_t > & data,
size_t & offset )
staticprivate

Deserialize data portion based on type.

Definition at line 359 of file value.cpp.

362 {
363 switch (type) {
364 case value_types::null_value:
365 result.data_.emplace<0>(); // null is at index 0
366 return true;
367
368 case value_types::bool_value:
369 if (offset >= data.size()) return false;
370 result.data_ = (data[offset++] != 0);
371 return true;
372
373 case value_types::short_value: {
374 if (offset + sizeof(int16_t) > data.size()) return false;
375 int16_t value;
376 std::memcpy(&value, data.data() + offset, sizeof(value));
377 offset += sizeof(value);
378 result.data_ = value;
379 return true;
380 }
381
382 case value_types::ushort_value: {
383 if (offset + sizeof(uint16_t) > data.size()) return false;
384 uint16_t value;
385 std::memcpy(&value, data.data() + offset, sizeof(value));
386 offset += sizeof(value);
387 result.data_ = value;
388 return true;
389 }
390
391 case value_types::int_value: {
392 if (offset + sizeof(int32_t) > data.size()) return false;
393 int32_t value;
394 std::memcpy(&value, data.data() + offset, sizeof(value));
395 offset += sizeof(value);
396 result.data_ = value;
397 return true;
398 }
399
400 case value_types::uint_value: {
401 if (offset + sizeof(uint32_t) > data.size()) return false;
402 uint32_t value;
403 std::memcpy(&value, data.data() + offset, sizeof(value));
404 offset += sizeof(value);
405 result.data_ = value;
406 return true;
407 }
408
409 case value_types::long_value: {
410 if (offset + sizeof(int64_t) > data.size()) return false;
411 int64_t value;
412 std::memcpy(&value, data.data() + offset, sizeof(value));
413 offset += sizeof(value);
414 result.data_ = value;
415 return true;
416 }
417
418 case value_types::llong_value: {
419 if (offset + sizeof(int64_t) > data.size()) return false;
420#if defined(_MSC_VER) && _MSC_VER < 1900
421 // Old MSVC (2013-): int64_t is __int64, long long is separate
422 long long value;
423 std::memcpy(&value, data.data() + offset, sizeof(value));
424 offset += sizeof(value);
425 result.data_ = value; // Stored at index 8 (llong_value slot)
426#else
427 // Modern platforms (macOS, Linux, MSVC 2015+): int64_t == long long
428 // llong_value uses same storage as long_value (int64_t)
429 int64_t value;
430 std::memcpy(&value, data.data() + offset, sizeof(value));
431 offset += sizeof(value);
432 result.data_ = value; // Stored at index 6 (long_value slot)
433#endif
434 return true;
435 }
436
437 case value_types::ulong_value: {
438 if (offset + sizeof(uint64_t) > data.size()) return false;
439 uint64_t value;
440 std::memcpy(&value, data.data() + offset, sizeof(value));
441 offset += sizeof(value);
442 result.data_ = value;
443 return true;
444 }
445
446 case value_types::ullong_value: {
447 if (offset + sizeof(uint64_t) > data.size()) return false;
448#if defined(_MSC_VER) && _MSC_VER < 1900
449 // Old MSVC (2013-): uint64_t is unsigned __int64, unsigned long long is separate
450 unsigned long long value;
451 std::memcpy(&value, data.data() + offset, sizeof(value));
452 offset += sizeof(value);
453 result.data_ = value; // Stored at index 9 (ullong_value slot)
454#else
455 // Modern platforms (macOS, Linux, MSVC 2015+): uint64_t == unsigned long long
456 // ullong_value uses same storage as ulong_value (uint64_t)
457 uint64_t value;
458 std::memcpy(&value, data.data() + offset, sizeof(value));
459 offset += sizeof(value);
460 result.data_ = value; // Stored at index 7 (ulong_value slot)
461#endif
462 return true;
463 }
464
465 case value_types::float_value: {
466 if (offset + sizeof(float) > data.size()) return false;
467 float value;
468 std::memcpy(&value, data.data() + offset, sizeof(value));
469 offset += sizeof(value);
470 result.data_ = value;
471 return true;
472 }
473
474 case value_types::double_value: {
475 if (offset + sizeof(double) > data.size()) return false;
476 double value;
477 std::memcpy(&value, data.data() + offset, sizeof(value));
478 offset += sizeof(value);
479 result.data_ = value;
480 return true;
481 }
482
483 case value_types::bytes_value: {
484 if (offset + sizeof(uint32_t) > data.size()) return false;
485 uint32_t size;
486 std::memcpy(&size, data.data() + offset, sizeof(size));
487 offset += sizeof(size);
488 if (offset + size > data.size()) return false;
489 std::vector<uint8_t> bytes(data.begin() + offset,
490 data.begin() + offset + size);
491 offset += size;
492 result.data_ = std::move(bytes);
493 return true;
494 }
495
496 case value_types::string_value: {
497 if (offset + sizeof(uint32_t) > data.size()) return false;
498 uint32_t size;
499 std::memcpy(&size, data.data() + offset, sizeof(size));
500 offset += sizeof(size);
501 if (offset + size > data.size()) return false;
502 std::string str(data.begin() + offset, data.begin() + offset + size);
503 offset += size;
504 result.data_ = std::move(str);
505 return true;
506 }
507
508 case value_types::container_value: {
509 if (offset + sizeof(uint32_t) > data.size()) return false;
510 uint32_t size;
511 std::memcpy(&size, data.data() + offset, sizeof(size));
512 offset += sizeof(size);
513 if (size == 0) {
514 result.data_ = std::shared_ptr<thread_safe_container>(nullptr);
515 return true;
516 }
517 if (offset + size > data.size()) return false;
518 std::vector<uint8_t> container_data(data.begin() + offset,
519 data.begin() + offset + size);
520 offset += size;
521 auto container = thread_safe_container::deserialize(container_data);
522 result.data_ = container;
523 return true;
524 }
525
526 case value_types::array_value: {
527 if (offset + sizeof(uint32_t) > data.size()) return false;
528 uint32_t count;
529 std::memcpy(&count, data.data() + offset, sizeof(count));
530 offset += sizeof(count);
531
532 array_variant arr;
533 arr.values.reserve(count);
534
535 for (uint32_t i = 0; i < count; ++i) {
536 auto elem = deserialize(std::vector<uint8_t>(data.begin() + offset, data.end()));
537 if (!elem) return false;
538
539 // Update offset based on elem's serialized size
540 auto elem_size = elem->serialize().size();
541 offset += elem_size;
542
543 arr.values.push_back(std::make_shared<value>(std::move(*elem)));
544 }
545
546 result.data_ = std::move(arr);
547 return true;
548 }
549
550 default:
551 return false;
552 }
553 }
static std::shared_ptr< thread_safe_container > deserialize(const std::vector< uint8_t > &data)
Deserialize from binary.
static std::optional< value > deserialize(const std::vector< uint8_t > &data)
Deserialize from binary format (legacy compatible)
Definition value.cpp:555

References data_, kcenon::container::thread_safe_container::deserialize(), deserialize(), type(), value(), and kcenon::container::array_variant::values.

Referenced by deserialize(), and value().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get()

template<concepts::ValueVariantType T>
std::optional< T > kcenon::container::value::get ( ) const
inline

Type-safe getter with optional return.

Template Parameters
TThe type to retrieve (must be valid variant type)
Returns
std::optional containing value if type matches

Definition at line 213 of file value.h.

213 {
214 std::shared_lock lock(mutex_);
215 if (auto* ptr = std::get_if<T>(&data_)) {
216 return *ptr;
217 }
218 return std::nullopt;
219 }
std::shared_mutex mutex_
Definition value.h:314

References data_, and mutex_.

◆ is_null()

bool kcenon::container::value::is_null ( ) const
inline

Check if null value.

Definition at line 202 of file value.h.

202 {
203 std::shared_lock lock(mutex_);
204 return data_.index() == 0;
205 }

References data_, and mutex_.

◆ name()

std::string_view kcenon::container::value::name ( ) const
inlinenoexcept

Get the name of this value (lock-free, immutable)

Definition at line 177 of file value.h.

177{ return name_; }

References name_.

Referenced by deserialize(), kcenon::container::thread_safe_container::set_variant(), and to_json().

Here is the caller graph for this function:

◆ operator!=()

bool kcenon::container::value::operator!= ( const value & other) const
inline

Definition at line 295 of file value.h.

295{ return !(*this == other); }

◆ operator<()

bool kcenon::container::value::operator< ( const value & other) const

Definition at line 601 of file value.cpp.

601 {
602 if (this == &other) return false;
603 std::scoped_lock lock(mutex_, other.mutex_);
604 if (name_ != other.name_) return name_ < other.name_;
605 return data_ < other.data_;
606 }

References data_, mutex_, and name_.

◆ operator=() [1/2]

value & kcenon::container::value::operator= ( const value & other)

Copy assignment (thread-safe)

Definition at line 118 of file value.cpp.

118 {
119 if (this != &other) {
120 std::unique_lock lock(mutex_);
121 std::shared_lock other_lock(other.mutex_);
122 data_ = other.data_;
123 write_count_.fetch_add(1, std::memory_order_relaxed);
124 }
125 return *this;
126 }

References data_, mutex_, and write_count_.

◆ operator=() [2/2]

value & kcenon::container::value::operator= ( value && other)
noexcept

Move assignment.

Definition at line 128 of file value.cpp.

128 {
129 if (this != &other) {
130 std::unique_lock lock(mutex_);
131 std::unique_lock other_lock(other.mutex_);
132 data_ = std::move(other.data_);
133 write_count_.fetch_add(1, std::memory_order_relaxed);
134 }
135 return *this;
136 }

◆ operator==()

bool kcenon::container::value::operator== ( const value & other) const

Comparison operators (thread-safe)

Definition at line 595 of file value.cpp.

595 {
596 if (this == &other) return true;
597 std::scoped_lock lock(mutex_, other.mutex_);
598 return name_ == other.name_ && data_ == other.data_;
599 }

References data_, mutex_, and name_.

◆ read_count()

size_t kcenon::container::value::read_count ( ) const
inline

Get read/write statistics.

Definition at line 288 of file value.h.

288{ return read_count_.load(std::memory_order_relaxed); }

References read_count_.

◆ serialize()

std::vector< uint8_t > kcenon::container::value::serialize ( ) const

Serialize to binary format (legacy compatible)

Wire format: [name_len:4][name:UTF-8][type:1][data:variable]

Type byte matches value_types enum EXACTLY (0-15)

Definition at line 339 of file value.cpp.

339 {
340 std::vector<uint8_t> result;
341
342 // Header: [name_length:4][name:UTF-8][type:1]
343 uint32_t name_len = static_cast<uint32_t>(name_.size());
344 result.insert(result.end(),
345 reinterpret_cast<const uint8_t*>(&name_len),
346 reinterpret_cast<const uint8_t*>(&name_len) + sizeof(name_len));
347 result.insert(result.end(), name_.begin(), name_.end());
348
349 // Type byte: use value_types enum value (0-15)
350 uint8_t type_byte = static_cast<uint8_t>(type());
351 result.push_back(type_byte);
352
353 // Data: type-specific serialization
354 serialize_data(result);
355
356 return result;
357 }
void serialize_data(std::vector< uint8_t > &result) const
Serialize data portion based on type.
Definition value.cpp:259

References name_, serialize_data(), and type().

Referenced by kcenon::container::clone_with_name(), kcenon::container::thread_safe_container::serialize(), and serialize_data().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ serialize_data()

void kcenon::container::value::serialize_data ( std::vector< uint8_t > & result) const
private

Serialize data portion based on type.

Definition at line 259 of file value.cpp.

259 {
260 visit([&result](auto&& val) {
261 using T = std::decay_t<decltype(val)>;
262
263 if constexpr (std::is_same_v<T, std::monostate>) {
264 // Null val: no data
265 }
266 else if constexpr (std::is_same_v<T, bool>) {
267 result.push_back(val ? 1 : 0);
268 }
269 else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
270 // Bytes: [length:4][data:length]
271 uint32_t size = static_cast<uint32_t>(val.size());
272 result.insert(result.end(),
273 reinterpret_cast<const uint8_t*>(&size),
274 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
275 result.insert(result.end(), val.begin(), val.end());
276 }
277 else if constexpr (std::is_arithmetic_v<T> && !std::is_same_v<T, bool>) {
278 // Numeric: raw bytes
279 result.insert(result.end(),
280 reinterpret_cast<const uint8_t*>(&val),
281 reinterpret_cast<const uint8_t*>(&val) + sizeof(T));
282 }
283 else if constexpr (std::is_same_v<T, std::string>) {
284 // String: [length:4][UTF-8 data:length]
285 uint32_t size = static_cast<uint32_t>(val.size());
286 result.insert(result.end(),
287 reinterpret_cast<const uint8_t*>(&size),
288 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
289 result.insert(result.end(), val.begin(), val.end());
290 }
291 else if constexpr (std::is_same_v<T, std::shared_ptr<thread_safe_container>>) {
292 // Container: [serialized_size:4][serialized_data:size]
293 if (val) {
294 // Check for circular reference
295 detail::circular_ref_guard guard(val.get());
296 if (guard.is_circular()) {
297 // Circular reference detected - serialize as null container
298 uint32_t size = 0;
299 result.insert(result.end(),
300 reinterpret_cast<const uint8_t*>(&size),
301 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
302 } else {
303 auto container_data = val->serialize();
304 uint32_t size = static_cast<uint32_t>(container_data.size());
305 result.insert(result.end(),
306 reinterpret_cast<const uint8_t*>(&size),
307 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
308 result.insert(result.end(), container_data.begin(), container_data.end());
309 }
310 } else {
311 uint32_t size = 0;
312 result.insert(result.end(),
313 reinterpret_cast<const uint8_t*>(&size),
314 reinterpret_cast<const uint8_t*>(&size) + sizeof(size));
315 }
316 }
317 else if constexpr (std::is_same_v<T, array_variant>) {
318 // Array: [count:4][value1_serialized][value2_serialized]...
319 uint32_t count = static_cast<uint32_t>(val.values.size());
320 result.insert(result.end(),
321 reinterpret_cast<const uint8_t*>(&count),
322 reinterpret_cast<const uint8_t*>(&count) + sizeof(count));
323
324 for (const auto& elem : val.values) {
325 if (elem) {
326 auto elem_data = elem->serialize();
327 result.insert(result.end(), elem_data.begin(), elem_data.end());
328 } else {
329 // Null element: serialize as null_value with empty name
330 kcenon::container::value null_elem("");
331 auto null_data = null_elem.serialize();
332 result.insert(result.end(), null_data.begin(), null_data.end());
333 }
334 }
335 }
336 });
337 }
auto visit(Visitor &&vis) const
Apply visitor to contained value (const)
Definition value.h:237

References kcenon::container::detail::circular_ref_guard::is_circular(), serialize(), and visit().

Referenced by serialize().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set()

template<concepts::ValueVariantType T>
void kcenon::container::value::set ( T && value)
inline

Type-safe setter.

Template Parameters
TThe type to store (must be valid variant type)

Definition at line 226 of file value.h.

226 {
227 std::unique_lock lock(mutex_);
228 data_ = std::forward<T>(value);
229 write_count_.fetch_add(1, std::memory_order_relaxed);
230 }

References data_, mutex_, and write_count_.

◆ to_json()

std::string kcenon::container::value::to_json ( ) const

Convert to JSON representation.

Definition at line 186 of file value.cpp.

186 {
187 auto var_name = name();
188 auto var_type = type();
189
190 return visit([var_name, var_type](auto&& value) -> std::string {
191 using T = std::decay_t<decltype(value)>;
192
193 // Manual JSON header formatting (no std::format dependency)
194 std::string result = "{\"name\":\"" + std::string(var_name) +
195 "\",\"type\":" + std::to_string(static_cast<int>(var_type)) +
196 ",\"value\":";
197
198 if constexpr (std::is_same_v<T, std::monostate>) {
199 result += "null";
200 }
201 else if constexpr (std::is_same_v<T, bool>) {
202 result += value ? "true" : "false";
203 }
204 else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
205 result += "\"";
206 std::ostringstream oss;
207 oss << std::hex << std::setfill('0');
208 for (auto byte : value) {
209 oss << std::setw(2) << static_cast<int>(byte);
210 }
211 result += oss.str();
212 result += "\"";
213 }
214 else if constexpr (std::is_arithmetic_v<T>) {
215 result += std::to_string(value);
216 }
217 else if constexpr (std::is_same_v<T, std::string>) {
218 // Escape string for JSON
219 result += "\"";
220 for (char c : value) {
221 switch (c) {
222 case '"': result += "\\\""; break;
223 case '\\': result += "\\\\"; break;
224 case '\b': result += "\\b"; break;
225 case '\f': result += "\\f"; break;
226 case '\n': result += "\\n"; break;
227 case '\r': result += "\\r"; break;
228 case '\t': result += "\\t"; break;
229 default:
230 if (c >= 0x20 && c <= 0x7E) {
231 result += c;
232 } else {
233 // Manual Unicode escape formatting (no std::format dependency)
234 char buf[7]; // "\uXXXX" + null terminator
235 std::snprintf(buf, sizeof(buf), "\\u%04x", static_cast<unsigned char>(c));
236 result += buf;
237 }
238 }
239 }
240 result += "\"";
241 }
242 else if constexpr (std::is_same_v<T, std::shared_ptr<thread_safe_container>>) {
243 result += value ? value->to_json() : "null";
244 }
245 else if constexpr (std::is_same_v<T, array_variant>) {
246 result += "[";
247 for (size_t i = 0; i < value.values.size(); ++i) {
248 if (i > 0) result += ",";
249 result += value.values[i] ? value.values[i]->to_json() : "null";
250 }
251 result += "]";
252 }
253
254 result += "}";
255 return result;
256 });
257 }

References name(), to_json(), type(), value(), and visit().

Referenced by kcenon::container::thread_safe_container::to_json(), to_json(), and to_string().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ to_string()

std::string kcenon::container::value::to_string ( ) const

Convert to string representation.

Definition at line 146 of file value.cpp.

146 {
147 return visit([](auto&& value) -> std::string {
148 using T = std::decay_t<decltype(value)>;
149
150 if constexpr (std::is_same_v<T, std::monostate>) {
151 return "null";
152 }
153 else if constexpr (std::is_same_v<T, bool>) {
154 return value ? "true" : "false";
155 }
156 else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
157 std::ostringstream oss;
158 oss << std::hex << std::setfill('0');
159 for (auto byte : value) {
160 oss << std::setw(2) << static_cast<int>(byte);
161 }
162 return oss.str();
163 }
164 else if constexpr (std::is_arithmetic_v<T>) {
165 return std::to_string(value);
166 }
167 else if constexpr (std::is_same_v<T, std::string>) {
168 return value;
169 }
170 else if constexpr (std::is_same_v<T, std::shared_ptr<thread_safe_container>>) {
171 return value ? value->to_json() : "null";
172 }
173 else if constexpr (std::is_same_v<T, array_variant>) {
174 std::string result = "[";
175 for (size_t i = 0; i < value.values.size(); ++i) {
176 if (i > 0) result += ",";
177 result += value.values[i] ? value.values[i]->to_string() : "null";
178 }
179 result += "]";
180 return result;
181 }
182 return "";
183 });
184 }

References to_json(), to_string(), value(), and visit().

Referenced by to_string().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ type()

value_types kcenon::container::value::type ( ) const

Get the value_types enum (NOT variant::index()!)

IMPORTANT: This returns the logical type, which matches:

  • value_types enum (0-15)
  • Wire format type byte
  • Legacy system type

On platforms where llong == int64_t, llong_value returns as long_value.

Definition at line 138 of file value.cpp.

138 {
139 std::shared_lock lock(mutex_);
140 // Direct mapping: variant index == value_types enum value
141 // Positions 6-9 now use unaliased fundamental types (long, unsigned long,
142 // long long, unsigned long long) so direct index cast is always correct.
143 return static_cast<value_types>(data_.index());
144 }

References data_, and mutex_.

Referenced by deserialize(), deserialize_data(), kcenon::container::same_type(), serialize(), to_json(), kcenon::container::type_name(), and value().

Here is the caller graph for this function:

◆ variant_index()

size_t kcenon::container::value::variant_index ( ) const
inline

Get variant index (for internal use only)

Definition at line 194 of file value.h.

194 {
195 std::shared_lock lock(mutex_);
196 return data_.index();
197 }

References data_, and mutex_.

◆ visit()

template<concepts::ValueVisitor Visitor>
auto kcenon::container::value::visit ( Visitor && vis) const
inline

Apply visitor to contained value (const)

Template Parameters
VisitorThe visitor callable type

Definition at line 237 of file value.h.

237 {
238 std::shared_lock lock(mutex_);
239 read_count_.fetch_add(1, std::memory_order_relaxed);
240 return std::visit(std::forward<Visitor>(vis), data_);
241 }

References data_, mutex_, and read_count_.

Referenced by serialize_data(), to_json(), and to_string().

Here is the caller graph for this function:

◆ visit_mut()

template<concepts::ValueVisitor Visitor>
auto kcenon::container::value::visit_mut ( Visitor && vis)
inline

Apply visitor to contained value (mutable)

Template Parameters
VisitorThe visitor callable type

Definition at line 248 of file value.h.

248 {
249 std::unique_lock lock(mutex_);
250 write_count_.fetch_add(1, std::memory_order_relaxed);
251 return std::visit(std::forward<Visitor>(vis), data_);
252 }

References data_, mutex_, and write_count_.

◆ write_count()

size_t kcenon::container::value::write_count ( ) const
inline

Definition at line 289 of file value.h.

289{ return write_count_.load(std::memory_order_relaxed); }

References write_count_.

Member Data Documentation

◆ data_

ValueVariant kcenon::container::value::data_
private

◆ mutex_

std::shared_mutex kcenon::container::value::mutex_
mutableprivate

◆ name_

const std::string kcenon::container::value::name_
private

Definition at line 312 of file value.h.

Referenced by name(), operator<(), operator==(), and serialize().

◆ read_count_

std::atomic<size_t> kcenon::container::value::read_count_ {0}
mutableprivate

Definition at line 315 of file value.h.

315{0};

Referenced by read_count(), and visit().

◆ write_count_

std::atomic<size_t> kcenon::container::value::write_count_ {0}
mutableprivate

Definition at line 316 of file value.h.

316{0};

Referenced by operator=(), set(), visit_mut(), and write_count().


The documentation for this class was generated from the following files: