简体   繁体   中英

binary reading and writing complicated stucts in c++

I need the ability to save/read my data structures in my project, but all the data is in the form of quite complicated and distinct structures themselves which I typically implement through other structs and vectors. I have wrapped all of them up into a single struct so that I have something like

struct master{
    std::vector<apprentice_type1> a;
    std::vector<apprentice_type2> b; //etc.

    std::string label;
};

with other ones defined like

struct apprentice_type1{
    vec location;
    int point_label;
    std::vector<int> relational_data;
};

struct vec{
    double x,y,z;
};

So it gets pretty complicated! I was desperately hoping something nice, quick and naive like

master obj;
//write to obj....
std::ofstream ofs("data.dat", std::ios::binary);
ofs.write((char *)&obj, sizeof(obj));

would work, but at present it doesn't seem to. Before I get lost in the debugging rabbit hole is this actually possible the way I'm approaching it or do I need to rethink? If so, how?

Thanks.

[...] or do I need to rethink? If so, how?

You will probably need to provide a full implementation (ie explore the "rabbit-hole").

This is a known problem (stream serialization) and there is no single best-approach solution to it, because most implementations need to solve different needs.

You can do one of the following:

  • implement std::i/ostream serialization; This means you will go over your classes and implement operator>>(std::istream*, your_type&) and it's inverse operator<<(std::ostream*, your_type&) .

  • implement serialization based on a stream library (like boost.archive).

  • use a JSON or XML library.

  • use google protocol buffers (or something else)

  • roll your own implementation, depending on your needs.

If you go for boost::serialization, here is a little sample:

#include <fstream>
#include <vector>

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


template <typename T>
inline const boost::serialization::nvp<T> archive_value(const char* name, T& value) {
    return boost::serialization::make_nvp(name, value);
}

const unsigned Version = 0;

class Point{
    public:
    double x,y,z;

    private:
    template <typename P, typename Archive>
    static void serialize(P& p, Archive& ar, const unsigned int version) {
        std::cout << "Point\n";
        ar & archive_value("X", p.x);
        ar & archive_value("Y", p.y);
        ar & archive_value("Z", p.z);
    }

    public:
    template <typename Archive>
    void serialize(Archive& ar, const unsigned int version) const {
        serialize(*this, ar, version);
    }

    template <typename Archive>
    void serialize(Archive& ar, const unsigned int version) {
        serialize(*this, ar, version);
    }
};

BOOST_CLASS_VERSION(Point, Version)

struct Scene{
    std::vector<Point> points;

    private:
    template <typename S, typename Archive>
    static void serialize(S& s, Archive& ar, const unsigned int version) {
        std::cout << "Scene\n";
        ar & archive_value("Points", s.points);
    }

    public:
    template <typename Archive>
    void serialize(Archive& ar, const unsigned int version) const {
        serialize(*this, ar, version);
    }

    template <typename Archive>
    void serialize(Archive& ar, const unsigned int version) {
        serialize(*this, ar, version);
    }
};

BOOST_CLASS_VERSION(Scene, Version)


template <typename Archive>
void register_types(Archive& ar) {
    ar.template register_type<Point>();
    ar.template register_type<Scene>();
}

int main() {
    Scene scene;
    scene.points = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 } };

    // Output
    {
        std::ofstream out("test.dat", std::ios_base::binary);
        boost::archive::binary_oarchive output(out);


        // First the version!
        output & archive_value("Version", Version);
        // Next the types!
        register_types(output);
        // Finally the data
        output & archive_value("Scene", scene);
    }

    scene.points = {};

    // Input
    {
        int version;
        std::ifstream in("test.dat", std::ios_base::binary);
        boost::archive::binary_iarchive input(in);
        // First the version!
        input & archive_value("Version", Version);
        // Next the types!
        register_types(input);
        // Finally the data
        input & archive_value("Scene", scene);
    }

    for(const auto& p : scene.points)
        std::cout << p.x << '\n';
}

Note: The file format may evolve and serialization functions (input and/or output) may get adjustment depending on the file version.

If you want an alternative to Boost serialization and have access to a C++11 compiler, you can also check out cereal . It works in a near identical fashion to Boost serialize but is a header only library so there is nothing to link against.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM