Boost接口映射的序列化

发布于 2025-01-10 15:01:04 字数 4347 浏览 0 评论 0 原文

我创建了多种不同数据类型的地图。 s64、f64、数组、图像等 为此,我使用了 std::map> 类型的映射;数据库;。 我想存储它,并从文件系统重新加载它。但我听说地图不能用一句话来表达。所以我尝试存储数据部分 std::unique_ptr;首先测试一对:

    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & test;
    }

程序在 ar & 处崩溃。测试抛出异常:“未注册的类 - 未注册或导出的派生类”。 有什么问题吗?我不明白。

这是最小的代码:(已删除)

#include <boost/serialization/vector.hpp>
//....
};

正如 463035818_is_not_a_number 指出的那样,我的狙击不起作用。 我重新创建了它,并且我认为得到了更多。但是一旦我插入从文件加载函数,它就不再编译了: 错误 C2280:“std::pair::pair(const std::pair &)”:Es wurde versucht,auf eine gelöschte Funktion zu verweisen

#include <boost/serialization/vector.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <fstream>
#include <iostream>

class MapEntryInterface {
public:
    MapEntryInterface(std::string type_str) : type(type_str) {}

    std::string type;

    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        if (type == "s64") {
            MapEntryS64* cast = (MapEntryS64*)this;
            cast->serialize(ar, version);
        }
        if (type == "f64") {
            MapEntryF64* cast = (MapEntryF64*)this;
            cast->serialize(ar, version);
        }
    }

};

class MapEntryS64 : public MapEntryInterface {
public:

    MapEntryS64(int init_val, const std::string& type_str)
        : data(init_val), MapEntryInterface(type_str)
    {}

    uint64_t data;
    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & type;
        ar & data;
    }

};

class MapEntryF64 : public MapEntryInterface {
public:

    MapEntryF64(double init_val, const std::string& type_str)
        : data(init_val), MapEntryInterface(type_str)
    {}

    double data;
    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & type;
        ar & data;
    }

};

class MapDataBase {
public:
    MapDataBase()
        //: test(std::unique_ptr<MapEntryInterface>(new MapEntryS64(381, "s64")))
    {
        database["key1"] = std::unique_ptr<MapEntryInterface>(new MapEntryS64(381, "s64"));
        database["key2"] = std::unique_ptr<MapEntryInterface>(new MapEntryF64(3.124512, "f64"));
    };

    bool SaveToFile() {
        std::ofstream ofs("boost_export.dat");
        if (ofs.is_open()) {
            boost::archive::text_oarchive oa(ofs);
            oa & *this;
            return true;
        }
        return false;
    }

    bool loadFromFile() {
        std::ifstream ifs("boost_export.dat");
        if (ifs.is_open())
        {
            try
            {
                boost::archive::text_iarchive ia(ifs);
                ia & *this;
                //std::string yolo;
                //ia >> yolo;
                //ia >> bam;
            }
            catch (std::exception& ex)
            {
                std::cout << ex.what() << std::endl;
                return false;
            }
        }

        return true;
    }

private:

    std::map<std::string, std::unique_ptr<MapEntryInterface>> database;
    //std::unique_ptr<MapEntryInterface> test;

    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & database;
    }

};


void main() {

    MapDataBase tmp;
    tmp.SaveToFile();

    MapDataBase tmp2;
    //tmp2.loadFromFile();

}

I have created a map of multiple different data types. s64, f64, Arrays, Images, etc.
To do so, i used a map of type std::map<std::string, std::unique_ptr<MapEntryInterface>> database;.
I want to store it, and reload it from filesystem. But i heard that maps cant be sored in a one-liner. So i tried to store the data part std::unique_ptr<MapEntryInterface> test; of one pair first:

    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & test;
    }

The program crashes at ar & test throwing an exception: "unregistered class - derived class not registered or exported".
whats the issue? i dont understand it.

here is the minimal code: (deleted)

#include <boost/serialization/vector.hpp>
//....
};

as 463035818_is_not_a_number pointed out, my sniped is not working.
i recreated it, and got a lot further i think. But as soon as i insert the load from file function, it does not compile anymore saying:
error C2280: "std::pair<const _Kty,_Ty>::pair(const std::pair<const _Kty,_Ty> &)" : Es wurde versucht, auf eine gelöschte Funktion zu verweisen

#include <boost/serialization/vector.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <fstream>
#include <iostream>

class MapEntryInterface {
public:
    MapEntryInterface(std::string type_str) : type(type_str) {}

    std::string type;

    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        if (type == "s64") {
            MapEntryS64* cast = (MapEntryS64*)this;
            cast->serialize(ar, version);
        }
        if (type == "f64") {
            MapEntryF64* cast = (MapEntryF64*)this;
            cast->serialize(ar, version);
        }
    }

};

class MapEntryS64 : public MapEntryInterface {
public:

    MapEntryS64(int init_val, const std::string& type_str)
        : data(init_val), MapEntryInterface(type_str)
    {}

    uint64_t data;
    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & type;
        ar & data;
    }

};

class MapEntryF64 : public MapEntryInterface {
public:

    MapEntryF64(double init_val, const std::string& type_str)
        : data(init_val), MapEntryInterface(type_str)
    {}

    double data;
    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & type;
        ar & data;
    }

};

class MapDataBase {
public:
    MapDataBase()
        //: test(std::unique_ptr<MapEntryInterface>(new MapEntryS64(381, "s64")))
    {
        database["key1"] = std::unique_ptr<MapEntryInterface>(new MapEntryS64(381, "s64"));
        database["key2"] = std::unique_ptr<MapEntryInterface>(new MapEntryF64(3.124512, "f64"));
    };

    bool SaveToFile() {
        std::ofstream ofs("boost_export.dat");
        if (ofs.is_open()) {
            boost::archive::text_oarchive oa(ofs);
            oa & *this;
            return true;
        }
        return false;
    }

    bool loadFromFile() {
        std::ifstream ifs("boost_export.dat");
        if (ifs.is_open())
        {
            try
            {
                boost::archive::text_iarchive ia(ifs);
                ia & *this;
                //std::string yolo;
                //ia >> yolo;
                //ia >> bam;
            }
            catch (std::exception& ex)
            {
                std::cout << ex.what() << std::endl;
                return false;
            }
        }

        return true;
    }

private:

    std::map<std::string, std::unique_ptr<MapEntryInterface>> database;
    //std::unique_ptr<MapEntryInterface> test;

    friend class boost::serialization::access;
    template<class Archive>
    void    serialize(Archive& ar, unsigned int version) {
        ar & database;
    }

};


void main() {

    MapDataBase tmp;
    tmp.SaveToFile();

    MapDataBase tmp2;
    //tmp2.loadFromFile();

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寂寞笑我太脆弱 2025-01-17 15:01:04

我花了很多时间让事情变得独立。在众多更改中:

  1. 您不应该让基类序列化派生类(这是经典的 OOP),相反,Boost 希望派生类序列化它们的 base_object (允许静态多态性,顺便说一句,输入注册)

  2. 当然,基类应该序列化其数据成员(类型

  3. 基类应该有一个虚拟析构函数(否则通过unique_ptr删除 的析构函数将是未指定的


    <块引用>

    直到这里:

    类 MapEntryInterface {
      民众:
        虚拟〜MapEntryInterface()=默认;
    
      受保护:
        MapEntryInterface(std::string type_str) : type_(type_str) {}
        std::字符串类型_;
    
        友元类 boost::serialization::access;
        模板<类Ar> void serialize(Ar& ar, unsigned) { ar& }类型_; }
    };
    使用 EntryPtr = std::unique_ptr;
    


  4. 基本/成员初始化列表的顺序具有误导性;无论如何,初始化都是按照声明顺序进行的

  5. type_str 是一种代码味道:OOP 虚拟化的整个思想是在任何地方都可以切换类型。我至少通过默认值来为您做了一半的工作,但您可能可以完全不使用它。毕竟类型就是类型。

  6. 现在添加 base_object 序列化:

    模板  void Serialize(Ar& ar, 无符号)
    {
        ar& boost::序列化::base_object(*this);
        ar&数据_;
    }
    
  7. MapDatabase 受益于多项简化

    • 切勿使用newdelete
    • 之前检查流是多余的,因为您已经处理了异常
    • 因为 loadFromFile 没有有用的方法来处理异常、重新抛出或只是让其转义,
    • 还允许您将 MapDatabase 设置为返回类型,而不是 bool
  8. saveToFileloadFromFile 可能应该采用文件名参数

  9. 未显示:可以说 saveToFileloadFromFile 不需要MapDatabase的一部分

此时,添加一点代码来打印数据库内容:

< strong>在 Wandbox 上直播

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <boost/serialization/map.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/vector.hpp>

#include <fstream>
#include <iostream>

class MapEntryInterface {
  public:
    virtual ~MapEntryInterface() = default;
    virtual void print(std::ostream&) const = 0;

  protected:
    MapEntryInterface(std::string type_str) : type_(type_str) {}
    std::string type_;

    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned) { ar& type_; }

    friend std::ostream& operator<<(std::ostream& os, MapEntryInterface const& e) {
        e.print(os);
        return os;
    }
};

using EntryPtr = std::unique_ptr<MapEntryInterface>;

class MapEntryS64 : public MapEntryInterface {
  public:
    MapEntryS64(int init_val = 0, const std::string& type_str = "s64")
        : MapEntryInterface(type_str)
        , data_(init_val)
    {
    }

  private:
    uint64_t data_;

    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned) {
        ar& boost::serialization::base_object<MapEntryInterface>(*this);
        ar& data_;
    }

    virtual void print(std::ostream& os) const override {
        os << "S64(" << data_ << ", " << std::quoted(type_) << ")";
    }
};

class MapEntryF64 : public MapEntryInterface {
  public:
    MapEntryF64(double init_val = 0, const std::string& type_str = "f64")
        : MapEntryInterface(type_str)
        , data_(init_val)
    {
    }

  private:
    double data_;
    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned)
    {
        ar& boost::serialization::base_object<MapEntryInterface>(*this);
        ar& data_;
    }

    virtual void print(std::ostream& os) const override {
        os << "F64(" << data_ << ", " << std::quoted(type_) << ")";
    }
};

class MapDatabase {
  public:
    using Map = std::map<std::string, EntryPtr>;

    MapDatabase() {
        database_.emplace("key1", std::make_unique<MapEntryS64>(381));
        database_.emplace("key2", std::make_unique<MapEntryF64>(3.124512));
    };

    bool SaveToFile(std::string const& filename) const
    {
        try {
            std::ofstream ofs(filename, std::ios::binary);
            boost::archive::text_oarchive oa(ofs);
            oa << *this;
            return true;
        } catch (std::exception& ex) {
            std::cout << ex.what() << std::endl;
            return false;
        }
    }

    static MapDatabase loadFromFile(std::string const& filename)
    {
        MapDatabase db;
        std::ifstream ifs(filename, std::ios::binary);
        boost::archive::text_iarchive ia(ifs);
        ia >> db;
        return db;
    }

    friend std::ostream& operator<<(std::ostream& os, MapDatabase const& mdb)
    {
        for (auto const& [k, b] : mdb.database_)
            if (b) os << std::quoted(k) << " -> " << *b << "\n";
            else   os << std::quoted(k) << " -> NULL\n";

        return os;
    }

  private:
    Map database_;

    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned) { ar& database_; }
};

int main() {
    {
        MapDatabase tmp;
        std::cout << "------ tmp:\n" << tmp << "\n";
        if (not tmp.SaveToFile("boost_export.dat"))
            return 1;
    }

    MapDatabase roundtrip = MapDatabase::loadFromFile("boost_export.dat");
    std::cout << "------ roundtrip:\n" << roundtrip << "\n";
}

打印

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3.12451, "f64")

unregistered class - derived class not registered or exported

unregistered class 运行时错误

该消息说明了一切:在反序列化时,没有有关可以反序列化的类型的信息。添加:

#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(MapEntryF64)
BOOST_CLASS_EXPORT(MapEntryS64)

现在它会打印

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3.12451, "f64")

------ roundtrip:
"key1" -> S64(381, "s64")
"key2" -> F64(3.12451, "f64")

几个翻译单元。

如果是单独的翻译单元,请根据文档拆分类导出宏。例如,类导出宏最终会

#define BOOST_CLASS_EXPORT_GUID(T, K)                                  \
BOOST_CLASS_EXPORT_KEY2(T, K)                                          \
BOOST_CLASS_EXPORT_IMPLEMENT(T)                                        \
/**/

天真地这样做(又花了半个小时来明智地拆分所有操作:)

  • File test.cpp

    <前><代码>#include“MapDatabase.h”
    #include ;

    int main() {
    {
    地图数据库tmp;
    std::cout << "------ tmp:\n" << tmp << “\n”;
    if (不是 tmp.SaveToFile("boost_export.dat"))
    返回1;
    }

    MapDatabase 往返 = MapDatabase::loadFromFile("boost_export.dat");
    std::cout << "-------- 往返:\n" <<往返<< “\n”;
    }

  • 文件MapDatabase.h

     #pragma 一次
     #include“MapEntryS64.h”
     #include“MapEntryF64.h”
     #include ;
     #include ;
    
     地图数据库类 {
       民众:
         使用 Map = std::map;
    
         地图数据库();
    
         bool SaveToFile(std::string const& 文件名) const;
         static MapDatabase loadFromFile(std::string const& 文件名);
    
         朋友 std::ostream&运算符<<(std::ostream&, MapDatabase const&);
    
       私人的:
         地图数据库_;
    
         友元类 boost::serialization::access;
         模板<类Ar> void serialize(Ar& ar, unsigned) { ar& }数据库_; }
     };
    
  • 文件MapDatabase.cpp

    <前><代码>#include“MapDatabase.h”
    #include ;
    #include ;
    #include ;
    #include ;

    地图数据库::地图数据库() {
    database_.emplace("key1", std::make_unique(381));
    database_.emplace("key2", std::make_unique(3.124512));
    }

    bool MapDatabase::SaveToFile(std::string const& 文件名) const
    {
    尝试 {
    std::ofstream ofs(文件名, std::ios::binary);
    boost::archive::text_oarchive oa(ofs);
    OA << *这;
    返回真;
    } catch (std::Exception& ex) {
    std::cout <<例如.what() << std::endl;
    返回假;
    }
    }

    MapDatabase MapDatabase::loadFromFile(std::string const& 文件名)
    {
    地图数据库数据库;
    std::ifstream ifs(文件名, std::ios::binary);
    boost::archive::text_iarchive ia(ifs);
    ia>>数据库;
    返回数据库;
    }

    std::ostream&运算符<<(std::ostream&os,MapDatabase const&mdb)
    {
    for (auto const& [k, b] : mdb.database_)
    如果 (b) os << std::quoted(k) << “->”<< *b<<; “\n”;
    否则操作系统<< std::quoted(k) << " -> NULL\n";

    返回操作系统;
    }

  • 文件MapEntryF64.h

     #pragma 一次
     #include“MapEntryInterface.h”
     #include ;
    
     类 MapEntryF64 :公共 MapEntryInterface {
       民众:
         MapEntryF64(int init_val = 0, const std::string& type_str = "f64");
    
       私人的:
         uint64_t 数据_;
    
         友元类 boost::serialization::access;
         模板<类Ar>无效序列化(Ar&amp; ar,无符号){
             ar& boost::序列化::base_object(*this);
             ar&数据_;
         }
    
         virtual void print(std::ostream&os) const override;
     };
    
     #include ;
     BOOST_CLASS_EXPORT_KEY(MapEntryF64)
    
  • 文件MapEntryInterface.h

     #pragma 一次
     #include ;
     #include <字符串>
     #include <内存>
     #include ;
    
     类 MapEntryInterface {
       民众:
         虚拟〜MapEntryInterface()=默认;
         虚拟无效打印(std::ostream&) const = 0;
    
       受保护:
         MapEntryInterface(std::string type_str);
         std::字符串类型_;
    
         友元类 boost::serialization::access;
         模板<类Ar> void serialize(Ar& ar, unsigned) { ar& }类型_; }
    
         朋友 std::ostream&运算符<<(std::ostream&, MapEntryInterface const&);
     };
    
     使用 EntryPtr = std::unique_ptr;
    
  • 文件MapEntryS64.h

     #pragma 一次
     #include“MapEntryInterface.h”
     #include ;
    
     类 MapEntryS64 :公共 MapEntryInterface {
       民众:
         MapEntryS64(int init_val = 0, const std::string& type_str = "s64");
    
       私人的:
         uint64_t 数据_;
    
         友元类 boost::serialization::access;
         模板<类Ar> void Serialize(Ar& ar, 无符号) {
             ar& boost::序列化::base_object(*this);
             ar&数据_;
         }
    
         virtual void print(std::ostream&os) const override;
     };
    
     #include ;
     BOOST_CLASS_EXPORT_KEY(MapEntryS64)
    
  • 文件MapEntryF64.cpp

    <前><代码>#include“MapEntryF64.h”
    #include ;
    #include ;

    MapEntryF64::MapEntryF64(int init_val, const std::string& type_str)
    : MapEntryInterface(type_str)
    , 数据_(初始化值)
    {
    }

    void MapEntryF64::print(std::ostream&os) const {
    操作系统<< "F64(" << data_ << ", " << std::quoted(type_) << ")";
    }

    BOOST_CLASS_EXPORT_IMPLMENT(MapEntryF64)

  • 文件MapEntryInterface.cpp

    <前><代码>#include“MapEntryInterface.h”

    MapEntryInterface::MapEntryInterface(std::string type_str) : type_(type_str) {}

    std::ostream&运算符<<(std::ostream&os,MapEntryInterface const&e)
    {
    e.print(os);
    返回操作系统;
    }

  • 文件MapEntryS64.cpp

    <前><代码>#include“MapEntryS64.h”
    #include ;
    #include ;

    MapEntryS64::MapEntryS64(int init_val, const std::string& type_str)
    : MapEntryInterface(type_str)
    , 数据_(初始化值)
    {
    }

    void MapEntryS64::print(std::ostream&os) const {
    操作系统<< "S64(" << data_ << ", " << std::quoted(type_) << ")";
    }

    BOOST_CLASS_EXPORT_IMPLMENT(MapEntryS64)

