【问题标题】:How to allow to "forget" objets types in boost::serialize如何允许在 boost::serialize 中“忘记”对象类型
【发布时间】:2022-10-13 09:21:55
【问题描述】:

我使用 Boost 来序列化我在register_type 注册的类,如here 所述。

后来,如果我决定某个特定的类不再有用,并且我想打开旧文件并丢弃忘记的类实例,我就没有办法了。

我怎样才能使这项工作?

这是一个例子:

#include <iostream>
#include <sstream>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

struct Base
{
    virtual ~Base() = default;

    template <class Archive>
    void serialize(Archive &ar, long int version)
    {}

    virtual void display(std::ostream &os) const = 0;
};

struct MyType1 : public Base
{
    int i, j;

    MyType1(): MyType1(0, 0) {}
    MyType1(int i_, int j_): i {i_}, j {j_} {}
    ~MyType1() override = default;

    template <class Archive>
    void serialize(Archive &ar, long int version)
    {
        ar & boost::serialization::base_object<Base>(*this);
        ar & i;
        ar & j;
    }

    void display(std::ostream &os) const override
    {
        os << "MyType1{" << i << ", " << j << "}";
    }
};

struct MyType2 : public Base
{
    float a;

    MyType2(): MyType2(0.f) {}
    MyType2(float a_): a {a_} {}
    ~MyType2() override = default;

    template <class Archive>
    void serialize(Archive &ar, long int version)
    {
        ar & boost::serialization::base_object<Base>(*this);
        ar & a;
    }

    void display(std::ostream &os) const override
    {
        os << "MyType2{" << a << '}';
    }
};

std::ostream &operator<<(std::ostream &os, Base const &b)
{
    b.display(os);
    return os;
}

int main()
{
    std::stringstream stream;

    {
        boost::archive::binary_oarchive oar {stream};
        oar.register_type<MyType1>();
        oar.register_type<MyType2>();
        Base *foo1 = new MyType1 {42, 12},
                *foo2 = new MyType2 {32.f};

        oar << foo1 << foo2;
        delete foo1;
        delete foo2;
    }

    boost::archive::binary_iarchive iar {stream};
    
    // Remove a type
    //iar.register_type<MyType1>();

    iar.register_type<MyType2>();
    Base *obj = nullptr;
    iar >> obj;
    // Outputs MyType2{5.88545e-44}
    std::cout << *obj << '\n';

    return 0;
}

