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/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..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 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