Skip to content

Fatal Issue with Polymorphism Greater Than 2 Levels Deep! #281

@Devacor

Description

@Devacor

I am having a vital breaking problem since pulling the head of develop. I am not sure how long it's been a problem as I haven't pulled in a long time, but I'll share a program that should be relatively minimal and crashes when trying to save (or load if you were able to get save game json it breaks for the same reason.)

It's breaking on the lookup in "downcast". It seems to work when polymorphism is only one level deep. This used to work in older versions and is currently semi-blocking as I need to revert to a much much older version of cereal in order to fix the issue.

  template <class Derived> inline
  static const Derived * downcast( const void * const dptr, std::type_info const & baseInfo )
  {
    auto const * mapping = lookup( baseInfo, typeid(Derived), [&](){ UNREGISTERED_POLYMORPHIC_CAST_EXCEPTION(save) } );
    return static_cast<Derived const *>( mapping->downcast( dptr ) );
  }
#include <iostream>
#include <memory>

#include "cereal/cereal.hpp"
#include "cereal/access.hpp"

struct WorkingBase {
    virtual ~WorkingBase(){}

    template <class Archive>
    void serialize(Archive & archive) {
        archive(CEREAL_NVP(child));
    }

    virtual void call() {
        std::cout << "works1" << std::endl;
    }

    std::shared_ptr<WorkingBase> child;
};

struct WorkingDerived : public WorkingBase {
    friend cereal::access;

    static std::shared_ptr<WorkingBase> make() { return std::shared_ptr<WorkingBase>(new WorkingDerived()); }

    template <class Archive>
    void serialize(Archive & archive) {
        archive(
            cereal::make_nvp("WorkingBase", cereal::base_class<WorkingBase>(this))
        );
    }

    virtual void call() {
        std::cout << "works2" << std::endl;
    }
};

struct BrokenDerivedSecond : public WorkingDerived {
    friend cereal::access;

    static std::shared_ptr<WorkingBase> make() { return std::shared_ptr<WorkingBase>(new BrokenDerivedSecond()); }

    template <class Archive>
    void serialize(Archive & archive) {
        archive(
            cereal::make_nvp("WorkingDerived", cereal::base_class<WorkingDerived>(this))
        );
    }

    virtual void call() {
        std::cout << "works3" << std::endl;
    }
};

#include "cereal/archives/json.hpp"
CEREAL_REGISTER_TYPE(WorkingBase);
CEREAL_REGISTER_TYPE(WorkingDerived);
CEREAL_REGISTER_TYPE(BrokenDerivedSecond);

int main(){
    { //This is a-okay!
        auto parent = WorkingDerived::make();
        parent->child = WorkingDerived::make();
        parent->child->child = WorkingDerived::make();

        std::stringstream stream;

        {
            cereal::JSONOutputArchive archive(stream);
            archive(parent); //Totally fine.
        }

        std::shared_ptr<WorkingBase> loadedResult;
        {
            cereal::JSONInputArchive archive(stream);
            archive(loadedResult);
            if (loadedResult->child) {
                std::cout << "loaded!" << std::endl;
            }
        }
    }
        //THIS IS WHERE IT ALL BREAKS DOWN!
    {
        auto parent = BrokenDerivedSecond::make(); //When we use BrokenDerivedSecond anywhere it causes issues.
        parent->child = BrokenDerivedSecond::make();
        parent->child->child = BrokenDerivedSecond::make();

        std::stringstream stream;

        {
            cereal::JSONOutputArchive archive(stream);
            archive(parent); //CRASH!
        }

        std::shared_ptr<WorkingBase> loadedResult;
        {
            cereal::JSONInputArchive archive(stream);
            archive(loadedResult);
            if (loadedResult->child) {
                std::cout << "loaded!" << std::endl;
            }
        }
    }
}
    KernelBase.dll!RaiseException�()    Unknown
    [External Code] 
    M2tMLibrary.exe!cereal::detail::PolymorphicCasters::downcast::__l2::<lambda>() Line 144 C++
    M2tMLibrary.exe!cereal::detail::PolymorphicCasters::lookup<void <lambda>(void) >(const type_info & baseInfo, const type_info & derivedInfo, cereal::detail::PolymorphicCasters::downcast::__l2::void <lambda>(void) && exceptionFunc) Line 137  C++
    M2tMLibrary.exe!cereal::detail::PolymorphicCasters::downcast<BrokenDerivedSecond>(const void * const dptr, const type_info & baseInfo) Line 144 C++
    M2tMLibrary.exe!cereal::detail::OutputBindingCreator<cereal::JSONOutputArchive,BrokenDerivedSecond>::{ctor}::__l2::<lambda>(void * arptr, const void * dptr, const type_info & baseInfo) Line 449   C++
    [External Code] 
    M2tMLibrary.exe!cereal::save<cereal::JSONOutputArchive,Broken>(cereal::JSONOutputArchive & ar, const std::shared_ptr<Broken> & ptr) Line 364    C++
    M2tMLibrary.exe!cereal::OutputArchive<cereal::JSONOutputArchive,0>::processImpl<std::shared_ptr<Broken>,0>(const std::shared_ptr<Broken> & t) Line 454  C++
    M2tMLibrary.exe!cereal::OutputArchive<cereal::JSONOutputArchive,0>::process<std::shared_ptr<Broken> & __ptr64>(std::shared_ptr<Broken> & head) Line 377 C++
>   M2tMLibrary.exe!cereal::OutputArchive<cereal::JSONOutputArchive,0>::operator()<std::shared_ptr<Broken> & __ptr64>(std::shared_ptr<Broken> & <args_0>) Line 293  C++
    M2tMLibrary.exe!SDL_main(int argc, char * * argv) Line 110  C++
    [External Code] 

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions