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

Discussion in 'C++' started by Jorge Schramm, Oct 7, 2003.

  1. 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
     
    Jorge Schramm, Oct 7, 2003
    #1
    1. Advertising

  2. Jorge Schramm

    Attila Feher Guest

    Jorge Schramm wrote:
    > 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.

    --
    Attila aka WW
     
    Attila Feher, Oct 7, 2003
    #2
    1. Advertising

  3. 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
     
    Jorge Schramm, Oct 7, 2003
    #3
  4. Jorge Schramm

    Attila Feher Guest

    Jorge Schramm wrote:
    > 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 :)


    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.

    --
    Attila aka WW
     
    Attila Feher, Oct 7, 2003
    #4
  5. Jorge Schramm

    Attila Feher Guest

    Attila Feher wrote:
    > 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.

    --
    Attila aka WW
     
    Attila Feher, Oct 7, 2003
    #5
  6. Jorge Schramm

    tom_usenet Guest

    On Tue, 07 Oct 2003 13:42:25 +0200, Jorge Schramm
    <> wrote:

    >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;
    }
     
    tom_usenet, Oct 7, 2003
    #6
  7. Hi tom_usenet,

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

    Jorge
     
    Jorge Schramm, Oct 7, 2003
    #7
  8. tom_usenet wrote:

    > 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
     
    Jorge Schramm, Oct 8, 2003
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Maitre Bart
    Replies:
    2
    Views:
    549
    Maitre Bart
    Feb 11, 2004
  2. Simon Elliott
    Replies:
    4
    Views:
    1,190
    Simon Elliott
    Mar 10, 2005
  3. Marcus
    Replies:
    2
    Views:
    620
    Marcus
    Dec 9, 2005
  4. Replies:
    4
    Views:
    829
    Daniel T.
    Feb 16, 2006
  5. wolverine
    Replies:
    2
    Views:
    481
    Marcus Kwok
    Jul 24, 2006
Loading...

Share This Page