Serialisation of STL-container - fails with maps...

J

Jorge Schramm

Hi,

a collegue of mine is trying to write a serialisable container (reads at
construction, writes at destruction).
The writing part is pretty easy: simply iterate through the container and
write into the file.
Reading from the file is (should) be easy too:
while you're getting data from the file (stream) insert it into your
container.
Unfortunatly this fails with the container map, because the
map::value_type's key is const :-/

This is the stripped code:

#include <list>
#include <vector>
#include <map>
#include <set>
#include <deque>

template< class containerT >
class MyContainer : public containerT
{
public:
void read()
{
typename containerT::value_type val;

/* normally:
read from file and make the assigment,
this is simply for demonstration
*/
val = val;

/* insert into container */
insert(end(), val);
}
};


template< class containerT >
void foo()
{
MyContainer< containerT > cont;
cont.read();
}

int main(int, char**)
{
/* compiles */
foo< std::vector< int > >();
foo< std::list< char > >();
foo< std::set< char > >();
foo< std::deque< long > >();

/* fails */
foo< std::map< char, int > >();
return 0;
}


Any ideas, how to modify the code for being able to assign to the value and
add to the container??

The read function would look something like this:

void read()
{
std::ifstream stream(m_path.c_str());
while (!stream.eof())
{
typename containerT::value_type val;
stream >> val;
insert(end(), val);
}
}

Thanks in advance :)
Jorge
 
A

Attila Feher

Jorge said:
Hi,

a collegue of mine is trying to write a serialisable container (reads
at construction, writes at destruction).
The writing part is pretty easy: simply iterate through the container
and write into the file.
Reading from the file is (should) be easy too:
while you're getting data from the file (stream) insert it into your
container.
Unfortunatly this fails with the container map, because the
map::value_type's key is const :-/ [SNIP]
Any ideas, how to modify the code for being able to assign to the
value and add to the container??
[SNIP]

Try to find it. If found, erase it. Then insert. This is the way to do it
with map and set.
 
J

Jorge Schramm

Hi Attila,

thanks for your answer.
Try to find it. If found, erase it. Then insert. This is the way to do
it with map and set.

Unfortunately this is not the problem :-/ The line with the assignment fails
compiling for std::map.

| typename containerT::value_type val;
| val = val; // fails


This is because the value_type of map is a pair, having its key *const*.
I'd like to know an alternative :)

Jorge
 
A

Attila Feher

Jorge said:
Hi Attila,

thanks for your answer.


Unfortunately this is not the problem :-/ The line with the
assignment fails compiling for std::map.


This is because the value_type of map is a pair, having its key
*const*. I'd like to know an alternative :)

The right eay to do it (or alternative as you call it) is to search for the
key part. If you find it, delete it. Then insert the new.

PSEUDO CODE!

void read()
{
typedef typename containerT::value_type value_type;
typedef typename containerT::iterator iterator;
// read key and value
iterator it = this->find(key);
if (key != this->end()) {
this->erase(key);
}
this->insert(end(), make_pair(key,value));
}

BTW do *not* inherit from the conatiner! Have it as a member. By
inheriting from it you wonder to two-phase name lookup land and *all* the
names coming from the container has to be prefixed with typename or this->
to make sure it will work.
 
A

Attila Feher

Attila said:
BTW do *not* inherit from the conatiner! Have it as a member. By
inheriting from it you wonder to two-phase name lookup land and *all*
the names coming from the container has to be prefixed with typename
or this-> to make sure it will work.

Update. You will still need typename before the types if it is a member.
But since it is a member you will not run into trouble by leaving out the
this-> before the member function calls. If you do leave out (and the
compiler "starts" to support two phase name lookup) you can end up calling
the std::find instead of the members etc. In addition your container class
is not a map (if I understand it right), it is implemented in terms of a map
(or whatever container you use).

BTW I suggest you read Scott Meyers Effective STL and Item #2, Beware of the
illusion of container independent code.
 
T

tom_usenet

Hi,

a collegue of mine is trying to write a serialisable container (reads at
construction, writes at destruction).
The writing part is pretty easy: simply iterate through the container and
write into the file.
Reading from the file is (should) be easy too:
while you're getting data from the file (stream) insert it into your
container.
Unfortunatly this fails with the container map, because the
map::value_type's key is const :-/

Here's a version that works on MSVC7.1. It won't work on earlier
versions, since they don't support partial specialization, which is
required to solve this problem (there may be an MSVC6 solution - I'll
have a fiddle).

Container persistence systems have been done before. You might want to
check out progress of the boost serialization library (see the files
section of www.boost.org)

#include <list>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <fstream>

template <class T>
struct value_traits
{
typedef T value_type;
typedef T non_const_value_type;

static std::istream& get_from_stream(std::istream& is,
non_const_value_type& t)
{
is >> t;
return is;
}
};

template <class T, class U>
struct value_traits<std::pair<T const, U> >
{
typedef std::pair<T const, U> value_type;
typedef std::pair<T, U> non_const_value_type;
static std::istream& get_from_stream(std::istream& is,
non_const_value_type& t)
{
is >> t.first >> t.second;
return is;
}
};

template< class containerT >
class MyContainer : public containerT
{
public:
std::string m_path;

void read()
{
std::ifstream stream(m_path.c_str());
typedef value_traits<typename containerT::value_type> traits;
typename traits::non_const_value_type val;
while (traits::get_from_stream(stream, val))
{
this->insert(this->end(), val);
}
}
};


template< class containerT >
void foo()
{
MyContainer< containerT > cont;
cont.read();
}

int main(int, char**)
{
/* compiles */
foo< std::vector<int> >();
foo< std::list<char> >();
foo< std::set<char> >();
foo< std::deque<long> >();

/* fails */
foo< std::map<char, int> >();
return 0;
}
 
J

Jorge Schramm

Hi tom_usenet,

thanks! Works on Linux with gcc 3.2.2 too ;)
Nice idea using partial specialisation!!

Jorge
 
J

Jorge Schramm

tom_usenet said:
Here's a version that works on MSVC7.1. It won't work on earlier
versions, since they don't support partial specialization, which is
required to solve this problem (there may be an MSVC6 solution - I'll
have a fiddle).

*argh* my collegue uses msvc 6.0 :/

We made a workaround with a load-policy. It's not very nice but it works.

template< class containerT, typename loadPolicyT = defaultLoadPolicy >
class MyContainer : public containerT
{
void read()
{
std::ifstream stream(m_path.c_str());
loadPolicy::load(stream, *this);
}
}


Thx,
Jorge
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top