C++ Serializing a std::map to a file
Asked Answered
F

7

18

I have a C++ STL map, which is a map of int and customType. The customType is a struct, which has string and a list of string, How can i serialize this to a file.

sample struct:

struct customType{
string;
string;
int;
list<string>;
}
Furnishings answered 14/11, 2011 at 12:15 Comment(2)
Look into Boost.Archive.Crepitate
This was already answered lots of times. For example here: https://mcmap.net/q/741045/-serialization-of-stl-classShoveler
P
14

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); 
Pow answered 14/11, 2011 at 12:25 Comment(0)
R
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.

Rosinski answered 14/11, 2011 at 12:20 Comment(0)
O
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/

Oliver answered 14/11, 2011 at 12:25 Comment(0)
W
1

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.

Waxman answered 14/11, 2011 at 12:23 Comment(1)
You haven't completely read/understood my answer. Please do it again more carefully.Waxman
I
1

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.

Immaterial answered 7/9, 2019 at 1:31 Comment(0)
A
0

You can try this: cxx-prettyprint

Aleshia answered 14/11, 2011 at 12:26 Comment(2)
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.Guild
Yes I did understand her requirement, but boost serialization or google protobuf might be a overkill. Hence my suggestion.Aleshia
C
0

It's not that hard to write your own serialization routines

struct customType {
  string a;
  string b;
  int x;
  list<string> li;
  
  void save(string filename) {
    FILE* f = fopen( filename.c_str(), "wb" );
    if( !f )  return;

    // write: a's length, a's data:
    size_t aLen = a.length();
    fwrite( &aLen, sizeof( size_t ), 1, f );

    fwrite( a.data(), a.length(), 1, f );

    // repeat for b, x, each element in list.
    fclose( f );
  }

  void load(string filename) {
    FILE* f = fopen( filename.c_str(), "rb" );
    if( !f )  return;

    size_t aLen;
    fread( &aLen, sizeof( size_t ), 1, f );

    a.resize( aLen );
    fread( a.data(), aLen, 1, f );

    fclose( f );
  }
};

customType ct;
ct.a = "string data";
ct.save("FILENAME.txt");

customType ct2;
ct2.load("FILENAME.txt");
printf("loaded: `%s`\n", ct2.a.c_str() );


Cinchonism answered 13/3 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.