Skip to content

"cereal found more than one compatible output serialization function" when using external split load/save functions with explicit version #415

Open
@yuqian90

Description

@yuqian90

Ran into cereal assertion error when trying to use external split load/save functions with explicit version on a subclass which has two constructors, a default constructor and a non-default one.

The non-default constructor seems to have somehow caused some interesting issues. When I did any one of these, the code compiles all okay:

  1. comment out the non-default constructor
  2. Make the non-default constructor explicit like this:
    explicit Derived(const std::string& name)
    ...
  3. remove the explicit version
  4. use serialize() instead of load/save.

If I missed something from the doc, please point out. (the doc just said "cereal requires access to a default constructor for types it serializes", in my case, there is a public default constructor.)

Here's the code to produce this issue:

#include <string>
#include <sstream>
#include <memory>
#include <cassert>
#include <cereal/cereal.hpp>
#include <cereal/archives/binary.hpp>
#include <cereal/types/polymorphic.hpp>

struct Base
{
    virtual ~Base() = default;
    int id = 0;
};

struct Derived: public Base
{
    Derived() = default;

    Derived(const std::string& name): name(name){}

    virtual ~Derived() = default;
    std::string name;
};

namespace cereal{

template<class Archive>
void save(Archive& ar, const Base& obj, const std::uint32_t version)
{
    ar(obj.id);
}

template<class Archive>
void load(Archive& ar, Base& obj, const std::uint32_t version)
{
    ar(obj.id);
}

template<class Archive>
void save(Archive& ar, const Derived& obj, const std::uint32_t version)
{
    ar(base_class<Base>(&obj), obj.name);
}

template<class Archive>
void load(Archive& ar, Derived& obj, const std::uint32_t version)
{
    ar(base_class<Base>(&obj), obj.name);
}

}

CEREAL_REGISTER_TYPE(Derived)

int main(int argc, char* argv[])
{
    using namespace cereal;
    std::ostringstream osm;
    BinaryOutputArchive oarc(osm);
    std::shared_ptr<Derived> obj = std::make_shared<Derived>();
    obj->id = 10;
    obj->name = "test name";
    oarc(std::dynamic_pointer_cast<Base>(obj));

    std::istringstream ism(osm.str());
    BinaryInputArchive iarc(ism);
    std::shared_ptr<Base> obj2;
    iarc(obj2);

    auto actual = std::dynamic_pointer_cast<Derived>(obj2);
    assert(obj->name == actual->name);
    return 0;
}

Here's the compile error:

cereal/include/cereal/cereal.hpp: In instantiation of ‘ArchiveType& cereal::OutputArchive<ArchiveType, Flags>::processImpl(const T&) [with T = std::basic_string<char>; typename cereal::traits::detail::EnableIfHelper<(cereal::traits::has_invalid_output_versioning<T, ArchiveType>::value || ((! cereal::traits::is_output_serializable<T, ArchiveType>::value) && ((!(Flags & AllowEmptyClassElision)) || ((Flags & AllowEmptyClassElision) && (! std::is_empty<T>::value)))))>::type <anonymous> = (cereal::traits::detail::type)0; ArchiveType = cereal::BinaryOutputArchive; unsigned int Flags = 1u]’:
cereal/include/cereal/cereal.hpp:347:9:   required from ‘void cereal::OutputArchive<ArchiveType, Flags>::process(T&&) [with T = std::basic_string<char>&; ArchiveType = cereal::BinaryOutputArchive; unsigned int Flags = 1u]’
cereal/include/cereal/cereal.hpp:249:9:   required from ‘ArchiveType& cereal::OutputArchive<ArchiveType, Flags>::operator()(Types&& ...) [with Types = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >&}; ArchiveType = cereal::BinaryOutputArchive; unsigned int Flags = 1u]’
cereal/include/cereal/details/polymorphic_impl.hpp:545:59:   required from ‘static void cereal::detail::OutputBindingCreator<Archive, T>::writeMetadata(Archive&) [with Archive = cereal::BinaryOutputArchive; T = Derived]’
cereal/include/cereal/details/polymorphic_impl.hpp:621:29:   required from ‘cereal::detail::OutputBindingCreator<Archive, T>::OutputBindingCreator() [with Archive = cereal::BinaryOutputArchive; T = Derived]::__lambda10’
cereal/include/cereal/details/polymorphic_impl.hpp:618:13:   required from ‘struct cereal::detail::OutputBindingCreator<Archive, T>::OutputBindingCreator() [with Archive = cereal::BinaryOutputArchive; T = Derived]::__lambda10’
cereal/include/cereal/details/polymorphic_impl.hpp:617:32:   [ skipping 4 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
cereal/include/cereal/details/polymorphic_impl.hpp:706:96:   required from ‘static void cereal::detail::polymorphic_serialization_support<Archive, T>::instantiate() [with Archive = cereal::BinaryOutputArchive; T = Derived]’
cereal/include/cereal/details/polymorphic_impl.hpp:686:5:   required from ‘struct cereal::detail::polymorphic_serialization_support<cereal::BinaryOutputArchive, Derived>’
cereal/include/cereal/archives/binary.hpp:163:1:   required by substitution of ‘template<class T, class BindingTag> typename cereal::detail::polymorphic_serialization_support<cereal::BinaryOutputArchive, T>::type cereal::detail::instantiate_polymorphic_binding(T*, cereal::BinaryOutputArchive*, BindingTag, cereal::detail::adl_tag) [with T = Derived; BindingTag = cereal::detail::{anonymous}::polymorphic_binding_tag]’
cereal/include/cereal/details/polymorphic_impl.hpp:724:86:   required from ‘void cereal::detail::bind_to_archives<T, Tag>::bind(std::false_type) const [with T = Derived; Tag = cereal::detail::{anonymous}::polymorphic_binding_tag; std::false_type = std::integral_constant<bool, false>]’
cereal/include/cereal/details/polymorphic_impl.hpp:738:37:   required from ‘const cereal::detail::bind_to_archives<T, Tag>& cereal::detail::bind_to_archives<T, Tag>::bind() const [with T = Derived; Tag = cereal::detail::{anonymous}::polymorphic_binding_tag]’
simplecerealtest.cpp:53:1:   required from here
cereal/include/cereal/cereal.hpp:472:9: error: static assertion failed: cereal found more than one compatible output serialization function for the provided type and archive combination. 

 Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these). 
 Use specialization (see access.hpp) if you need to disambiguate between serialize vs load/save functions.  
 Note that serialization functions can be inherited which may lead to the aforementioned ambiguities. 
 In addition, you may not mix versioned with non-versioned serialization functions. 

 
         static_assert(traits::detail::count_output_serializers<T, ArchiveType>::value < 2,

cereal version used is release 1.2.2

Compiler used:

$ c++ --version
c++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions