|
| 1 | +# Atomic variables (`std::atomic` and `std::atomic_flag`) |
| 2 | + |
| 3 | +reflect-cpp supports serializing and deserializing atomic types. The library treats atomic wrappers as containers around an underlying value and provides helpers to read plain (non-atomic) representations from input and to set atomic fields afterwards. |
| 4 | + |
| 5 | +## Supported atomic types |
| 6 | + |
| 7 | +- `std::atomic<T>` |
| 8 | +- `std::atomic_flag` (serialized as a boolean) |
| 9 | +- Arrays of atomic types (std::array<T, N> and C-style arrays) |
| 10 | +- Aggregate types (structs/NamedTuple) containing atomic fields — each atomic field is handled independently |
| 11 | + |
| 12 | +## Example (writing) |
| 13 | + |
| 14 | +```cpp |
| 15 | +struct Stats { |
| 16 | + std::atomic<std::uint64_t> bytes_downloaded; |
| 17 | + std::atomic<bool> finished; |
| 18 | + std::atomic_flag atomic_flag; |
| 19 | +}; |
| 20 | + |
| 21 | +Stats stats{.bytes_downloaded = 123456789, .finished = true, .atomic_flag = ATOMIC_FLAG_INIT}; |
| 22 | +const auto json_str = rfl::json::write(stats); |
| 23 | +// -> {"bytes_downloaded":123456789,"finished":true,"atomic_flag":false} |
| 24 | +``` |
| 25 | +
|
| 26 | +Note: the exact boolean value for `atomic_flag` depends on whether it is set or cleared. |
| 27 | +
|
| 28 | +## Example (reading) |
| 29 | +
|
| 30 | +Reading atomic variables is not quite trivial, because atomic fields cannot be copied or moved. Consider the following example: |
| 31 | +
|
| 32 | +```cpp |
| 33 | +// const auto res = rfl::json::read<Stats>(json_str); |
| 34 | +// This will NOT compile because std::atomic<T> is neither copyable nor movable |
| 35 | +``` |
| 36 | + |
| 37 | +There are two ways around this problem: |
| 38 | + |
| 39 | +### 1. Wrap in `rfl::Ref`, `rfl::Box`, `std::shared_ptr` or `std::unique_ptr` |
| 40 | + |
| 41 | +The easiest way to read structs with atomic fields is to wrap them in a pointer-like type such as `rfl::Ref`, `rfl::Box`, `std::shared_ptr` or `std::unique_ptr`. This works because the pointer-like types themselves are copyable/movable, even if the underlying type is not. |
| 42 | + |
| 43 | +```cpp |
| 44 | +const auto res = rfl::json::read<rfl::Ref<Stats>>(json_str); |
| 45 | +``` |
| 46 | + |
| 47 | +### 2. Read into a non-atomic representation and then set atomic fields |
| 48 | + |
| 49 | +The second way is to read into a non-atomic representation of the struct and then set the atomic fields afterwards using `rfl::atomic::set_atomic_fields`. The non-atomic representation can be obtained using `rfl::atomic::remove_atomic_t`. |
| 50 | + |
| 51 | +```cpp |
| 52 | +Stats stats; |
| 53 | + |
| 54 | +const rfl::Result<rfl::Nothing> res = |
| 55 | + rfl::json::read<rfl::atomic::remove_atomic_t<Stats>>(json_str) |
| 56 | + .transform([&](auto&& non_atomic_stats) { |
| 57 | + return rfl::atomic::set_atomic_fields(non_atomic_stats, &stats); |
| 58 | + }); |
| 59 | + |
| 60 | +if (!res) { |
| 61 | + // handle error |
| 62 | + std::cerr << "Error reading JSON: " << res.error().what() << std::endl; |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +## Limitations and notes |
| 67 | + |
| 68 | +- Structs containing atomic fields must be default-constructible. |
| 69 | +- Atomic types cannot be mixed with `rfl::DefaultVal` or the `rfl::DefaultIfMissing` processor; attempting to do so triggers a static assertion at compile-time (see parser implementations). |
| 70 | +- The semantics used for setting atomic values use relaxed memory order (`std::memory_order_relaxed`). |
| 71 | +- For complex aggregates, `rfl::atomic` will recurse into nested fields and arrays to set atomic members. |
| 72 | + |
0 commit comments