打印:Live On Wandbox

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3, "f64")

unregistered class - derived class not registered or exported

呃我们注定要失败吗?

一点也不。只需要 仔细阅读文档

BOOST_CLASS_EXPORT 在包含任何以下内容的同一源模块中
归档类头将实例化序列化所需的代码
指向所有这些存档的指定类型的多态指针
类。如果不包含存档类标头,则不会有任何代码
被实例化。

因此,添加以下内容:(

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
BOOST_CLASS_EXPORT_IMPLEMENT(MapEntryF64)

对于 MapEntryS64 也是如此):

Live On Wandbox

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3, "f64")

------ roundtrip:
"key1" -> S64(381, "s64")
"key2" -> F64(3, "f64")

奖励:简单

我更喜欢简单。我可能会将以上所有内容替换为:

  • File MapDatabase.h

     #pragma 一次
     #include <地图>
     #include ;
    
     命名空间数据库{
         struct Nil { void serialize(auto&, unsigned) {} };
         使用 S64 = uint64_t;
         使用 F64 = 双;
         使用 Entry = boost::variant;
         使用 Map = std::map;
    
         std::string_view typeOf(Entry const&);
    
         void SaveToFile(std::string const& 文件名, Map const& m);
         [[nodiscard]] Map loadFromFile(std::string const& 文件名);
    
         std::ostream&运算符<<(std::ostream&, Nil);
         std::ostream&运算符<<(std::ostream&, Map const&);
     } // 命名空间数据库
    
  • 文件MapDatabase.cpp

    <前><代码>#include“MapDatabase.h”
    #include ;
    #include ;
    #include ;
    #include ;
    #include ;
    #include ;
    #include ;

    命名空间数据库{
    std::string_view typeOf(Entry const& e) {
    断言(e.which() < 3);
    返回 std::array{"Nil", "S64", "F64"}[e.which()];
    }

    void SaveToFile(std::string const& 文件名, Map const& m)
    {
    std::ofstream ofs(文件名, std::ios::binary);
    boost::archive::text_oarchive oa(ofs);
    OA <<米;
    }

    映射 loadFromFile(std::string const& 文件名)
    {
    地图数据库;
    std::ifstream ifs(文件名, std::ios::binary);
    boost::archive::text_iarchive ia(ifs);
    ia>>数据库;
    返回数据库;
    }

    std::ostream&运算符 <<(std::ostream&os, Nil) { return os << “无效的”; }
    std::ostream&运算符<<(std::ostream&os,Map const&m)
    {
    for (auto const& [k, v] : m)
    操作系统<< typeOf(v) << “\t”<< std::quoted(k) << “->”<< v << “\n”;
    返回操作系统;
    }
    } // 命名空间数据库

  • 文件test.cpp

    <前><代码>#include“MapDatabase.h”
    #include ;

    int main() {
    SaveToFile("boost_export.dat",
    数据库::地图{
    {“key1”,381ul},
    {“key3”,{}},
    {“键2”,3.124512},
    });

    std::cout <<数据库::loadFromFile("boost_export.dat");
    }

打印 Live On Wandbox

S64 "key1" -> 381
F64 "key2" -> 3.12451
Nil "key3" -> NULL

