Skip to content

Commit

Permalink
Test XML and streamline output archive
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-sparus committed May 29, 2024
1 parent e187769 commit b7280d5
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 200 deletions.
86 changes: 82 additions & 4 deletions immer/extra/persist/json/json_immer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include <cereal/archives/json.hpp>

#include <boost/hana/functional/id.hpp>
#include <boost/hana.hpp>

/**
* Special types of archives, working with JSON, that support providing extra
Expand All @@ -11,6 +11,8 @@

namespace immer::persist {

namespace detail {

struct blackhole_output_archive
{
void setNextName(const char* name) {}
Expand Down Expand Up @@ -48,6 +50,37 @@ struct blackhole_output_archive
}
};

template <class T>
class error_duplicate_pool_name_found;

inline auto are_type_names_unique(auto type_names)
{
namespace hana = boost::hana;
auto names_set =
hana::fold_left(type_names, hana::make_set(), [](auto set, auto pair) {
return hana::if_(
hana::contains(set, hana::second(pair)),
[](auto pair) {
return error_duplicate_pool_name_found<
decltype(hana::second(pair))>{};
},
[&set](auto pair) {
return hana::insert(set, hana::second(pair));
})(pair);
});
return hana::length(type_names) == hana::length(names_set);
}

template <class Pools>
constexpr bool is_pool_empty()
{
using Result =
decltype(boost::hana::is_empty(boost::hana::keys(Pools{}.storage())));
return Result::value;
}

} // namespace detail

/**
* Adapted from cereal/archives/adapters.hpp
*/
Expand Down Expand Up @@ -85,15 +118,26 @@ class json_immer_output_archive
{
}

void setNextName(const char* name) { previous.setNextName(name); }
~json_immer_output_archive() { finalize(); }

Pools& get_output_pools() & { return pools; }
Pools&& get_output_pools() && { return std::move(pools); }

void finalize()
{
if constexpr (detail::is_pool_empty<Pools>()) {
return;
}

if (finalized) {
return;
}

save_pools_impl();

auto& self = *this;
self(CEREAL_NVP(pools));
finalized = true;
}

template <class T>
Expand Down Expand Up @@ -152,10 +196,46 @@ class json_immer_output_archive
CEREAL_SAVE_FUNCTION_NAME(ar.previous, v);
}

private:
template <class Previous_, class Pools_, class WrapF_>
friend class json_immer_output_archive;

// Recursively serializes the pools but not calling finalize
void save_pools_impl()
{
const auto save_pool = [wrap = wrap](auto pools) {
auto ar =
json_immer_output_archive<detail::blackhole_output_archive,
Pools,
decltype(wrap)>{pools, wrap};
// Do not try to serialize pools again inside of this temporary
// archive
ar.finalized = true;
ar(pools);
return std::move(ar).get_output_pools();
};

using Names = typename Pools::names_t;
using IsUnique = decltype(detail::are_type_names_unique(Names{}));
static_assert(IsUnique::value,
"Pool names for each type must be unique");

auto prev = pools;
while (true) {
// Keep saving pools until everything is saved.
pools = save_pool(std::move(pools));
if (prev == pools) {
break;
}
prev = pools;
}
}

private:
WrapF wrap;
Previous previous;
Pools pools;
bool finalized{false};
};

template <class Previous, class Pools, class WrapF = boost::hana::id_t>
Expand Down Expand Up @@ -183,8 +263,6 @@ class json_immer_input_archive
{
}

void setNextName(const char* name) { previous.setNextName(name); }

Pools& get_input_pools() { return pools; }
const Pools& get_input_pools() const { return pools; }

Expand Down
72 changes: 5 additions & 67 deletions immer/extra/persist/json/json_with_pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ namespace hana = boost::hana;
template <class T>
class error_no_pool_for_the_given_type_check_get_pools_types_function;

template <class T>
class error_duplicate_pool_name_found;

template <class T>
class error_missing_pool_for_type;

Expand Down Expand Up @@ -445,23 +442,6 @@ inline auto generate_output_pools(auto type_names)
return output_pools<Storage, Names>{storage};
}

inline auto are_type_names_unique(auto type_names)
{
auto names_set =
hana::fold_left(type_names, hana::make_set(), [](auto set, auto pair) {
return hana::if_(
hana::contains(set, hana::second(pair)),
[](auto pair) {
return error_duplicate_pool_name_found<
decltype(hana::second(pair))>{};
},
[&set](auto pair) {
return hana::insert(set, hana::second(pair));
})(pair);
});
return hana::length(type_names) == hana::length(names_set);
}

inline auto generate_input_pools(auto type_names)
{
auto storage =
Expand Down Expand Up @@ -493,72 +473,30 @@ auto get_pools_types(const T&)
return boost::hana::make_map();
}

template <class Pools>
constexpr bool is_pool_empty()
{
using Result =
decltype(boost::hana::is_empty(boost::hana::keys(Pools{}.storage())));
return Result::value;
}

