From 718b928847a46a072125c7fb93e4a952ee83b753 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 17 Jan 2026 18:01:48 +0100 Subject: [PATCH 1/5] Began developing an atomic wrapper --- include/rfl/parsing/Parser.hpp | 1 + include/rfl/parsing/Parser_atomic.hpp | 43 +++++++++++++++++++++++++++ tests/json/test_atomic.cpp | 37 +++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 include/rfl/parsing/Parser_atomic.hpp create mode 100644 tests/json/test_atomic.cpp diff --git a/include/rfl/parsing/Parser.hpp b/include/rfl/parsing/Parser.hpp index 3bd444b0..ae5c92c5 100644 --- a/include/rfl/parsing/Parser.hpp +++ b/include/rfl/parsing/Parser.hpp @@ -2,6 +2,7 @@ #define RFL_PARSING_PARSER_HPP_ #include "Parser_array.hpp" +#include "Parser_atomic.hpp" #include "Parser_base.hpp" #include "Parser_basic_type.hpp" #include "Parser_box.hpp" diff --git a/include/rfl/parsing/Parser_atomic.hpp b/include/rfl/parsing/Parser_atomic.hpp new file mode 100644 index 00000000..f8250912 --- /dev/null +++ b/include/rfl/parsing/Parser_atomic.hpp @@ -0,0 +1,43 @@ +#ifndef RFL_PARSING_PARSER_ATOMIC_HPP_ +#define RFL_PARSING_PARSER_ATOMIC_HPP_ + +#include +#include +#include + +#include "../DefaultVal.hpp" +#include "AreReaderAndWriter.hpp" +#include "Parent.hpp" +#include "Parser_base.hpp" +#include "schema/Type.hpp" + +namespace rfl::parsing { + +template + requires AreReaderAndWriter> +struct Parser, ProcessorsType> { + using InputVarType = typename R::InputVarType; + + using ParentType = Parent; + + static Result read(const R& _r, const InputVarType& _var) noexcept { + return Parser, ProcessorsType>::read(_r, _var); + } + + template + static void write(const W& _w, const std::atomic& _a, const P& _parent) { + Parser, ProcessorsType>::write( + _w, _a.load(std::memory_order_relaxed), _parent); + } + + static schema::Type to_schema( + std::map* _definitions) { + using U = std::remove_cvref_t; + return schema::Type{ + Parser::to_schema(_definitions)}; + } +}; + +} // namespace rfl::parsing + +#endif diff --git a/tests/json/test_atomic.cpp b/tests/json/test_atomic.cpp new file mode 100644 index 00000000..8a87c6db --- /dev/null +++ b/tests/json/test_atomic.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_atomic { + +struct Stats { + std::atomic bytes_downloaded; + std::atomic finished; + + Stats(Stats&& other) noexcept + : bytes_downloaded( + other.bytes_downloaded.load(std::memory_order_relaxed)), + finished(other.finished.load(std::memory_order_relaxed)) {} + + Stats& operator=(Stats&& other) noexcept { + bytes_downloaded.store( + other.bytes_downloaded.load(std::memory_order_relaxed), + std::memory_order_relaxed); + finished.store(other.finished.load(std::memory_order_relaxed), + std::memory_order_relaxed); + return *this; + } + + Stats(const std::uint64_t _bytes_downloaded, const bool _finished) + : bytes_downloaded(_bytes_downloaded), finished(_finished) {} +}; + +TEST(json, test_atomic) { + auto stats = Stats(123456789, true); + + write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); +} +} // namespace test_atomic From 89b17c1832e7d5d755d66147a45b8688f672c959 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 18 Jan 2026 16:40:31 +0100 Subject: [PATCH 2/5] Added more comprehensive support for atomic variables --- include/rfl.hpp | 3 + include/rfl/atomic/is_atomic.hpp | 191 ++++++++++++++++++++++ include/rfl/atomic/remove_atomic_t.hpp | 16 ++ include/rfl/atomic/set_atomic.hpp | 24 +++ include/rfl/parsing/Parser_box.hpp | 31 +++- include/rfl/parsing/Parser_ref.hpp | 29 +++- include/rfl/parsing/Parser_shared_ptr.hpp | 17 +- include/rfl/parsing/Parser_unique_ptr.hpp | 23 ++- tests/json/test_atomic.cpp | 48 +++--- tests/json/test_atomic_array.cpp | 26 +++ tests/json/test_atomic_box.cpp | 20 +++ tests/json/test_atomic_ref.cpp | 20 +++ tests/json/test_atomic_shared_ptr.cpp | 20 +++ tests/json/test_atomic_unique_ptr.cpp | 20 +++ 14 files changed, 446 insertions(+), 42 deletions(-) create mode 100644 include/rfl/atomic/is_atomic.hpp create mode 100644 include/rfl/atomic/remove_atomic_t.hpp create mode 100644 include/rfl/atomic/set_atomic.hpp create mode 100644 tests/json/test_atomic_array.cpp create mode 100644 tests/json/test_atomic_box.cpp create mode 100644 tests/json/test_atomic_ref.cpp create mode 100644 tests/json/test_atomic_shared_ptr.cpp create mode 100644 tests/json/test_atomic_unique_ptr.cpp diff --git a/include/rfl.hpp b/include/rfl.hpp index 8aaa0cbc..74e7ef6f 100644 --- a/include/rfl.hpp +++ b/include/rfl.hpp @@ -50,6 +50,9 @@ #include "rfl/always_false.hpp" #include "rfl/apply.hpp" #include "rfl/as.hpp" +#include "rfl/atomic/is_atomic.hpp" +#include "rfl/atomic/remove_atomic_t.hpp" +#include "rfl/atomic/set_atomic.hpp" #include "rfl/comparisons.hpp" #include "rfl/concepts.hpp" #include "rfl/default.hpp" diff --git a/include/rfl/atomic/is_atomic.hpp b/include/rfl/atomic/is_atomic.hpp new file mode 100644 index 00000000..132630b7 --- /dev/null +++ b/include/rfl/atomic/is_atomic.hpp @@ -0,0 +1,191 @@ +#ifndef RFL_ATOMIC_ISATOMIC_HPP_ +#define RFL_ATOMIC_ISATOMIC_HPP_ + +#include +#include +#include +#include + +#include "../Box.hpp" +#include "../NamedTuple.hpp" +#include "../Ref.hpp" +#include "../Tuple.hpp" +#include "../named_tuple_t.hpp" +#include "../to_view.hpp" + +namespace rfl::atomic { + +template +struct is_atomic; + +template +struct is_atomic { + static constexpr bool value = false; + using RemoveAtomicT = T; + static void set(RemoveAtomicT&& val, T* _t) { *_t = std::forward(val); }; +}; + +template +struct is_atomic> { + static constexpr bool value = true; + using RemoveAtomicT = T; + static void set(RemoveAtomicT&& val, std::atomic* _t) { + _t->store(std::forward(val), std::memory_order_relaxed); + }; +}; + +template +struct is_atomic> { + using Type = std::remove_cvref_t; + + static constexpr bool value = is_atomic::value; + using RemoveAtomicT = std::array::RemoveAtomicT, N>; + static void set(RemoveAtomicT&& val, std::array* _t) { + for (size_t i = 0; i < N; ++i) { + is_atomic::set( + std::forward::RemoveAtomicT>(val[i]), + &((*_t)[i])); + } + } +}; + +template +struct is_atomic { + using Type = std::remove_cvref_t; + + static constexpr bool value = is_atomic::value; + using RemoveAtomicT = std::array::RemoveAtomicT, N>; + static void set(RemoveAtomicT&& val, T (*_t)[N]) { + for (size_t i = 0; i < N; ++i) { + is_atomic::set( + std::forward::RemoveAtomicT>(val[i]), + &((*_t)[i])); + } + } +}; + +template +struct is_atomic> { + using Type = std::remove_cvref_t; + + static constexpr bool value = is_atomic::value; + using RemoveAtomicT = + std::shared_ptr::RemoveAtomicT>; + static void set(RemoveAtomicT&& val, std::shared_ptr* _t) { + if (val) { + auto ptr = std::make_shared(); + is_atomic::set( + std::forward::RemoveAtomicT>(*val), ptr.get()); + *_t = std::move(ptr); + } + } +}; + +template +struct is_atomic> { + using Type = std::remove_cvref_t; + + static constexpr bool value = is_atomic::value; + using RemoveAtomicT = + std::unique_ptr::RemoveAtomicT>; + static void set(RemoveAtomicT&& val, std::unique_ptr* _t) { + if (val) { + auto ptr = std::make_unique(); + is_atomic::set( + std::forward::RemoveAtomicT>(*val), ptr.get()); + *_t = std::move(ptr); + } + } +}; + +template +struct is_atomic> { + using Type = std::remove_cvref_t; + + static constexpr bool value = is_atomic::value; + using RemoveAtomicT = Ref::RemoveAtomicT>; + static void set(RemoveAtomicT&& val, Ref* _t) { + auto ref = Ref::make(); + is_atomic::set( + std::forward::RemoveAtomicT>(*val), &(*ref)); + *_t = std::move(ref); + } +}; + +template +struct is_atomic> { + using Type = std::remove_cvref_t; + static constexpr bool value = is_atomic::value; + using RemoveAtomicT = Box::RemoveAtomicT>; + static void set(RemoveAtomicT&& val, Box* _t) { + auto box = Box::make(); + is_atomic::set( + std::forward::RemoveAtomicT>(*val), &(*box)); + *_t = std::move(box); + } +}; + +template +struct is_atomic> { + static constexpr bool value = + (is_atomic::value || ...); + + using RemoveAtomicT = NamedTuple< + rfl::Field::RemoveAtomicT>...>; + + static void set(RemoveAtomicT&& val, NamedTuple* _t) { + (is_atomic::set( + std::forward::RemoveAtomicT>( + val.template get()), + &(_t->template get())), + ...); + } +}; + +template + requires(std::is_class_v && std::is_aggregate_v && + !std::is_move_constructible_v) +struct is_atomic { + static constexpr bool value = is_atomic>::value; + + using RemoveAtomicT = typename is_atomic>::RemoveAtomicT; + + static void set(RemoveAtomicT&& val, T* _t) { + using Fields = typename named_tuple_t::Fields; + + const auto view = to_view(*_t); + + const auto set_field = [&](std::integral_constant) { + using FieldType = typename rfl::tuple_element_t<_i, Fields>::Type; + using FieldRemoveAtomicT = + typename is_atomic>::RemoveAtomicT; + + is_atomic>::set( + std::forward(val.template get<_i>()), + view.template get<_i>()); + }; + + constexpr size_t num_fields = std::remove_cvref_t::size(); + + [&](std::index_sequence<_is...>) { + (set_field(std::integral_constant{}), ...); + }(std::make_index_sequence{}); + } +}; + +template + requires(std::is_class_v && std::is_aggregate_v && + std::is_move_constructible_v) +struct is_atomic { + static constexpr bool value = false; + using RemoveAtomicT = T; + static void set(RemoveAtomicT&& val, T* _t) { *_t = std::forward(val); }; +}; + +template +constexpr bool is_atomic_v = is_atomic>::value; + +} // namespace rfl::atomic + +#endif diff --git a/include/rfl/atomic/remove_atomic_t.hpp b/include/rfl/atomic/remove_atomic_t.hpp new file mode 100644 index 00000000..e875eda7 --- /dev/null +++ b/include/rfl/atomic/remove_atomic_t.hpp @@ -0,0 +1,16 @@ +#ifndef RFL_ATOMIC_REMOVE_ATOMIC_T_HPP_ +#define RFL_ATOMIC_REMOVE_ATOMIC_T_HPP_ + +#include + +#include "is_atomic.hpp" + +namespace rfl::atomic { + +template +using remove_atomic_t = + typename is_atomic>::RemoveAtomicT; + +} // namespace rfl::atomic + +#endif diff --git a/include/rfl/atomic/set_atomic.hpp b/include/rfl/atomic/set_atomic.hpp new file mode 100644 index 00000000..958c3787 --- /dev/null +++ b/include/rfl/atomic/set_atomic.hpp @@ -0,0 +1,24 @@ +#ifndef RFL_ATOMIC_SET_ATOMIC_HPP_ +#define RFL_ATOMIC_SET_ATOMIC_HPP_ + +#include + +#include "../Result.hpp" +#include "is_atomic.hpp" + +namespace rfl::atomic { + +template +Nothing set_atomic(U&& val, T* _t) { + is_atomic>::set(std::forward(val), _t); + return Nothing{}; +} + +template +Nothing set_atomic(U&& val, T& _t) { + return set_atomic(std::forward(val), &_t); +} + +} // namespace rfl::atomic + +#endif diff --git a/include/rfl/parsing/Parser_box.hpp b/include/rfl/parsing/Parser_box.hpp index 56bf25ac..ad8cb3f7 100644 --- a/include/rfl/parsing/Parser_box.hpp +++ b/include/rfl/parsing/Parser_box.hpp @@ -6,11 +6,13 @@ #include "../Box.hpp" #include "../Result.hpp" +#include "../atomic/is_atomic.hpp" +#include "../atomic/remove_atomic_t.hpp" +#include "../atomic/set_atomic.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" -namespace rfl { -namespace parsing { +namespace rfl::parsing { template requires AreReaderAndWriter> @@ -19,11 +21,23 @@ struct Parser, ProcessorsType> { static Result> read(const R& _r, const InputVarType& _var) noexcept { - const auto to_box = [](auto&& _t) { - return Box::make(std::move(_t)); - }; - return Parser, ProcessorsType>::read(_r, _var) - .transform(to_box); + if constexpr (atomic::is_atomic_v) { + using RemoveAtomicT = atomic::remove_atomic_t; + return Parser::read(_r, _var) + .transform([](auto&& _t) { + auto atomic_box = Box::make(); + atomic::set_atomic(std::move(_t), &(*atomic_box)); + return atomic_box; + }); + + } else { + const auto to_box = [](auto&& _t) { + return Box::make(std::move(_t)); + }; + return Parser, ProcessorsType>::read(_r, + _var) + .transform(to_box); + } } template @@ -39,7 +53,6 @@ struct Parser, ProcessorsType> { } }; -} // namespace parsing -} // namespace rfl +} // namespace rfl::parsing #endif diff --git a/include/rfl/parsing/Parser_ref.hpp b/include/rfl/parsing/Parser_ref.hpp index 452fef55..cbd23755 100644 --- a/include/rfl/parsing/Parser_ref.hpp +++ b/include/rfl/parsing/Parser_ref.hpp @@ -7,11 +7,13 @@ #include "../Ref.hpp" #include "../Result.hpp" #include "../always_false.hpp" +#include "../atomic/is_atomic.hpp" +#include "../atomic/remove_atomic_t.hpp" +#include "../atomic/set_atomic.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" -namespace rfl { -namespace parsing { +namespace rfl::parsing { template requires AreReaderAndWriter> @@ -19,9 +21,23 @@ struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; static Result> read(const R& _r, const InputVarType& _var) noexcept { - const auto to_ref = [&](auto&& _t) { return Ref::make(std::move(_t)); }; - return Parser, ProcessorsType>::read(_r, _var) - .transform(to_ref); + if constexpr (atomic::is_atomic_v) { + using RemoveAtomicT = atomic::remove_atomic_t; + return Parser::read(_r, _var) + .transform([](auto&& _t) { + auto atomic_ref = Ref::make(); + atomic::set_atomic(std::move(_t), &(*atomic_ref)); + return atomic_ref; + }); + + } else { + const auto to_ref = [&](auto&& _t) { + return Ref::make(std::move(_t)); + }; + return Parser, ProcessorsType>::read(_r, + _var) + .transform(to_ref); + } } template @@ -37,7 +53,6 @@ struct Parser, ProcessorsType> { } }; -} // namespace parsing -} // namespace rfl +} // namespace rfl::parsing #endif diff --git a/include/rfl/parsing/Parser_shared_ptr.hpp b/include/rfl/parsing/Parser_shared_ptr.hpp index e7dcba02..5b5e53f4 100644 --- a/include/rfl/parsing/Parser_shared_ptr.hpp +++ b/include/rfl/parsing/Parser_shared_ptr.hpp @@ -8,6 +8,9 @@ #include "../Ref.hpp" #include "../Result.hpp" #include "../always_false.hpp" +#include "../atomic/is_atomic.hpp" +#include "../atomic/remove_atomic_t.hpp" +#include "../atomic/set_atomic.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" @@ -26,7 +29,19 @@ struct Parser, ProcessorsType> { static Result> read(const R& _r, const InputVarType& _var) noexcept { - if constexpr (schemaful::IsSchemafulReader) { + if constexpr (atomic::is_atomic_v) { + using RemoveAtomicT = std::shared_ptr>; + return Parser::read(_r, _var) + .transform([](auto&& _t) { + if (!_t) { + return std::shared_ptr(); + } + auto atomic_shared_ptr = std::make_shared(); + atomic::set_atomic(std::move(*_t), atomic_shared_ptr.get()); + return atomic_shared_ptr; + }); + + } else if constexpr (schemaful::IsSchemafulReader) { using S = schemaful::SharedPtrReader, ProcessorsType>; const auto to_shared = [&](const auto& _u) -> Result> { diff --git a/include/rfl/parsing/Parser_unique_ptr.hpp b/include/rfl/parsing/Parser_unique_ptr.hpp index 7c26a464..d7e9f2b5 100644 --- a/include/rfl/parsing/Parser_unique_ptr.hpp +++ b/include/rfl/parsing/Parser_unique_ptr.hpp @@ -8,6 +8,9 @@ #include "../Ref.hpp" #include "../Result.hpp" #include "../always_false.hpp" +#include "../atomic/is_atomic.hpp" +#include "../atomic/remove_atomic_t.hpp" +#include "../atomic/set_atomic.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" @@ -15,8 +18,7 @@ #include "schemaful/IsSchemafulWriter.hpp" #include "schemaful/UniquePtrReader.hpp" -namespace rfl { -namespace parsing { +namespace rfl ::parsing { template requires AreReaderAndWriter> @@ -27,7 +29,19 @@ struct Parser, ProcessorsType> { static Result> read(const R& _r, const InputVarType& _var) noexcept { - if constexpr (schemaful::IsSchemafulReader) { + if constexpr (atomic::is_atomic_v) { + using RemoveAtomicT = std::unique_ptr>; + return Parser::read(_r, _var) + .transform([](auto&& _t) { + if (!_t) { + return std::unique_ptr(); + } + auto atomic_unique_ptr = std::make_unique(); + atomic::set_atomic(std::move(*_t), atomic_unique_ptr.get()); + return atomic_unique_ptr; + }); + + } else if constexpr (schemaful::IsSchemafulReader) { using S = schemaful::UniquePtrReader, ProcessorsType>; const auto to_unique = [&](const auto& _u) -> Result> { @@ -75,7 +89,6 @@ struct Parser, ProcessorsType> { } }; -} // namespace parsing -} // namespace rfl +} // namespace rfl::parsing #endif diff --git a/tests/json/test_atomic.cpp b/tests/json/test_atomic.cpp index 8a87c6db..fbabb518 100644 --- a/tests/json/test_atomic.cpp +++ b/tests/json/test_atomic.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,28 +11,35 @@ namespace test_atomic { struct Stats { std::atomic bytes_downloaded; std::atomic finished; - - Stats(Stats&& other) noexcept - : bytes_downloaded( - other.bytes_downloaded.load(std::memory_order_relaxed)), - finished(other.finished.load(std::memory_order_relaxed)) {} - - Stats& operator=(Stats&& other) noexcept { - bytes_downloaded.store( - other.bytes_downloaded.load(std::memory_order_relaxed), - std::memory_order_relaxed); - finished.store(other.finished.load(std::memory_order_relaxed), - std::memory_order_relaxed); - return *this; - } - - Stats(const std::uint64_t _bytes_downloaded, const bool _finished) - : bytes_downloaded(_bytes_downloaded), finished(_finished) {} + rfl::Ref> ref_atomic_int; + rfl::Box> box_atomic_int; + std::shared_ptr> shared_atomic_int; + std::unique_ptr> unique_atomic_int; }; TEST(json, test_atomic) { - auto stats = Stats(123456789, true); - - write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); + auto stats = + Stats{.bytes_downloaded = 123456789, + .finished = true, + .ref_atomic_int = rfl::make_ref>(42), + .box_atomic_int = rfl::Box>::make(7), + .shared_atomic_int = std::make_shared>(13), + .unique_atomic_int = std::make_unique>(21)}; + + const auto json_str = rfl::json::write(stats); + + Stats stats2{}; + + const auto res = + rfl::json::read>(json_str).transform( + [&](auto&& t) { + return rfl::atomic::set_atomic(std::move(t), &stats2); + }); + + ASSERT_TRUE(res.has_value()) << res.error().what(); + EXPECT_EQ(rfl::json::write(stats2), json_str); + EXPECT_EQ( + json_str, + R"({"bytes_downloaded":123456789,"finished":true,"ref_atomic_int":42,"box_atomic_int":7,"shared_atomic_int":13,"unique_atomic_int":21})"); } } // namespace test_atomic diff --git a/tests/json/test_atomic_array.cpp b/tests/json/test_atomic_array.cpp new file mode 100644 index 00000000..c25c2b7a --- /dev/null +++ b/tests/json/test_atomic_array.cpp @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_atomic_array { + +struct Stats { + std::atomic bytes_downloaded; + std::atomic finished; +}; + +TEST(json, test_atomic_array) { + auto arr = rfl::Ref>{}; + (*arr)[0].bytes_downloaded = 123456789; + (*arr)[0].finished = true; + (*arr)[1].bytes_downloaded = 987654321; + (*arr)[1].finished = false; + + write_and_read( + arr, + R"([{"bytes_downloaded":123456789,"finished":true},{"bytes_downloaded":987654321,"finished":false}])"); +} +} // namespace test_atomic_array diff --git a/tests/json/test_atomic_box.cpp b/tests/json/test_atomic_box.cpp new file mode 100644 index 00000000..f07fb6d0 --- /dev/null +++ b/tests/json/test_atomic_box.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_atomic_ref { + +struct Stats { + std::atomic bytes_downloaded; + std::atomic finished; +}; + +TEST(json, test_atomic_ref) { + auto stats = rfl::make_ref(123456789, true); + + write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); +} +} // namespace test_atomic_ref diff --git a/tests/json/test_atomic_ref.cpp b/tests/json/test_atomic_ref.cpp new file mode 100644 index 00000000..41dbb604 --- /dev/null +++ b/tests/json/test_atomic_ref.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_atomic_box { + +struct Stats { + std::atomic bytes_downloaded; + std::atomic finished; +}; + +TEST(json, test_atomic_box) { + auto stats = rfl::make_box(123456789, true); + + write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); +} +} // namespace test_atomic_box diff --git a/tests/json/test_atomic_shared_ptr.cpp b/tests/json/test_atomic_shared_ptr.cpp new file mode 100644 index 00000000..f2ac2a68 --- /dev/null +++ b/tests/json/test_atomic_shared_ptr.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_atomic_shared_ptr { + +struct Stats { + std::atomic bytes_downloaded; + std::atomic finished; +}; + +TEST(json, test_atomic_shared_ptr) { + auto stats = std::make_shared(123456789, true); + + write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); +} +} // namespace test_atomic_shared_ptr diff --git a/tests/json/test_atomic_unique_ptr.cpp b/tests/json/test_atomic_unique_ptr.cpp new file mode 100644 index 00000000..1560625c --- /dev/null +++ b/tests/json/test_atomic_unique_ptr.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_atomic_unique_ptr { + +struct Stats { + std::atomic bytes_downloaded; + std::atomic finished; +}; + +TEST(json, test_atomic_unique_ptr) { + auto stats = std::make_unique(123456789, true); + + write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); +} +} // namespace test_atomic_unique_ptr From c7ab9255698a35e64a64cb23ca2866fe940e124a Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 18 Jan 2026 17:00:11 +0100 Subject: [PATCH 3/5] Removed unnecessary overload --- include/rfl/atomic/is_atomic.hpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/rfl/atomic/is_atomic.hpp b/include/rfl/atomic/is_atomic.hpp index 132630b7..f95cb361 100644 --- a/include/rfl/atomic/is_atomic.hpp +++ b/include/rfl/atomic/is_atomic.hpp @@ -174,15 +174,6 @@ struct is_atomic { } }; -template - requires(std::is_class_v && std::is_aggregate_v && - std::is_move_constructible_v) -struct is_atomic { - static constexpr bool value = false; - using RemoveAtomicT = T; - static void set(RemoveAtomicT&& val, T* _t) { *_t = std::forward(val); }; -}; - template constexpr bool is_atomic_v = is_atomic>::value; From 731e0f0b3a01297556bc61101a4b6851b8a0228f Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 18 Jan 2026 22:05:26 +0100 Subject: [PATCH 4/5] Removed overloads that were no longer needed --- include/rfl/atomic/is_atomic.hpp | 61 -------------------------------- 1 file changed, 61 deletions(-) diff --git a/include/rfl/atomic/is_atomic.hpp b/include/rfl/atomic/is_atomic.hpp index f95cb361..d4349249 100644 --- a/include/rfl/atomic/is_atomic.hpp +++ b/include/rfl/atomic/is_atomic.hpp @@ -64,67 +64,6 @@ struct is_atomic { } }; -template -struct is_atomic> { - using Type = std::remove_cvref_t; - - static constexpr bool value = is_atomic::value; - using RemoveAtomicT = - std::shared_ptr::RemoveAtomicT>; - static void set(RemoveAtomicT&& val, std::shared_ptr* _t) { - if (val) { - auto ptr = std::make_shared(); - is_atomic::set( - std::forward::RemoveAtomicT>(*val), ptr.get()); - *_t = std::move(ptr); - } - } -}; - -template -struct is_atomic> { - using Type = std::remove_cvref_t; - - static constexpr bool value = is_atomic::value; - using RemoveAtomicT = - std::unique_ptr::RemoveAtomicT>; - static void set(RemoveAtomicT&& val, std::unique_ptr* _t) { - if (val) { - auto ptr = std::make_unique(); - is_atomic::set( - std::forward::RemoveAtomicT>(*val), ptr.get()); - *_t = std::move(ptr); - } - } -}; - -template -struct is_atomic> { - using Type = std::remove_cvref_t; - - static constexpr bool value = is_atomic::value; - using RemoveAtomicT = Ref::RemoveAtomicT>; - static void set(RemoveAtomicT&& val, Ref* _t) { - auto ref = Ref::make(); - is_atomic::set( - std::forward::RemoveAtomicT>(*val), &(*ref)); - *_t = std::move(ref); - } -}; - -template -struct is_atomic> { - using Type = std::remove_cvref_t; - static constexpr bool value = is_atomic::value; - using RemoveAtomicT = Box::RemoveAtomicT>; - static void set(RemoveAtomicT&& val, Box* _t) { - auto box = Box::make(); - is_atomic::set( - std::forward::RemoveAtomicT>(*val), &(*box)); - *_t = std::move(box); - } -}; - template struct is_atomic> { static constexpr bool value = From 9a5739206fc9fb129db5d6582690fb9e8bc5f4ff Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 18 Jan 2026 22:27:42 +0100 Subject: [PATCH 5/5] Added support for atomic flags --- include/rfl/atomic/is_atomic.hpp | 24 ++++++++++---- include/rfl/parsing/Parser.hpp | 1 + include/rfl/parsing/Parser_atomic.hpp | 6 +--- include/rfl/parsing/Parser_atomic_flag.hpp | 38 ++++++++++++++++++++++ tests/json/test_atomic.cpp | 4 ++- tests/json/test_atomic_array.cpp | 5 ++- tests/json/test_atomic_box.cpp | 8 ++--- tests/json/test_atomic_ref.cpp | 8 ++--- 8 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 include/rfl/parsing/Parser_atomic_flag.hpp diff --git a/include/rfl/atomic/is_atomic.hpp b/include/rfl/atomic/is_atomic.hpp index d4349249..93312de2 100644 --- a/include/rfl/atomic/is_atomic.hpp +++ b/include/rfl/atomic/is_atomic.hpp @@ -3,12 +3,9 @@ #include #include -#include #include -#include "../Box.hpp" #include "../NamedTuple.hpp" -#include "../Ref.hpp" #include "../Tuple.hpp" #include "../named_tuple_t.hpp" #include "../to_view.hpp" @@ -34,6 +31,19 @@ struct is_atomic> { }; }; +template <> +struct is_atomic { + static constexpr bool value = true; + using RemoveAtomicT = bool; + static void set(RemoveAtomicT&& val, std::atomic_flag* _t) { + if (val) { + _t->test_and_set(std::memory_order_relaxed); + } else { + _t->clear(std::memory_order_relaxed); + } + } +}; + template struct is_atomic> { using Type = std::remove_cvref_t; @@ -58,7 +68,7 @@ struct is_atomic { static void set(RemoveAtomicT&& val, T (*_t)[N]) { for (size_t i = 0; i < N; ++i) { is_atomic::set( - std::forward::RemoveAtomicT>(val[i]), + std::forward::RemoveAtomicT>(val[i]), &((*_t)[i])); } } @@ -75,7 +85,8 @@ struct is_atomic> { static void set(RemoveAtomicT&& val, NamedTuple* _t) { (is_atomic::set( - std::forward::RemoveAtomicT>( + std::forward>::RemoveAtomicT>( val.template get()), &(_t->template get())), ...); @@ -83,8 +94,7 @@ struct is_atomic> { }; template - requires(std::is_class_v && std::is_aggregate_v && - !std::is_move_constructible_v) + requires(std::is_class_v && std::is_aggregate_v) struct is_atomic { static constexpr bool value = is_atomic>::value; diff --git a/include/rfl/parsing/Parser.hpp b/include/rfl/parsing/Parser.hpp index ae5c92c5..4f1cb355 100644 --- a/include/rfl/parsing/Parser.hpp +++ b/include/rfl/parsing/Parser.hpp @@ -3,6 +3,7 @@ #include "Parser_array.hpp" #include "Parser_atomic.hpp" +#include "Parser_atomic_flag.hpp" #include "Parser_base.hpp" #include "Parser_basic_type.hpp" #include "Parser_box.hpp" diff --git a/include/rfl/parsing/Parser_atomic.hpp b/include/rfl/parsing/Parser_atomic.hpp index f8250912..9bc3e4de 100644 --- a/include/rfl/parsing/Parser_atomic.hpp +++ b/include/rfl/parsing/Parser_atomic.hpp @@ -18,11 +18,7 @@ template struct Parser, ProcessorsType> { using InputVarType = typename R::InputVarType; - using ParentType = Parent; - - static Result read(const R& _r, const InputVarType& _var) noexcept { - return Parser, ProcessorsType>::read(_r, _var); - } + /// Read is not supported for atomic types - we must used rfl::atomic instead. template static void write(const W& _w, const std::atomic& _a, const P& _parent) { diff --git a/include/rfl/parsing/Parser_atomic_flag.hpp b/include/rfl/parsing/Parser_atomic_flag.hpp new file mode 100644 index 00000000..1cbb4e0f --- /dev/null +++ b/include/rfl/parsing/Parser_atomic_flag.hpp @@ -0,0 +1,38 @@ +#ifndef RFL_PARSING_PARSER_ATOMIC_FLAG_HPP_ +#define RFL_PARSING_PARSER_ATOMIC_FLAG_HPP_ + +#include +#include +#include + +#include "../DefaultVal.hpp" +#include "AreReaderAndWriter.hpp" +#include "Parent.hpp" +#include "Parser_base.hpp" +#include "schema/Type.hpp" + +namespace rfl::parsing { + +template + requires AreReaderAndWriter +struct Parser { + using InputVarType = typename R::InputVarType; + + /// Read is not supported for atomic types - we must used rfl::atomic instead. + + template + static void write(const W& _w, const std::atomic_flag& _a, const P& _parent) { + Parser::write( + _w, _a.test(std::memory_order_relaxed), _parent); + } + + static schema::Type to_schema( + std::map* _definitions) { + return schema::Type{ + Parser::to_schema(_definitions)}; + } +}; + +} // namespace rfl::parsing + +#endif diff --git a/tests/json/test_atomic.cpp b/tests/json/test_atomic.cpp index fbabb518..0ff15d82 100644 --- a/tests/json/test_atomic.cpp +++ b/tests/json/test_atomic.cpp @@ -11,6 +11,7 @@ namespace test_atomic { struct Stats { std::atomic bytes_downloaded; std::atomic finished; + std::atomic_flag atomic_flag; rfl::Ref> ref_atomic_int; rfl::Box> box_atomic_int; std::shared_ptr> shared_atomic_int; @@ -21,6 +22,7 @@ TEST(json, test_atomic) { auto stats = Stats{.bytes_downloaded = 123456789, .finished = true, + .atomic_flag = ATOMIC_FLAG_INIT, .ref_atomic_int = rfl::make_ref>(42), .box_atomic_int = rfl::Box>::make(7), .shared_atomic_int = std::make_shared>(13), @@ -40,6 +42,6 @@ TEST(json, test_atomic) { EXPECT_EQ(rfl::json::write(stats2), json_str); EXPECT_EQ( json_str, - R"({"bytes_downloaded":123456789,"finished":true,"ref_atomic_int":42,"box_atomic_int":7,"shared_atomic_int":13,"unique_atomic_int":21})"); + R"({"bytes_downloaded":123456789,"finished":true,"atomic_flag":false,"ref_atomic_int":42,"box_atomic_int":7,"shared_atomic_int":13,"unique_atomic_int":21})"); } } // namespace test_atomic diff --git a/tests/json/test_atomic_array.cpp b/tests/json/test_atomic_array.cpp index c25c2b7a..eb9678f2 100644 --- a/tests/json/test_atomic_array.cpp +++ b/tests/json/test_atomic_array.cpp @@ -10,17 +10,20 @@ namespace test_atomic_array { struct Stats { std::atomic bytes_downloaded; std::atomic finished; + std::atomic_flag atomic_flag = ATOMIC_FLAG_INIT; }; TEST(json, test_atomic_array) { auto arr = rfl::Ref>{}; (*arr)[0].bytes_downloaded = 123456789; (*arr)[0].finished = true; + (*arr)[0].atomic_flag.test_and_set(); (*arr)[1].bytes_downloaded = 987654321; (*arr)[1].finished = false; + (*arr)[1].atomic_flag.clear(); write_and_read( arr, - R"([{"bytes_downloaded":123456789,"finished":true},{"bytes_downloaded":987654321,"finished":false}])"); + R"([{"bytes_downloaded":123456789,"finished":true,"atomic_flag":true},{"bytes_downloaded":987654321,"finished":false,"atomic_flag":false}])"); } } // namespace test_atomic_array diff --git a/tests/json/test_atomic_box.cpp b/tests/json/test_atomic_box.cpp index f07fb6d0..41dbb604 100644 --- a/tests/json/test_atomic_box.cpp +++ b/tests/json/test_atomic_box.cpp @@ -5,16 +5,16 @@ #include "write_and_read.hpp" -namespace test_atomic_ref { +namespace test_atomic_box { struct Stats { std::atomic bytes_downloaded; std::atomic finished; }; -TEST(json, test_atomic_ref) { - auto stats = rfl::make_ref(123456789, true); +TEST(json, test_atomic_box) { + auto stats = rfl::make_box(123456789, true); write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); } -} // namespace test_atomic_ref +} // namespace test_atomic_box diff --git a/tests/json/test_atomic_ref.cpp b/tests/json/test_atomic_ref.cpp index 41dbb604..f07fb6d0 100644 --- a/tests/json/test_atomic_ref.cpp +++ b/tests/json/test_atomic_ref.cpp @@ -5,16 +5,16 @@ #include "write_and_read.hpp" -namespace test_atomic_box { +namespace test_atomic_ref { struct Stats { std::atomic bytes_downloaded; std::atomic finished; }; -TEST(json, test_atomic_box) { - auto stats = rfl::make_box(123456789, true); +TEST(json, test_atomic_ref) { + auto stats = rfl::make_ref(123456789, true); write_and_read(stats, R"({"bytes_downloaded":123456789,"finished":true})"); } -} // namespace test_atomic_box +} // namespace test_atomic_ref