I spent a large amount of time making things self-contained. Among the many changes:

  1. you should not have the base class serializing the derived (that's classic OOP), instead Boost expects derived classes to serialize their base_object<> (allowing static polymorphism and, incidentally, type registration)

  2. of course, the base class should serialize its data members (type)

  3. the base class SHOULD have a virtual destructor (otherwise deleting through unique_ptr's destructor will be unspecified

    Up till here:

    class MapEntryInterface {
      public:
        virtual ~MapEntryInterface() = default;
    
      protected:
        MapEntryInterface(std::string type_str) : type_(type_str) {}
        std::string type_;
    
        friend class boost::serialization::access;
        template <class Ar> void serialize(Ar& ar, unsigned) { ar& type_; }
    };
    using EntryPtr = std::unique_ptr<MapEntryInterface>;
    
  4. The base/member initialize lists are in misleading order; initialization happens in declaration order anyways

  5. The type_str is a code smell: the whole idea of OOP virtualization is not to have switching on types everywhere. I went half-way for you by at least defaulting the values, but you could probably do without it entirely. After all the type is the type.

  6. Now add the base_object serialization:

    template <class Ar> void serialize(Ar& ar, unsigned)
    {
        ar& boost::serialization::base_object<MapEntryInterface>(*this);
        ar& data_;
    }
    
  7. MapDatabase benefits from several simplifications

    • never use new or delete
    • checking the streams before is redundant since you already handle exceptions
    • since loadFromFile has no useful way of handling the exception, re-throw, or just let is escape,
    • also allowing you to make MapDatabase the return type instead of bool.
  8. saveToFile and loadFromFile should probably take a filename parameter

  9. Not shown: arguably saveToFile and loadFromFile need not be part of MapDatabase

At this point, adding a little bit of code to print database contents:

Live On Wandbox

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <boost/serialization/map.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/vector.hpp>

#include <fstream>
#include <iostream>

class MapEntryInterface {
  public:
    virtual ~MapEntryInterface() = default;
    virtual void print(std::ostream&) const = 0;

  protected:
    MapEntryInterface(std::string type_str) : type_(type_str) {}
    std::string type_;

    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned) { ar& type_; }

    friend std::ostream& operator<<(std::ostream& os, MapEntryInterface const& e) {
        e.print(os);
        return os;
    }
};

using EntryPtr = std::unique_ptr<MapEntryInterface>;

class MapEntryS64 : public MapEntryInterface {
  public:
    MapEntryS64(int init_val = 0, const std::string& type_str = "s64")
        : MapEntryInterface(type_str)
        , data_(init_val)
    {
    }

  private:
    uint64_t data_;

    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned) {
        ar& boost::serialization::base_object<MapEntryInterface>(*this);
        ar& data_;
    }

    virtual void print(std::ostream& os) const override {
        os << "S64(" << data_ << ", " << std::quoted(type_) << ")";
    }
};

class MapEntryF64 : public MapEntryInterface {
  public:
    MapEntryF64(double init_val = 0, const std::string& type_str = "f64")
        : MapEntryInterface(type_str)
        , data_(init_val)
    {
    }

  private:
    double data_;
    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned)
    {
        ar& boost::serialization::base_object<MapEntryInterface>(*this);
        ar& data_;
    }

    virtual void print(std::ostream& os) const override {
        os << "F64(" << data_ << ", " << std::quoted(type_) << ")";
    }
};

class MapDatabase {
  public:
    using Map = std::map<std::string, EntryPtr>;

    MapDatabase() {
        database_.emplace("key1", std::make_unique<MapEntryS64>(381));
        database_.emplace("key2", std::make_unique<MapEntryF64>(3.124512));
    };

    bool SaveToFile(std::string const& filename) const
    {
        try {
            std::ofstream ofs(filename, std::ios::binary);
            boost::archive::text_oarchive oa(ofs);
            oa << *this;
            return true;
        } catch (std::exception& ex) {
            std::cout << ex.what() << std::endl;
            return false;
        }
    }

    static MapDatabase loadFromFile(std::string const& filename)
    {
        MapDatabase db;
        std::ifstream ifs(filename, std::ios::binary);
        boost::archive::text_iarchive ia(ifs);
        ia >> db;
        return db;
    }

    friend std::ostream& operator<<(std::ostream& os, MapDatabase const& mdb)
    {
        for (auto const& [k, b] : mdb.database_)
            if (b) os << std::quoted(k) << " -> " << *b << "\n";
            else   os << std::quoted(k) << " -> NULL\n";

        return os;
    }

  private:
    Map database_;

    friend class boost::serialization::access;
    template <class Ar> void serialize(Ar& ar, unsigned) { ar& database_; }
};

int main() {
    {
        MapDatabase tmp;
        std::cout << "------ tmp:\n" << tmp << "\n";
        if (not tmp.SaveToFile("boost_export.dat"))
            return 1;
    }

    MapDatabase roundtrip = MapDatabase::loadFromFile("boost_export.dat");
    std::cout << "------ roundtrip:\n" << roundtrip << "\n";
}

Prints

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3.12451, "f64")

unregistered class - derived class not registered or exported

unregistered class runtime error

The message says it all: at time of deserialization, there is no information about the types that can be deserialized. Add that:

#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(MapEntryF64)
BOOST_CLASS_EXPORT(MapEntryS64)

Now it prints

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3.12451, "f64")

------ roundtrip:
"key1" -> S64(381, "s64")
"key2" -> F64(3.12451, "f64")

Several Translation Units

In the case of separate translation units, split the class export macros as per the documentation. E.g. the class export macro ends up doing

#define BOOST_CLASS_EXPORT_GUID(T, K)                                  \
BOOST_CLASS_EXPORT_KEY2(T, K)                                          \
BOOST_CLASS_EXPORT_IMPLEMENT(T)                                        \
/**/

