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

Compilation fails when deserializing (binary) std::shared_prt<T const> #543

Closed
mabl3 opened this issue Jan 23, 2019 · 2 comments
Closed

Comments

@mabl3
Copy link

mabl3 commented Jan 23, 2019

Hi,

I just discovered cereal and want to transition from Boost. Doing this, I discovered that cereal doesn't seem to be able to handle a special case where Boost did not complain.

I have a std::shared_ptr<T const> amongst my data to save and load. I use binary archives. Everything works fine until saving the data, but when I write the code to load the archive, compilation of my program fails. Here is a minimal example that illustrates the issue:

#include <fstream>
#include <unordered_map>

#include <cereal/access.hpp>
#include <cereal/archives/binary.hpp>
#include <cereal/types/memory.hpp>

class Foo {
public:
    Foo() : foo_{std::make_shared<int>(0)} {}
    int foo() const { return *foo_; }
private:
    friend class cereal::access;
    template <typename Archive>
    void serialize(Archive& archive) {
        archive(foo_);
    }

    std::shared_ptr<int const> foo_;
};



int main() {
    Foo foo;

    {
        std::ofstream os("./foo.cereal", std::ios_base::binary);
        cereal::BinaryOutputArchive oar(os);
        oar(foo);
        os.close();
    }

    {
        std::ifstream is("./foo.cereal", std::ios_base::binary);
        cereal::BinaryInputArchive iar(is);
        iar(bar);
        is.close();
    }
    return 0;
}

Compiling fails with following message:

./lib/cereal/types/memory.hpp:337:7: error: no matching function for call to ‘cereal::BinaryInputArchive::registerSharedPointer(uint32_t&, std::shared_ptr<const int>&)’
       ar.registerSharedPointer( id, ptr );
       ^~
In file included from ./lib/cereal/types/memory.hpp:33,
                 from src/experiment.cpp:14:
./lib/cereal/cereal.hpp:690:19: note: candidate: ‘void cereal::InputArchive<ArchiveType, Flags>::registerSharedPointer(uint32_t, std::shared_ptr<void>) [with ArchiveType = cereal::BinaryInputArchive; unsigned int Flags = 1; uint32_t = unsigned int]’
       inline void registerSharedPointer(std::uint32_t const id, std::shared_ptr<void> ptr)
                   ^~~~~~~~~~~~~~~~~~~~~
./lib/cereal/cereal.hpp:690:19: note:   no known conversion for argument 2 from ‘shared_ptr<const int>’ to ‘shared_ptr<void>’

FYI: I am using cereal v1.2.2 and gcc version 8.2.0 on Ubuntu 18.04

If I change std::shared_ptr<int const> foo_; to std::shared_ptr<int> foo_;, it compiles. However, in my actual project, this is not an option. I actually found a workaround to retain my datastructures:

If I use separate save() and load() functions instead of serialize(), I can load the data to a temporary std::sharet_ptr<T> and then manually copy it to the member:

class Foo {
public:
    Foo() : foo_{std::make_shared<int>(0)} {}
    int foo() const { return *foo_; }
private:
    friend class cereal::access;
    template <typename Archive>
    void save(Archive& archive) const {
        archive(foo_);
    }
    template <typename Archive>
    void load(Archive& archive) {
        std::shared_ptr<int> temp;
        archive(temp);
        foo_ = temp;
    }

    std::shared_ptr<int const> foo_;
};

I applied this workaround in my actual project and it seems to work fine. However, it would be nice if cereal would be able to work with std::shared_ptr<T const> natively.

@shiozaki
Copy link

(Sorry, what I wrote earlier was completely rubbish, please disregard if you saw it).

I figured out how to modify cereal.hpp to realize what you want to do. Again this is not a proper hack but it is working for me for the time being, until the developers will implement a proper version.

{shiozaki@pro cereal}$ diff cereal.hpp cereal_modified.hpp 
690c690
<       inline void registerSharedPointer(std::uint32_t const id, std::shared_ptr<void> ptr)
---
>       inline void registerSharedPointer(std::uint32_t const id, std::shared_ptr<const void> ptr)
693c693
<         itsSharedPointerMap[stripped_id] = ptr;
---
>         itsSharedPointerMap[stripped_id] = std::const_pointer_cast<void>(ptr);
764a765,778
>       template<typename U>
>       ArchiveType & helper_hack(std::shared_ptr<const U>& t) {
>         std::shared_ptr<U> tmp;
>         processImpl(tmp);
>         t = tmp;
>         return *self;
>       }
>       template <class T, class = typename std::enable_if<std::is_convertible<T, std::shared_ptr<const void>>::value
>                                                 and (not std::is_convertible<T, std::shared_ptr<void>>::value)>::type> inline
>       ArchiveType & processImpl(T & t)
>       {
>         return helper_hack(t); 
>       }
> 
777c791,793
<                          !traits::is_specialized<T, ArchiveType>::value))> = traits::sfinae
---
>                          !traits::is_specialized<T, ArchiveType>::value)),                  \
>                        !(std::is_convertible<T, std::shared_ptr<const void>>::value         \
>                         && (!std::is_convertible<T, std::shared_ptr<void>>::value)) > = traits::sfinae

@AzothAmmo
Copy link
Contributor

This is a known issue in the current release of cereal and will be fixed in version 1.2.3 which should be released soon (likely within days of writing this). In the meantime, you can try using the develop branch. See #417 and #423.

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

3 participants