JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
Loading...
Searching...
No Matches
immutable_str.h File Reference

Immutable, thread-safe string with optional auto-trimming and dual-mode build support. More...

#include <algorithm>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <cstring>
#include <string_view>
#include <cstdint>
#include <optional>
#include <type_traits>
#include <stdexcept>
#include "jh/synchronous/const_lock.h"
#include "jh/concurrent/observe_pool.h"
#include "jh/pods/string_view.h"
#include "jh/macros/header_begin.h"
#include "jh/macros/header_end.h"

Go to the source code of this file.

Classes

class  jh::immutable_str
 Immutable string with optional automatic trimming and thread-safe hash caching. More...
struct  jh::atomic_str_hash
 Custom hash functor for atomic_str_ptr and compatible types. More...
struct  jh::atomic_str_eq
 Custom equality functor for atomic_str_ptr and compatible types. More...

Concepts

concept  jh::immutable_str_compatible
 Concept for types compatible with jh::immutable_str.

Macros

#define JH_IMMUTABLE_STR_AUTO_TRIM   true
 Controls whether jh::immutable_str performs automatic leading/trailing ASCII whitespace trimming.

Typedefs

using jh::atomic_str_ptr = std::shared_ptr<immutable_str>
 Atomically replaceable handle to an immutable string.

Functions

template<typename T>
atomic_str_ptr jh::make_atomic (T str)=delete
atomic_str_ptr jh::make_atomic (const char *str)
 Creates a shared pointer to an immutable_str.
template<jh::concepts::mutex_like M>
atomic_str_ptr jh::safe_from (std::string_view sv, M &mtx)
 Creates a shared pointer to an immutable_str from a locked string view.

Detailed Description

Immutable, thread-safe string with optional auto-trimming and dual-mode build support.

Author
JeongHan-Bae <mastropseudo@gmail.com>

Overview

jh::immutable_str provides a true immutable string type in modern C++. It guarantees memory-level immutability and thread safety — once created, the string data can never be modified. This makes it ideal for concurrent environments, global configuration caches, or static metadata storage.

Key Characteristics

  • Strict immutability at the memory level — no API allows modification.
  • Thread-safe by design — multiple threads can safely share instances.
  • Optional automatic whitespace trimming during construction.
  • Compact, zero-reallocation model using unique_ptr<const char[]>.
  • Transparent hashing and equality for unordered containers.
  • Seamless integration with std::shared_ptr<immutable_str> for safe sharing.

Motivation

In C++, std::string remains mutable even when declared const. This permits unintended modification via const_cast or aliasing, leading to race conditions and subtle data corruption. jh::immutable_str eliminates these risks by enforcing immutability both at the type level and memory level.

Comparison with const std::string

Featurejh::immutable_strconst std::string
Memory-level immutability✅ True❌ False
Thread safety✅ Safe by design❌ Mutable buffer
Reallocation risk❌ None✅ Possible
Hashing✅ Cached, thread-safe❌ Recomputed each time
Storage modelCompact (unique_ptr)Dynamic capacity-managed

Core Features

  • Immutable Data: Stored via unique_ptr<const char[]>, preventing mutation.
  • Thread-Safe Hashing: Lazy-evaluated via std::once_flag to ensure safe caching.
  • Auto Trimming: Optional compile-time whitespace removal (controlled by JH_IMMUTABLE_STR_AUTO_TRIM).
  • Shared Ownership: Distributed through jh::atomic_str_ptr (shared_ptr alias).
  • Interop: Compatible with std::string_view and C-string APIs.
  • Custom Hash & Eq: Support for transparent unordered_map lookup via const char*.

Dual-Mode Header Integration

From v1.3.x, jh::immutable_str supports the Dual-Mode Header system:

  • Linked through jh::jh-toolkit → acts as a header-only component.
  • Linked through jh::jh-toolkit-static → compiled as a static implementation for performance and deterministic linking.
  • Mode controlled internally via JH_INTERNAL_SHOULD_DEFINE.

Static Build Detection

The method bool jh::immutable_str::is_static_built() allows runtime mode verification:

  • Returns true if built as part of jh-toolkit-static.
  • Returns false when using header-only mode via jh-toolkit.