So, naively (taking another half-hour to split it all op sensibly:)

  • File test.cpp

     #include "MapDatabase.h"
     #include <iostream>
    
     int main() {
         {
             MapDatabase tmp;
             std::cout << "------ tmp:\n" << tmp << "\n";
             if (not tmp.SaveToFile("boost_export.dat"))
                 return 1;
         }
    
         MapDatabase roundtrip = MapDatabase::loadFromFile("boost_export.dat");
         std::cout << "------ roundtrip:\n" << roundtrip << "\n";
     }
    
  • File MapDatabase.h

     #pragma once
     #include "MapEntryS64.h"
     #include "MapEntryF64.h"
     #include <boost/serialization/map.hpp>
     #include <boost/serialization/unique_ptr.hpp>
    
     class MapDatabase {
       public:
         using Map = std::map<std::string, EntryPtr>;
    
         MapDatabase();
    
         bool SaveToFile(std::string const& filename) const;
         static MapDatabase loadFromFile(std::string const& filename);
    
         friend std::ostream& operator<<(std::ostream&, MapDatabase const&);
    
       private:
         Map database_;
    
         friend class boost::serialization::access;
         template <class Ar> void serialize(Ar& ar, unsigned) { ar& database_; }
     };
    
  • File MapDatabase.cpp

     #include "MapDatabase.h"
     #include <boost/archive/text_iarchive.hpp>
     #include <boost/archive/text_oarchive.hpp>
     #include <fstream>
     #include <iostream>
    
     MapDatabase::MapDatabase() {
         database_.emplace("key1", std::make_unique<MapEntryS64>(381));
         database_.emplace("key2", std::make_unique<MapEntryF64>(3.124512));
     }
    
     bool MapDatabase::SaveToFile(std::string const& filename) const
     {
         try {
             std::ofstream ofs(filename, std::ios::binary);
             boost::archive::text_oarchive oa(ofs);
             oa << *this;
             return true;
         } catch (std::exception& ex) {
             std::cout << ex.what() << std::endl;
             return false;
         }
     }
    
     MapDatabase MapDatabase::loadFromFile(std::string const& filename)
     {
         MapDatabase db;
         std::ifstream ifs(filename, std::ios::binary);
         boost::archive::text_iarchive ia(ifs);
         ia >> db;
         return db;
     }
    
     std::ostream& operator<<(std::ostream& os, MapDatabase const& mdb)
     {
         for (auto const& [k, b] : mdb.database_)
             if (b) os << std::quoted(k) << " -> " << *b << "\n";
             else   os << std::quoted(k) << " -> NULL\n";
    
         return os;
     }
    
  • File MapEntryF64.h

     #pragma once
     #include "MapEntryInterface.h"
     #include <boost/serialization/base_object.hpp>
    
     class MapEntryF64 : public MapEntryInterface {
       public:
         MapEntryF64(int init_val = 0, const std::string& type_str = "f64");
    
       private:
         uint64_t data_;
    
         friend class boost::serialization::access;
         template <class Ar> void serialize(Ar& ar, unsigned) {
             ar& boost::serialization::base_object<MapEntryInterface>(*this);
             ar& data_;
         }
    
         virtual void print(std::ostream& os) const override;
     };
    
     #include <boost/serialization/export.hpp>
     BOOST_CLASS_EXPORT_KEY(MapEntryF64)
    
  • File MapEntryInterface.h

     #pragma once
     #include <iosfwd>
     #include <string>
     #include <memory>
     #include <boost/serialization/access.hpp>
    
     class MapEntryInterface {
       public:
         virtual ~MapEntryInterface() = default;
         virtual void print(std::ostream&) const = 0;
    
       protected:
         MapEntryInterface(std::string type_str);
         std::string type_;
    
         friend class boost::serialization::access;
         template <class Ar> void serialize(Ar& ar, unsigned) { ar& type_; }
    
         friend std::ostream& operator<<(std::ostream&, MapEntryInterface const&);
     };
    
     using EntryPtr = std::unique_ptr<MapEntryInterface>;
    
  • File MapEntryS64.h

     #pragma once
     #include "MapEntryInterface.h"
     #include <boost/serialization/base_object.hpp>
    
     class MapEntryS64 : public MapEntryInterface {
       public:
         MapEntryS64(int init_val = 0, const std::string& type_str = "s64");
    
       private:
         uint64_t data_;
    
         friend class boost::serialization::access;
         template <class Ar> void serialize(Ar& ar, unsigned) {
             ar& boost::serialization::base_object<MapEntryInterface>(*this);
             ar& data_;
         }
    
         virtual void print(std::ostream& os) const override;
     };
    
     #include <boost/serialization/export.hpp>
     BOOST_CLASS_EXPORT_KEY(MapEntryS64)
    
  • File MapEntryF64.cpp

     #include "MapEntryF64.h"
     #include <ostream>
     #include <iomanip>
    
     MapEntryF64::MapEntryF64(int init_val, const std::string& type_str)
         : MapEntryInterface(type_str)
         , data_(init_val)
     {
     }
    
     void MapEntryF64::print(std::ostream& os) const {
         os << "F64(" << data_ << ", " << std::quoted(type_) << ")";
     }
    
     BOOST_CLASS_EXPORT_IMPLEMENT(MapEntryF64)
    
  • File MapEntryInterface.cpp

     #include "MapEntryInterface.h"
    
     MapEntryInterface::MapEntryInterface(std::string type_str) : type_(type_str) {}
    
     std::ostream& operator<<(std::ostream& os, MapEntryInterface const& e)
     {
         e.print(os);
         return os;
     }
    
  • File MapEntryS64.cpp

     #include "MapEntryS64.h"
     #include <ostream>
     #include <iomanip>
    
     MapEntryS64::MapEntryS64(int init_val, const std::string& type_str)
         : MapEntryInterface(type_str)
         , data_(init_val)
     {
     }
    
     void MapEntryS64::print(std::ostream& os) const {
         os << "S64(" << data_ << ", " << std::quoted(type_) << ")";
     }
    
     BOOST_CLASS_EXPORT_IMPLEMENT(MapEntryS64)
    