// Recursively serializes the pools but not calling finalize
template <class Previous, class Pools, class WrapF, class SavePoolF>
void save_pools_impl(json_immer_output_archive<Previous, Pools, WrapF>& ar,
const SavePoolF& save_pool)
{
using Names = typename Pools::names_t;
using IsUnique = decltype(detail::are_type_names_unique(Names{}));
static_assert(IsUnique::value, "Pool names for each type must be unique");

auto& pools = ar.get_output_pools();

auto prev = pools;
while (true) {
// Keep saving pools until everything is saved.
pools = save_pool(std::move(pools));
if (prev == pools) {
break;
}
prev = pools;
}
}

/**
* Type T must provide a callable free function get_pools_types(const T&).
*/
template <typename T>
auto to_json_with_pool(const T& serializable)
{
auto pools = detail::generate_output_pools(get_pools_types(serializable));
using Pools = std::decay_t<decltype(pools)>;

const auto save_pool = [](auto pools) {
auto ar2 =
json_immer_output_archive<blackhole_output_archive, Pools>{pools};
ar2(pools);
return std::move(ar2).get_output_pools();
};

auto os = std::ostringstream{};
{
auto pools =
detail::generate_output_pools(get_pools_types(serializable));
using Pools = std::decay_t<decltype(pools)>;
auto ar =
immer::persist::json_immer_output_archive<cereal::JSONOutputArchive,
Pools>{os};
ar(serializable);
if constexpr (!is_pool_empty<Pools>()) {
save_pools_impl(ar, save_pool);
ar.finalize();
}
pools = std::move(ar).get_output_pools();
}
return std::make_pair(os.str(), std::move(pools));
return os.str();
}

template <typename Pools, class ReloadPoolF>
auto load_pools(std::istream& is, const ReloadPoolF& reload_pool)
{
auto pools = Pools{};
if constexpr (is_pool_empty<Pools>()) {
if constexpr (detail::is_pool_empty<Pools>()) {
return pools;
}

Expand Down
41 changes: 6 additions & 35 deletions immer/extra/persist/json/json_with_pool_auto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ auto to_json_with_auto_pool(const T& serializable,
const PoolsTypes& pools_types,
const WrapF& wrap = wrap_for_saving)
{
namespace hana = boost::hana;

// In the future, wrap function may ignore certain user-provided types that
// should not be persisted.
static_assert(
Expand All @@ -152,32 +150,18 @@ auto to_json_with_auto_pool(const T& serializable,
persistable<immer::vector<int>>>,
"and a value when it's wrapping");

auto pools = detail::generate_output_pools(pools_types);
using Pools = std::decay_t<decltype(pools)>;

const auto save_pool = [wrap](auto pools) {
auto ar = json_immer_output_archive<blackhole_output_archive,
Pools,
decltype(wrap)>{pools, wrap};
ar(pools);
return std::move(ar).get_output_pools();
};

auto os = std::ostringstream{};
{
auto ar = json_immer_output_archive<cereal::JSONOutputArchive,
auto pools = detail::generate_output_pools(pools_types);
using Pools = std::decay_t<decltype(pools)>;
auto ar = json_immer_output_archive<cereal::JSONOutputArchive,
Pools,
decltype(wrap)>{pools, wrap, os};
// value0 because that's now cereal saves the unnamed object by default,
// maybe change later.
ar(cereal::make_nvp("value0", serializable));
if constexpr (!is_pool_empty<Pools>()) {
save_pools_impl(ar, save_pool);
ar.finalize();
}
pools = std::move(ar).get_output_pools();
}
return std::make_pair(os.str(), std::move(pools));
return os.str();
}

// Same as to_json_with_auto_pool but we don't generate any JSON.
Expand All @@ -188,8 +172,6 @@ auto get_auto_pool(const T& serializable,
const PoolsTypes& pools_types,
const WrapF& wrap = wrap_for_saving)
{
namespace hana = boost::hana;

// In the future, wrap function may ignore certain user-provided types that
// should not be persisted.
static_assert(
Expand All @@ -205,25 +187,14 @@ auto get_auto_pool(const T& serializable,
auto pools = detail::generate_output_pools(pools_types);
using Pools = std::decay_t<decltype(pools)>;

const auto save_pool = [wrap](auto pools) {
auto ar = json_immer_output_archive<blackhole_output_archive,
Pools,
decltype(wrap)>{pools, wrap};
ar(pools);
return std::move(ar).get_output_pools();
};

{
auto ar = json_immer_output_archive<blackhole_output_archive,
auto ar = json_immer_output_archive<detail::blackhole_output_archive,
Pools,
decltype(wrap)>{pools, wrap};
// value0 because that's now cereal saves the unnamed object by default,
// maybe change later.
ar(cereal::make_nvp("value0", serializable));
if constexpr (!is_pool_empty<Pools>()) {
save_pools_impl(ar, save_pool);
ar.finalize();
}
ar.finalize();
pools = std::move(ar).get_output_pools();
}
return pools;
Expand Down
Loading

0 comments on commit b7280d5

Please sign in to comment.