diff --git a/README.md b/README.md index 0c9b8c8f..f82b555b 100644 --- a/README.md +++ b/README.md @@ -530,7 +530,6 @@ const auto c = C{.f1 = "C++", .f2 = "is", .f4 = "great"}; const auto c2 = rfl::replace(c, a); ``` - ### Support for containers #### C++ standard library @@ -538,6 +537,8 @@ const auto c2 = rfl::replace(c, a); reflect-cpp supports the following containers from the C++ standard library: - `std::array` +- `std::atomic` +- `std::atomic_flag` - `std::deque` - `std::chrono::duration` - `std::filesystem::path` diff --git a/docs/atomic.md b/docs/atomic.md new file mode 100644 index 00000000..88e5ae2a --- /dev/null +++ b/docs/atomic.md @@ -0,0 +1,72 @@ +# Atomic variables (`std::atomic` and `std::atomic_flag`) + +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. + +## Supported atomic types + +- `std::atomic` +- `std::atomic_flag` (serialized as a boolean) +- Arrays of atomic types (std::array and C-style arrays) +- Aggregate types (structs/NamedTuple) containing atomic fields — each atomic field is handled independently + +## Example (writing) + +```cpp +struct Stats { + std::atomic bytes_downloaded; + std::atomic finished; + std::atomic_flag atomic_flag; +}; + +Stats stats{.bytes_downloaded = 123456789, .finished = true, .atomic_flag = ATOMIC_FLAG_INIT}; +const auto json_str = rfl::json::write(stats); +// -> {"bytes_downloaded":123456789,"finished":true,"atomic_flag":false} +``` + +Note: the exact boolean value for `atomic_flag` depends on whether it is set or cleared. + +## Example (reading) + +Reading atomic variables is not quite trivial, because atomic fields cannot be copied or moved. Consider the following example: + +```cpp +// const auto res = rfl::json::read(json_str); +// This will NOT compile because std::atomic is neither copyable nor movable +``` + +There are two ways around this problem: + +### 1. Wrap in `rfl::Ref`, `rfl::Box`, `std::shared_ptr` or `std::unique_ptr` + +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. + +```cpp +const auto res = rfl::json::read>(json_str); +``` + +### 2. Read into a non-atomic representation and then set atomic fields + +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`. + +```cpp +Stats stats; + +const rfl::Result res = + rfl::json::read>(json_str) + .transform([&](auto&& non_atomic_stats) { + return rfl::atomic::set_atomic_fields(non_atomic_stats, &stats); + }); + +if (!res) { + // handle error + std::cerr << "Error reading JSON: " << res.error().what() << std::endl; +} +``` + +## Limitations and notes + +- Structs containing atomic fields must be default-constructible. +- 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). +- The semantics used for setting atomic values use relaxed memory order (`std::memory_order_relaxed`). +- For complex aggregates, `rfl::atomic` will recurse into nested fields and arrays to set atomic members. + diff --git a/docs/docs-readme.md b/docs/docs-readme.md index bf3d6160..813753c0 100644 --- a/docs/docs-readme.md +++ b/docs/docs-readme.md @@ -34,6 +34,10 @@ [rfl::Binary, rfl::Hex and rfl::Oct](number_systems.md)- For expressing numbers in different formats. +[Default values](default_val.md) - For defining default values for fields that might be absent during deserialization. + +[Atomic types](atomic.md) - For serializing and deserializing atomic types. + ## Validation [Regex patterns](patterns.md) - For requiring that strings follow used-defined regex patterns. 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..93312de2 --- /dev/null +++ b/include/rfl/atomic/is_atomic.hpp @@ -0,0 +1,131 @@ +#ifndef RFL_ATOMIC_ISATOMIC_HPP_ +#define RFL_ATOMIC_ISATOMIC_HPP_ + +#include +#include +#include + +#include "../NamedTuple.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 { + 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; + + 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> { + 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) +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 +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/internal/has_default_val_v.hpp b/include/rfl/internal/has_default_val_v.hpp index 6920ae7e..1d19ef96 100644 --- a/include/rfl/internal/has_default_val_v.hpp +++ b/include/rfl/internal/has_default_val_v.hpp @@ -1,5 +1,7 @@ #ifndef RFL_HASDEFAULTVALV_HPP_ #define RFL_HASDEFAULTVALV_HPP_ + +#include #include #include "../NamedTuple.hpp" @@ -11,6 +13,11 @@ namespace rfl::internal { template struct HasDefaultVal; +template +struct HasDefaultVal { + static constexpr bool value = false; +}; + template struct HasDefaultVal> { static constexpr bool value = @@ -20,7 +27,18 @@ struct HasDefaultVal> { }; template -constexpr bool has_default_val_v = HasDefaultVal>::value; + requires(std::is_class_v && std::is_aggregate_v) +struct HasDefaultVal : HasDefaultVal> {}; + +template +struct HasDefaultVal> : HasDefaultVal> { +}; + +template +struct HasDefaultVal : HasDefaultVal> {}; + +template +constexpr bool has_default_val_v = HasDefaultVal>::value; } // namespace rfl::internal diff --git a/include/rfl/parsing/Parser.hpp b/include/rfl/parsing/Parser.hpp index 3bd444b0..4f1cb355 100644 --- a/include/rfl/parsing/Parser.hpp +++ b/include/rfl/parsing/Parser.hpp @@ -2,6 +2,8 @@ #define RFL_PARSING_PARSER_HPP_ #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 new file mode 100644 index 00000000..9bc3e4de --- /dev/null +++ b/include/rfl/parsing/Parser_atomic.hpp @@ -0,0 +1,39 @@ +#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; + + /// 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) { + 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/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/include/rfl/parsing/Parser_box.hpp b/include/rfl/parsing/Parser_box.hpp index 56bf25ac..ab29a981 100644 --- a/include/rfl/parsing/Parser_box.hpp +++ b/include/rfl/parsing/Parser_box.hpp @@ -6,11 +6,14 @@ #include "../Box.hpp" #include "../Result.hpp" +#include "../atomic/is_atomic.hpp" +#include "../atomic/remove_atomic_t.hpp" +#include "../atomic/set_atomic.hpp" +#include "../internal/has_default_val_v.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" -namespace rfl { -namespace parsing { +namespace rfl::parsing { template requires AreReaderAndWriter> @@ -19,11 +22,29 @@ 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; + + static_assert(!internal::has_default_val_v, + "Atomic types cannot be mixed with rfl::DefaultVal"); + static_assert(!ProcessorsType::default_if_missing_, + "Atomic types cannot be mixed with rfl::DefaultIfMissing"); + + 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 +60,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..c9af9b40 100644 --- a/include/rfl/parsing/Parser_ref.hpp +++ b/include/rfl/parsing/Parser_ref.hpp @@ -7,11 +7,14 @@ #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 "../internal/has_default_val_v.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" -namespace rfl { -namespace parsing { +namespace rfl::parsing { template requires AreReaderAndWriter> @@ -19,9 +22,29 @@ 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; + + static_assert(!internal::has_default_val_v, + "Atomic types cannot be mixed with rfl::DefaultVal"); + static_assert(!ProcessorsType::default_if_missing_, + "Atomic types cannot be mixed with rfl::DefaultIfMissing"); + + 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 +60,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..78d21bdd 100644 --- a/include/rfl/parsing/Parser_shared_ptr.hpp +++ b/include/rfl/parsing/Parser_shared_ptr.hpp @@ -8,6 +8,10 @@ #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 "../internal/has_default_val_v.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" @@ -26,13 +30,32 @@ 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>; + + static_assert(!internal::has_default_val_v, + "Atomic types cannot be mixed with rfl::DefaultVal"); + static_assert(!ProcessorsType::default_if_missing_, + "Atomic types cannot be mixed with rfl::DefaultIfMissing"); + + 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> { return _r.template read_union, S>(_u); }; return _r.to_union(_var).and_then(to_shared); + } else { if (_r.is_empty(_var)) { return std::shared_ptr(); diff --git a/include/rfl/parsing/Parser_unique_ptr.hpp b/include/rfl/parsing/Parser_unique_ptr.hpp index 7c26a464..2f65f0dd 100644 --- a/include/rfl/parsing/Parser_unique_ptr.hpp +++ b/include/rfl/parsing/Parser_unique_ptr.hpp @@ -8,6 +8,10 @@ #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 "../internal/has_default_val_v.hpp" #include "Parent.hpp" #include "Parser_base.hpp" #include "schema/Type.hpp" @@ -15,8 +19,7 @@ #include "schemaful/IsSchemafulWriter.hpp" #include "schemaful/UniquePtrReader.hpp" -namespace rfl { -namespace parsing { +namespace rfl ::parsing { template requires AreReaderAndWriter> @@ -27,13 +30,32 @@ 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>; + + static_assert(!internal::has_default_val_v, + "Atomic types cannot be mixed with rfl::DefaultVal"); + static_assert(!ProcessorsType::default_if_missing_, + "Atomic types cannot be mixed with rfl::DefaultIfMissing"); + + 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> { return _r.template read_union, S>(_u); }; return _r.to_union(_var).and_then(to_unique); + } else { if (_r.is_empty(_var)) { return std::unique_ptr(); @@ -75,7 +97,6 @@ struct Parser, ProcessorsType> { } }; -} // namespace parsing -} // namespace rfl +} // namespace rfl::parsing #endif diff --git a/mkdocs.yaml b/mkdocs.yaml index 09127f8b..988c1339 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -128,6 +128,7 @@ nav: - C arrays and inheritance: c_arrays_and_inheritance.md - rfl::Bytestring: bytestring.md - rfl::Binary, rfl::Hex and rfl::Oct: number_systems.md + - std::atomic, std::atomic_flag: atomic.md - Validation: - Regex patterns: patterns.md - Validating numbers: validating_numbers.md diff --git a/tests/json/test_atomic.cpp b/tests/json/test_atomic.cpp new file mode 100644 index 00000000..0ff15d82 --- /dev/null +++ b/tests/json/test_atomic.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +#include "write_and_read.hpp" + +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; + std::unique_ptr> unique_atomic_int; +}; + +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), + .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,"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 new file mode 100644 index 00000000..eb9678f2 --- /dev/null +++ b/tests/json/test_atomic_array.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +#include "write_and_read.hpp" + +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,"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 new file mode 100644 index 00000000..41dbb604 --- /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_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_ref.cpp b/tests/json/test_atomic_ref.cpp new file mode 100644 index 00000000..f07fb6d0 --- /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_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_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