Prints: Live On Wandbox

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3, "f64")

unregistered class - derived class not registered or exported

UHOH Are We Doomed?

Not at all. Just need to read the documentation closely:

BOOST_CLASS_EXPORT in the same source module that includes any of the
archive class headers will instantiate code required to serialize
polymorphic pointers of the indicated type to the all those archive
classes. If no archive class headers are included, then no code will
be instantiated
.

So, adding the includes:

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
BOOST_CLASS_EXPORT_IMPLEMENT(MapEntryF64)

(and the same for MapEntryS64):

Live On Wandbox

------ tmp:
"key1" -> S64(381, "s64")
"key2" -> F64(3, "f64")

------ roundtrip:
"key1" -> S64(381, "s64")
"key2" -> F64(3, "f64")

BONUS: Simplicity

I prefer simplicity. I would probably replace all of the above with:

  • File MapDatabase.h

     #pragma once
     #include <map>
     #include <boost/variant.hpp>
    
     namespace Database {
         struct Nil { void serialize(auto&, unsigned) {} };
         using S64   = uint64_t;
         using F64   = double;
         using Entry = boost::variant<Nil, S64, F64>;
         using Map   = std::map<std::string, Entry>;
    
         std::string_view typeOf(Entry const&);
    
         void SaveToFile(std::string const& filename, Map const& m);
         [[nodiscard]] Map loadFromFile(std::string const& filename);
    
         std::ostream& operator<<(std::ostream&, Nil);
         std::ostream& operator<<(std::ostream&, Map const&);
     } // namespace Database
    
  • File MapDatabase.cpp

     #include "MapDatabase.h"
     #include <boost/archive/text_iarchive.hpp>
     #include <boost/archive/text_oarchive.hpp>
     #include <boost/serialization/map.hpp>
     #include <boost/serialization/variant.hpp>
     #include <fstream>
     #include <iomanip>
     #include <iostream>
    
     namespace Database {
         std::string_view typeOf(Entry const& e) {
             assert(e.which() < 3);
             return std::array{"Nil", "S64", "F64"}[e.which()];
         }
    
         void SaveToFile(std::string const& filename, Map const& m)
         {
             std::ofstream ofs(filename, std::ios::binary);
             boost::archive::text_oarchive oa(ofs);
             oa << m;
         }
    
         Map loadFromFile(std::string const& filename)
         {
             Map                           db;
             std::ifstream                 ifs(filename, std::ios::binary);
             boost::archive::text_iarchive ia(ifs);
             ia >> db;
             return db;
         }
    
         std::ostream& operator<<(std::ostream& os, Nil) { return os << "NULL"; }
         std::ostream& operator<<(std::ostream& os, Map const& m)
         {
             for (auto const& [k, v] : m)
                 os << typeOf(v) << "\t" << std::quoted(k) << " -> " << v << "\n";
             return os;
         }
     } // namespace Database
    
  • File test.cpp

     #include "MapDatabase.h"
     #include <iostream>
    
     int main() {
         SaveToFile("boost_export.dat",
                    Database::Map{
                        {"key1", 381ul},
                        {"key3", {}},
                        {"key2", 3.124512},
                    });
    
         std::cout << Database::loadFromFile("boost_export.dat");
     }
    

Printing Live On Wandbox

S64 "key1" -> 381
F64 "key2" -> 3.12451
Nil "key3" -> NULL
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文