【问题讨论】:

    标签: c++ serialization boost


    【解决方案1】:

    您可以忘记类型,但显然您不能再阅读任何包含旧类型的档案。由于这正是您要尝试的,因此它会中断。

    简化示例Live On Coliru

    如果您的存档不包含该类型,您仍然会收到异常,因为您注册类型的方式:Live On Coliru 抛出“未注册的类”。

    注册类型的方式意味着您必须始终匹配注册的顺序和数量。

    导出类

    相反,请考虑使用导出机制:https://www.boost.org/doc/libs/1_80_0/libs/serialization/doc/special.html#export

    这是演示:Live On Coliru

    #include <boost/archive/binary_iarchive.hpp>
    #include <boost/archive/binary_oarchive.hpp>
    #include <boost/serialization/export.hpp>
    #include <boost/serialization/serialization.hpp>
    #include <fstream>
    #include <iostream>
    #include <sstream>
    
    struct Base {
        virtual ~Base() = default;
        virtual void display(std::ostream& os) const = 0;
        void serialize(auto&, unsigned) {}
    };
    
    struct MyType1 : public Base {
        int i, j;
        MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
        void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
        void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
    };
    
    struct MyType2 : public Base {
        float a;
        MyType2(float a_ = 0.f): a {a_} {}
        void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
        void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
    };
    
    BOOST_CLASS_EXPORT(MyType1)
    BOOST_CLASS_EXPORT(MyType2)
    
    static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
    
    int main() {
        std::stringstream ss;
    
        {
            boost::archive::binary_oarchive oa{ss};
            Base* foo1 = new MyType1{42, 12};
            Base* foo2 = new MyType2{32.f};
    
            oa << foo1 << foo2;
            delete foo1;
            delete foo2;
        }
    
        std::ofstream("output.bin", std::ios::binary) << ss.str();
    
        {
            boost::archive::binary_iarchive ia{ss};
    
            Base* obj1 = nullptr;
            Base* obj2 = nullptr;
            ia >> obj1 >> obj2;
    
            std::cout << *obj1 << "
    ";
            std::cout << *obj2 << "
    ";
    
            delete obj2;
            delete obj1;
        }
    }
    

    这负责唯一标识(使用限定类型名称)。因此,当您准备放弃对 old 类的旧档案的支持时,您可以省略它,并且很高兴:Live On Coliru

    #include <boost/archive/binary_iarchive.hpp>
    #include <boost/archive/binary_oarchive.hpp>
    #include <boost/serialization/export.hpp>
    #include <boost/serialization/serialization.hpp>
    #include <fstream>
    #include <iostream>
    #include <sstream>
    
    struct Base {
        virtual ~Base() = default;
        virtual void display(std::ostream& os) const = 0;
        void serialize(auto&, unsigned) {}
    };
    
    struct MyType1 : public Base {
        int i, j;
        MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
        void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
        void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
    };
    
    struct MyType2 : public Base {
        float a;
        MyType2(float a_ = 0.f): a {a_} {}
        void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
        void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
    };
    
    static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
    
    #ifdef OLD_WRITER
        BOOST_CLASS_EXPORT(MyType1)
        BOOST_CLASS_EXPORT(MyType2)
    
        int main() {
            std::ofstream ofs("output.bin", std::ios::binary);
            boost::archive::binary_oarchive oa{ofs};
    
            Base* foo2 = new MyType2{42.f};
            oa << foo2;
            delete foo2;
        }
    #else
        // forgotten: MyType1
        BOOST_CLASS_EXPORT(MyType2)
        int main() {
            std::ifstream ifs("output.bin", std::ios::binary);
            boost::archive::binary_iarchive ia{ifs};
    
            Base* obj2 = nullptr;
            ia >> obj2;
    
            std::cout << *obj2 << "
    ";
            delete obj2;
        }
    #endif
    

    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DOLD_WRITER -o old
    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DNEW_READER -o new
    ./old; xxd output.bin; ./new
    

    印刷

    00000000: 1600 0000 0000 0000 7365 7269 616c 697a  ........serializ
    00000010: 6174 696f 6e3a 3a61 7263 6869 7665 1300  ation::archive..
    00000020: 0408 0408 0100 0000 0000 0700 0000 0000  ................
    00000030: 0000 4d79 5479 7065 3201 0000 0000 0000  ..MyType2.......
    00000040: 0000 0000 0000 0000 0028 42              .........(B
    MyType2{42}
    

    高级:版本控制

    能够也使用显式寄存器类型,但您必须进行版本控制才能获得某种兼容性,而不是UB

    您可以将存档数据包装在一个进行注册并进行版本控制的类中:

    Live On Coliru: V0

    #include <boost/archive/binary_iarchive.hpp>
    #include <boost/archive/binary_oarchive.hpp>
    #include <boost/serialization/serialization.hpp>
    #include <boost/serialization/unique_ptr.hpp>
    #include <boost/serialization/vector.hpp>
    #include <fstream>
    #include <iostream>
    #include <sstream>
    
    struct Base {
        virtual ~Base() = default;
        virtual void display(std::ostream& os) const = 0;
        void serialize(auto&, unsigned) {}
    };
    
    struct MyType1 : public Base {
        int i, j;
        MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
        void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
        void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
    };
    
    struct MyType2 : public Base {
        float a;
        MyType2(float a_ = 0.f): a {a_} {}
        void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
        void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
    };
    
    static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
    
    struct MyArchiveData {
        std::vector<std::unique_ptr<Base>> data;
    
        void serialize(auto& ar, unsigned version) {
            switch (version) {
            case 0: {
                ar.template register_type<MyType1>();
                ar.template register_type<MyType2>();
                ar& data;
                break;
            }
            default:
                using E = boost::archive::archive_exception;
                throw E(E::exception_code::unsupported_class_version);
            }
        }
    };
    
    int main() {
        {
            std::ofstream ofs("output.bin", std::ios::binary);
            boost::archive::binary_oarchive oa{ofs};
    
            MyArchiveData db;
            db.data.emplace_back(new MyType1(42, 12));
            db.data.emplace_back(new MyType2(32.f));
    
            oa << db;
        }
        {
            std::ifstream ifs("output.bin", std::ios::binary);
            boost::archive::binary_iarchive ia{ifs};
    
            MyArchiveData db;
            ia >> db;
    
            for (auto& el : db.data)
                std::cout << *el << "
    ";
        }
    }
    

    请注意我是如何借此机会摆脱原始指针的。

    MyArchiveData V1 简介

    我们声明新的类版本:

    BOOST_CLASS_VERSION(MyArchiveData, 1)
    

    并实现新的逻辑:

    struct MyArchiveData {
        std::vector<std::unique_ptr<Base>> data;
    
        void serialize(auto& ar, unsigned version) {
            switch (version) {
            case 0: {
                ar.template register_type<MyType1>();
                ar.template register_type<MyType2>();
                ar& data;
                break;
            }
    #ifdef V1
            case 1: {
                // MyType1 forgotten!
                ar.template register_type<MyType2>();
                ar& data;
                break;
            }
    #endif
            default:
                using E = boost::archive::archive_exception;
                throw E(E::exception_code::unsupported_class_version);
            }
        }
    };
    

    查看结果Live On Coliru

    #include <boost/archive/binary_iarchive.hpp>
    #include <boost/archive/binary_oarchive.hpp>
    #include <boost/serialization/serialization.hpp>
    #include <boost/serialization/unique_ptr.hpp>
    #include <boost/serialization/vector.hpp>
    #include <boost/serialization/version.hpp>
    #include <fstream>
    #include <iomanip>
    #include <iostream>
    
    struct Base {
        virtual ~Base() = default;
        virtual void display(std::ostream& os) const = 0;
        void serialize(auto&, unsigned) {}
    };
    
    struct MyType1 : public Base {
        int i, j;
        MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
        void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
        void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
    };
    
    struct MyType2 : public Base {
        float a;
        MyType2(float a_ = 0.f): a {a_} {}
        void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
        void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
    };
    
    static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
    
    struct MyArchiveData {
        std::vector<std::unique_ptr<Base>> data;
    
        void serialize(auto& ar, unsigned version) {
            switch (version) {
            case 0: {
                ar.template register_type<MyType1>();
                ar.template register_type<MyType2>();
                ar& data;
                break;
            }
    #ifdef V1
            case 1: {
                // MyType1 forgotten!
                ar.template register_type<MyType2>();
                ar& data;
                break;
            }
    #endif
            default:
                throw std::runtime_error("MyArchiveData: version not supported");
            }
        }
    };
    
    #ifndef V1
        // default class version is 0
        static constexpr bool is_V1 = false;
    #else
        BOOST_CLASS_VERSION(MyArchiveData, 1)
        static constexpr bool is_V1 = true;
    #endif
    
    int main(int argc, char** argv) {
        {
            std::ofstream ofs(is_V1 ? "v1.bin" : "v0.bin", std::ios::binary);
            boost::archive::binary_oarchive oa{ofs};
    
            MyArchiveData db;
            if (!is_V1)
                db.data.emplace_back(new MyType1(42, 12));
            db.data.emplace_back(new MyType2(32.f));
    
            oa << db;
        }
    
        for (auto fname : std::vector(argv + 1, argv + argc)) {
            std::cout << (is_V1?"V1":"V0") << " Reading " << std::quoted(fname) << std::endl;
            std::ifstream ifs(fname, std::ios::binary);
            boost::archive::binary_iarchive ia{ifs};
    
            MyArchiveData db;
            ia >> db;
    
            for (auto& el : db.data)
                std::cout << *el << std::endl;
        }
    }
    

    测试

    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DV0 -o v0
    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DV1 -o v1
    ./v0 v0.bin
    ./v1 v0.bin v1.bin
    # but this isn't going to work:
    ./v0 v1.bin
    

    输出:

    V0 Reading "v0.bin"
    MyType1{42, 12}
    MyType2{32}
    V1 Reading "v0.bin"
    MyType1{42, 12}
    MyType2{32}
    V1 Reading "v1.bin"
    MyType2{32}
    V0 Reading "v1.bin"
    terminate called after throwing an instance of 'std::runtime_error'
      what():  MyArchiveData: version not supported
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-25
      • 1970-01-01
      • 1970-01-01
      • 2014-11-21
      • 2020-05-19
      相关资源
      最近更新 更多