diff --git a/immer/extra/archive/box/archive.hpp b/immer/extra/archive/box/archive.hpp index d4d6ee9a..3e387376 100644 --- a/immer/extra/archive/box/archive.hpp +++ b/immer/extra/archive/box/archive.hpp @@ -143,6 +143,20 @@ loader make_loader_for(const immer::box&, return loader{std::move(ar)}; } +template +auto transform_archive(const archive_load& ar, F&& func) +{ + using U = std::decay_t()))>; + auto boxes = immer::vector>{}; + for (const auto& item : ar.boxes) { + boxes = std::move(boxes).push_back(func(item.get())); + } + + return archive_load{ + .boxes = std::move(boxes), + }; +} + } // namespace immer::archive::box namespace immer::archive { @@ -154,6 +168,13 @@ struct container_traits> using load_archive_t = box::archive_load; using loader_t = box::loader; using container_id = immer::archive::container_id; + + template + static auto transform(F&& func) + { + using U = std::decay_t()))>; + return immer::box{}; + } }; } // namespace immer::archive diff --git a/test/extra/archive/CMakeLists.txt b/test/extra/archive/CMakeLists.txt index 78e726d6..d0aab947 100644 --- a/test/extra/archive/CMakeLists.txt +++ b/test/extra/archive/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable( test_xxhash.cpp test_box.cpp test_conversion.cpp + test_circular_dependency_conversion.cpp ${PROJECT_SOURCE_DIR}/immer/extra/archive/xxhash/xxhash_64.cpp) add_dependencies(tests archive-tests) add_test("test/archive-tests" archive-tests) diff --git a/test/extra/archive/test_circular_dependency_conversion.cpp b/test/extra/archive/test_circular_dependency_conversion.cpp new file mode 100644 index 00000000..0034ee44 --- /dev/null +++ b/test/extra/archive/test_circular_dependency_conversion.cpp @@ -0,0 +1,166 @@ +#include + +#include + +#include "utils.hpp" + +#define DEFINE_OPERATIONS(name) \ + bool operator==(const name& left, const name& right) \ + { \ + return members(left) == members(right); \ + } \ + template \ + void serialize(Archive& ar, name& m) \ + { \ + serialize_members(ar, m); \ + } + +namespace { + +using test::members; +using test::serialize_members; +using test::vector_one; +namespace hana = boost::hana; + +} // namespace + +namespace model { + +struct value_two; + +struct two_boxed +{ + BOOST_HANA_DEFINE_STRUCT(two_boxed, (immer::box, two)); + + two_boxed() = default; + two_boxed(value_two val); +}; + +struct value_one +{ + BOOST_HANA_DEFINE_STRUCT(value_one, // + // (immer::box, box_test) + (vector_one, twos) + + ); +}; + +struct value_two +{ + vector_one ones; +}; + +two_boxed::two_boxed(value_two val) + : two{val} +{ +} + +} // namespace model + +BOOST_HANA_ADAPT_STRUCT(model::value_two, ones); + +namespace model { +DEFINE_OPERATIONS(two_boxed); +DEFINE_OPERATIONS(value_one); +DEFINE_OPERATIONS(value_two); +} // namespace model + +namespace format { + +struct value_two; + +struct two_boxed +{ + BOOST_HANA_DEFINE_STRUCT(two_boxed, (immer::box, two)); + + two_boxed() = default; + two_boxed(value_two val); +}; + +struct value_one +{ + BOOST_HANA_DEFINE_STRUCT(value_one, // + // (immer::box, box_test) + (vector_one, twos) + + ); +}; + +struct value_two +{ + vector_one ones; +}; + +two_boxed::two_boxed(value_two val) + : two{val} +{ +} + +} // namespace format + +BOOST_HANA_ADAPT_STRUCT(format::value_two, ones); + +namespace format { +DEFINE_OPERATIONS(two_boxed); +DEFINE_OPERATIONS(value_one); +DEFINE_OPERATIONS(value_two); +} // namespace format + +TEST_CASE("Test circular dependency archives", "[.broken]") +{ + const auto two1 = model::two_boxed{}; + const auto two2 = model::two_boxed{model::value_two{ + .ones = + { + model::value_one{ + .twos = {two1}, + }, + }, + }}; + const auto value = model::value_one{ + .twos = {two1, two2}, + }; + + const auto names = immer::archive::get_archives_for_types( + hana::tuple_t, + hana::make_map( + + )); + const auto [json_str, model_archives] = + immer::archive::to_json_with_auto_archive(value, names); + // REQUIRE(json_str == ""); + + /** + * NOTE: There is a circular dependency between archives: to convert + * value_one we need to convert value_two and vice versa. + */ + const auto map = hana::make_map( + hana::make_pair( + hana::type_c>, + [](model::two_boxed old, const auto& convert_container) { + SPDLOG_INFO("converting model::two_boxed"); + return format::two_boxed{convert_container( + hana::type_c>, old.two)}; + }), + hana::make_pair( + hana::type_c>, + [](model::value_two old, const auto& convert_container) { + SPDLOG_INFO("converting model::value_two"); + auto ones = convert_container( + hana::type_c>, old.ones); + return format::value_two{ones}; + }), + hana::make_pair( + hana::type_c>, + [](model::value_one old, const auto& convert_container) { + SPDLOG_INFO("converting model::value_one"); + auto twos = convert_container( + hana::type_c>, old.twos); + return format::value_one{twos}; + }) + + ); + const auto format_load_archives = + immer::archive::transform_save_archive(model_archives, map); + (void) format_load_archives; +}