Usage Example

#include <jh/immutable_str>
#include <iostream>
int main() {
const auto str = pool.acquire("Hello, JH Toolkit!");
std::cout << str->view() << std::endl;
return 0;
}
Forwarding header for jh::immutable_str.
conc::pointer_pool< T, weak_ptr_hash< T >, weak_ptr_eq< T > > observe_pool
Duck-typed alias of jh::conc::pointer_pool for content-based pooling of immutable objects.
Definition observe_pool.h:191

If the program prints the expected message, it confirms that the correct linkage (header-only or static) is configured properly in your build environment.

Automatic Pool Integration

This header automatically includes jh/core/pool.h, exposing the full pooling behavior of jh::immutable_str without requiring any additional include directives. Because jh::observe_pool performs duck-typed deduction (detecting hash() and operator==), immutable_str instances are automatically compatible with jh::observe_pool.

In addition to providing hash() and operator==, types used with jh::observe_pool must guarantee semantic immutability: once an object is inserted into the pool, its hash() and equality semantics must remain constant for its entire lifetime. This ensures that pooled instances remain stable and deduplicated.

jh::immutable_str naturally satisfies this requirement — its memory content and hash are fixed at construction time and can never change. Therefore, it represents the canonical example of a pool-safe immutable type.

This means you can directly acquire shared, deduplicated immutable strings:

#include <jh/immutable_str>
#include <cstdio>
int main() {
jh::observe_pool<jh::immutable_str> pool; // automatically available
auto a = pool.acquire("JH Toolkit");
auto b = pool.acquire("JH Toolkit");
// both handles reference the same pooled immutable instance
if (a.get() == b.get()) {
std::puts("deduplicated successfully");
}
}
std::shared_ptr< T > acquire(Args &&... args) const =delete
Deleted acquire() for const pointer_pool.

The pool.acquire() call internally checks for an existing equivalent object (by hash() and operator==) and reuses it if found. This guarantees that semantically identical strings always share the same underlying immutable buffer.

  • Automatic inclusion: jh/pool.h is included by default.
  • Value-based pooling: Identical strings resolve to the same shared instance.
  • Thread-safe: Pool operations are safe since immutable_str itself is immutable.
  • Duck-typed deduction: jh::observe_pool automatically recognizes compatible types implementing hash() and operator==.

Performance Notes

  • Immutable buffer — no internal reallocation or mutation.
  • Constant-time string comparison and hash access after first computation.
  • Optimized for concurrent, read-dominant workloads.
  • Minimal memory footprint: pointer + cached hash + length field.
  • Benchmark: In controlled micro-benchmarks (LLVM@20, Catch2, 1024× iterations),
    jh::immutable_str shows performance essentially identical to std::string — sometimes slower by about 1%, sometimes faster by up to 2%, typically fluctuating within ±2%. This variation is within normal measurement noise.

See Also

Version
1.3.x
Date
2025

Macro Definition Documentation

◆ JH_IMMUTABLE_STR_AUTO_TRIM

#define JH_IMMUTABLE_STR_AUTO_TRIM   true

Controls whether jh::immutable_str performs automatic leading/trailing ASCII whitespace trimming.

Default: true

If not defined manually, it defaults to:

#define JH_IMMUTABLE_STR_AUTO_TRIM true

If overriding manually, it must be defined before any inclusion of <jh/immutable_str> in the translation unit.

Configuration methods:

  • Source-level (before include):
        #define JH_IMMUTABLE_STR_AUTO_TRIM false
        
  • CMake:
        target_compile_definitions(target PRIVATE JH_IMMUTABLE_STR_AUTO_TRIM=false)
        
  • Compiler flag:
        -DJH_IMMUTABLE_STR_AUTO_TRIM=false
        

Important:

  • Must be a boolean literal: true or false (not 1/0).
  • Acts as a type-level compile-time policy parameter via static constexpr bool immutable_str::auto_trim.
  • Branch selection is performed using if constexpr, not preprocessor elimination.
  • In the static library target (jh-toolkit-static), both code paths are compiled; user compilation activates the selected branch.

Behavior:

  • true: remove leading/trailing ASCII whitespace at construction.
  • false: preserve input exactly.