Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cereal implementation for immer::map #105

Open
altschuler opened this issue Apr 8, 2021 · 4 comments
Open

Cereal implementation for immer::map #105

altschuler opened this issue Apr 8, 2021 · 4 comments

Comments

@altschuler
Copy link
Contributor

Is there a cereal serializer (or load/save pair) available for the immer::map type? It seems like there is code for immer::vector, immer::flex_vector etc. in the lager/debug/cereal/ folder, but I cannot find one for map.

I'm asking because adapting the load/save functionality in the todos example is giving a compile error from cereal about missing implementation for the map type.

If it's not available, can someone provide a pointer to how one would go about implementing it? I assume I could adapt the vector/flex_vector code:

namespace cereal {

template <typename Archive,
          typename T,
          typename MP,
          std::uint32_t B,
          std::uint32_t BL>
void CEREAL_SAVE_FUNCTION_NAME(
    Archive& ar, const immer::flex_vector<T, MP, B, BL>& flex_vector)
{
    ar(make_size_tag(static_cast<size_type>(flex_vector.size())));
    for (auto&& v : flex_vector)
        ar(v);
}

template <typename Archive,
          typename T,
          typename MP,
          std::uint32_t B,
          std::uint32_t BL>
void CEREAL_LOAD_FUNCTION_NAME(Archive& ar,
                               immer::flex_vector<T, MP, B, BL>& flex_vector)
{
    size_type size;
    ar(make_size_tag(size));

    auto t = std::move(flex_vector).transient();
    for (auto i = size_type{}; i < size; ++i) {
        T x;
        ar(x);
        t.push_back(std::move(x));
    }
    flex_vector = std::move(t).persistent();

    assert(size == flex_vector.size());
}

} // namespace cereal

My C++ fu is weak so any advice is greatly appreciated! Thanks :)

@arximboldi
Copy link
Owner

arximboldi commented Apr 8, 2021

I use this code.

I could maybe add it to the repo some time. Note that it uses a different shape if the element stored has an attrbute called id, to save some space in the representation. You can remove that part if you don't need it...

#pragma once

#include <cereal/cereal.hpp>

#include <immer/map.hpp>

namespace cereal {

template <typename Element>
auto get_auto_id(const Element& x) -> decltype(x.id)
{
    return x.id;
}

template <typename K, typename T>
struct has_auto_id : std::false_type
{};

template <typename T>
struct has_auto_id<std::decay_t<decltype(get_auto_id(std::declval<T>()))>, T>
    : std::true_type
{};

template <typename Archive,
          typename K,
          typename T,
          typename H,
          typename E,
          typename MP,
          std::uint32_t B>
std::enable_if_t<has_auto_id<K, T>::value>
CEREAL_LOAD_FUNCTION_NAME(Archive& ar, immer::map<K, T, H, E, MP, B>& m)
{
    size_type size;
    ar(make_size_tag(size));

    for (auto i = size_type{}; i < size; ++i) {
        T x;
        ar(x);
        auto id = get_auto_id(x);
        m       = std::move(m).set(std::move(id), std::move(x));
    }
    if (size != m.size())
        throw std::runtime_error{"duplicate ids?"};
}

template <typename Archive,
          typename K,
          typename T,
          typename H,
          typename E,
          typename MP,
          std::uint32_t B>
std::enable_if_t<has_auto_id<K, T>::value>
CEREAL_SAVE_FUNCTION_NAME(Archive& ar, const immer::map<K, T, H, E, MP, B>& m)
{
    ar(make_size_tag(static_cast<size_type>(m.size())));
    for (auto&& v : m)
        ar(v.second);
}

template <typename Archive,
          typename K,
          typename T,
          typename H,
          typename E,
          typename MP,
          std::uint32_t B>
std::enable_if_t<!has_auto_id<K, T>::value>
CEREAL_LOAD_FUNCTION_NAME(Archive& ar, immer::map<K, T, H, E, MP, B>& m)
{
    size_type size;
    ar(make_size_tag(size));

    for (auto i = size_type{}; i < size; ++i) {
        K k;
        T x;
        ar(make_map_item(k, x));
        m = std::move(m).set(std::move(k), std::move(x));
    }
    if (size != m.size())
        throw std::runtime_error{"duplicate ids?"};
}

template <typename Archive,
          typename K,
          typename T,
          typename H,
          typename E,
          typename MP,
          std::uint32_t B>
std::enable_if_t<!has_auto_id<K, T>::value>
CEREAL_SAVE_FUNCTION_NAME(Archive& ar, const immer::map<K, T, H, E, MP, B>& m)
{
    ar(make_size_tag(static_cast<size_type>(m.size())));
    for (auto&& v : m)
        ar(make_map_item(v.first, v.second));
}

} // namespace cereal

@altschuler
Copy link
Contributor Author

Thank you! Works perfectly :) would make sense to have this in the repo, at least from an easy-start point of view. Or maybe in the immer library itself?

@arximboldi
Copy link
Owner

Yes, it probably should!

I have a bunch of Lager related utilities that I often copy from project to project and I've been thinking for some time about what the best way to add them here is. Prob for this one I can just copy it in the lager/debug/cereal folder.

@arximboldi arximboldi reopened this Apr 9, 2021
@arximboldi
Copy link
Owner

I'll reopen this as a reminder :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants