16

I have a std::map<int, customType>, with:

struct customType { // sample struct
    std::string a;
    std::string b;
    int c;
    std::list<std::string> d;
}

How can I serialize this to a file?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
jarjarbinks
  • 171
  • 1
  • 3
  • 8
  • 3
    Look into [Boost.Archive](http://www.boost.org/doc/libs/release/libs/serialization/doc/tutorial.html). – Fred Foo Nov 14 '11 at 12:20
  • 2
    This was already answered lots of times. For example here: http://stackoverflow.com/questions/4422399/serialization-of-stl-class – enticedwanderer Nov 14 '11 at 12:21

6 Answers6

13

If you are not afraid of BOOST, try BOOST Serialize: (template code, here can be some errors...)

#include <boost/archive/binary_oarchive.hpp> 
#include <boost/archive/binary_iarchive.hpp> 
#include <boost/serialization/map.hpp> 
#include <boost/serialization/string.hpp> 
#include <boost/serialization/list.hpp> 

struct customType{
 string string1;
 string string2;
 int i;
 list<string> list;

// boost serialize 
private: 
    friend class boost::serialization::access; 
    template <typename Archive> void serialize(Archive &ar, const unsigned int version) { 
        ar & string1; 
        ar & string2; 
        ar & i;
        ar & list;
    } 
};

template <typename ClassTo> 
int Save(const string fname, const ClassTo &c) 
{ 
    ofstream f(fname.c_str(), ios::binary);
    if (f.fail()) return -1;
    boost::archive::binary_oarchive oa(f); 
    oa << c; 
    return 0;
} 

Usage:

Save< map<int, customType> >("test.map", yourMap); 
syntagma
  • 23,346
  • 16
  • 78
  • 134
Max Tkachenko
  • 792
  • 1
  • 12
  • 30
7

A simple solution is to output each member on a line on its own, including all the strings in the list. Each record start with the key to the map, and ends with a special character or character sequence that can not be in the list. This way you can read one line at a time, and know the first line is the map key, the second line the first string in the structure and so on, and when you reach your special record-ending sequence you know the list is done and it's time for the next item in the map. This scheme makes the files generated readable, and editable if you need to edit them outside the program.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
2

C++ doesn't have reflection capabilities like Java and others, so there's no 'automatic' way of doing that. You'll have to do all the work yourself: open the file, output each element in a loop, and close the file. Also there's no standard format for the file, you'd need to define one that meets your needs. Of course, there are libraries out there to help in this, but they aren't part of the language. Take a look at this question:

Is it possible to automatically serialize a C++ object?

Also take a look at: http://s11n.net/

Community
  • 1
  • 1
Fabio Ceconello
  • 15,819
  • 5
  • 38
  • 51
0

Hi I wrote a standalone C11 header to achieve this. Your example of a map of custom classes, I just added - to make sure it worked 8)

https://github.com/goblinhack/simple-c-plus-plus-serializer

#include "c_plus_plus_serializer.h"

class Custom {
public:
    int a;
    std::string b;
    std::vector c;

    friend std::ostream& operator<<(std::ostream &out, 
                                    Bits my)
    {
        out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
        return (out);
    }

    friend std::istream& operator>>(std::istream &in, 
                                    Bits my)
    {
        in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
        return (in);
    }

    friend std::ostream& operator<<(std::ostream &out, 
                                    class Custom &my)
    {
        out << "a:" << my.a << " b:" << my.b;

        out << " c:[" << my.c.size() << " elems]:";
        for (auto v : my.c) {
            out << v << " ";
        }
        out << std::endl;

        return (out);
    }
};

static void save_map_key_string_value_custom (const std::string filename)
{
    std::cout << "save to " << filename << std::endl;
    std::ofstream out(filename, std::ios::binary );

    std::map< std::string, class Custom > m;

    auto c1 = Custom();
    c1.a = 1;
    c1.b = "hello";
    std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
    std::vector l1(L1);
    c1.c = l1;

    auto c2 = Custom();
    c2.a = 2;
    c2.b = "there";
    std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
    std::vector l2(L2);
    c2.c = l2;

    m.insert(std::make_pair(std::string("key1"), c1));
    m.insert(std::make_pair(std::string("key2"), c2));

    out << bits(m);
}

static void load_map_key_string_value_custom (const std::string filename)
{
    std::cout << "read from " << filename << std::endl;
    std::ifstream in(filename);

    std::map< std::string, class Custom > m;

    in >> bits(m);
    std::cout << std::endl;

    std::cout << "m = " << m.size() << " list-elems { " << std::endl;
    for (auto i : m) {
        std::cout << "    [" << i.first << "] = " << i.second;
    }
    std::cout << "}" << std::endl;
}

void map_custom_class_example (void)
{
    std::cout << "map key string, value class" << std::endl;
    std::cout << "============================" << std::endl;
    save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    std::cout << std::endl;
}

Output:

map key string, value class
============================
save to map_of_custom_class.bin
read from map_of_custom_class.bin

m = 2 list-elems {
    [key1] = a:1 b:hello c:[2 elems]:vec-elem1 vec-elem2
    [key2] = a:2 b:there c:[2 elems]:vec-elem3 vec-elem4
}

Let me know if this helps - or you find bugs. It's quite a simple serializer and really just a learning tool for me. Heavier weight approaches like Cereal might work for you better.

Goblinhack
  • 2,859
  • 1
  • 26
  • 26
0

If you are asking this, then probably you already know that you cannot serialize this by means of:

file.write( (const char *) &mapOfCustom, sizeof( mapOfCustom ) );

The problem has to do with complex objects (and in C++, even a string variable is a complex object), i.e., those objects that are not self-contained. Actually, even simple serialization has problems, which range from platform compatibilty to even compiler compatibilty (different paddings, etc.).

One way to go is use a simple XML library such as tinyXML:

http://www.grinninglizard.com/tinyxml/

And write save to XML, and restore from XML procedures.

Baltasarq
  • 12,014
  • 3
  • 38
  • 57
0

You can try this: cxx-prettyprint

yasouser
  • 5,113
  • 2
  • 27
  • 41
  • I like the link, but unfortunately that doesn't do what the OP wants. For starters, her `customType` doesn't implement a formatted output operator (`>>`), and more importantly, serialization usually isn't *formatted* output, but some sort of raw, binary output. – Kerrek SB Nov 14 '11 at 12:29
  • Yes I did understand her requirement, but boost serialization or google protobuf might be a overkill. Hence my suggestion. – yasouser Nov 15 '11 at 03:43