Skip to content

Commit

Permalink
Demonstrate the problem of circular dependency between archives
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-sparus committed Apr 5, 2024
1 parent 24eb893 commit b86a974
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
21 changes: 21 additions & 0 deletions immer/extra/archive/box/archive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,20 @@ loader<T, MemoryPolicy> make_loader_for(const immer::box<T, MemoryPolicy>&,
return loader<T, MemoryPolicy>{std::move(ar)};
}

template <typename T, typename MemoryPolicy, class F>
auto transform_archive(const archive_load<T, MemoryPolicy>& ar, F&& func)
{
using U = std::decay_t<decltype(func(std::declval<T>()))>;
auto boxes = immer::vector<immer::box<U, MemoryPolicy>>{};
for (const auto& item : ar.boxes) {
boxes = std::move(boxes).push_back(func(item.get()));
}

return archive_load<U, MemoryPolicy>{
.boxes = std::move(boxes),
};
}

} // namespace immer::archive::box

namespace immer::archive {
Expand All @@ -154,6 +168,13 @@ struct container_traits<immer::box<T, MemoryPolicy>>
using load_archive_t = box::archive_load<T, MemoryPolicy>;
using loader_t = box::loader<T, MemoryPolicy>;
using container_id = immer::archive::container_id;

template <class F>
static auto transform(F&& func)
{
using U = std::decay_t<decltype(func(std::declval<T>()))>;
return immer::box<U, MemoryPolicy>{};
}
};

} // namespace immer::archive
1 change: 1 addition & 0 deletions test/extra/archive/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
166 changes: 166 additions & 0 deletions test/extra/archive/test_circular_dependency_conversion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include <catch2/catch_test_macros.hpp>

#include <immer/extra/archive/json/json_with_archive_auto.hpp>

#include "utils.hpp"

#define DEFINE_OPERATIONS(name) \
bool operator==(const name& left, const name& right) \
{ \
return members(left) == members(right); \
} \
template <class Archive> \
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<value_two>, two));

two_boxed() = default;
two_boxed(value_two val);
};

struct value_one
{
BOOST_HANA_DEFINE_STRUCT(value_one, //
// (immer::box<std::string>, box_test)
(vector_one<two_boxed>, twos)

);
};

struct value_two
{
vector_one<value_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<value_two>, two));

two_boxed() = default;
two_boxed(value_two val);
};

struct value_one
{
BOOST_HANA_DEFINE_STRUCT(value_one, //
// (immer::box<std::string>, box_test)
(vector_one<two_boxed>, twos)

);
};

struct value_two
{
vector_one<value_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<model::value_one, model::value_two, model::two_boxed>,
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<vector_one<model::two_boxed>>,
[](model::two_boxed old, const auto& convert_container) {
SPDLOG_INFO("converting model::two_boxed");
return format::two_boxed{convert_container(
hana::type_c<immer::box<format::value_two>>, old.two)};
}),
hana::make_pair(
hana::type_c<immer::box<model::value_two>>,
[](model::value_two old, const auto& convert_container) {
SPDLOG_INFO("converting model::value_two");
auto ones = convert_container(
hana::type_c<vector_one<format::value_one>>, old.ones);
return format::value_two{ones};
}),
hana::make_pair(
hana::type_c<vector_one<model::value_one>>,
[](model::value_one old, const auto& convert_container) {
SPDLOG_INFO("converting model::value_one");
auto twos = convert_container(
hana::type_c<vector_one<format::two_boxed>>, old.twos);
return format::value_one{twos};
})

);
const auto format_load_archives =
immer::archive::transform_save_archive(model_archives, map);
(void) format_load_archives;
}

0 comments on commit b86a974

Please sign in to comment.