Search code examples
c++serializationboosticu

icu::UnicodeString with boost serialize


I am trying to serialize an icu::UnicodeString with the boost serialization library but am having trouble.

The icu::UnicodeString does not have the required serialize function to serialize it. So I tried to create it, but I am not sure how to make these. Example code:

#include <map>
#include <sstream>
#include <fstream>
#include <unicode/unistr.h>
#include <unicode/ustream.h>

#include <boost/serialization/map.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

namespace boost {
namespace serialization {

template<class Archive>
inline void save(
    Archive & ar,
    const icu_55::UnicodeString& str,
    const unsigned int /* file_version */
){
}

template<class Archive>
inline void load(
    Archive & ar,
    icu_55::UnicodeString& str,
    const unsigned int /* file_version */
){
}

// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<class Archive>
inline void serialize(
    Archive & ar,
    icu_55::UnicodeString& str,
    const unsigned int file_version
){
    boost::serialization::split_free(ar, str, file_version);
}

} // serialization
} // namespace boost

int main()
{

   std::map<icu::UnicodeString, int> map = {{"asssdasd",2}, {"qwe",1}, {"Zxc",55}};
   std::stringstream ss;
   boost::archive::text_oarchive oarch(ss);
   oarch << map;
   std::map<icu::UnicodeString, int> new_map;
   boost::archive::text_iarchive iarch(ss);
   iarch >> new_map;

   std::cout << (map == new_map) << std::endl;
}

Compile with something like g++ -o new new.cpp -std=c++11 -lboost_serialization -licuuc

Currently the "save" and "load" functions are not implemented. I tried doing just the ar & str; statements that are used in the boost manuals, but I am getting a segmentation fault with that that I am also unable to fix.


Solution

  • I've never worked with LibICU directly, so probably someone can review this code.

    However, from my experience using Boost Serialization I think this should be helpful:

    template <class Archive>
    inline void save(Archive &ar, icu_55::UnicodeString const &str, const unsigned int) {
        auto sz = str.getCapacity();
        auto len = str.length();
        auto buf = str.getBuffer();
    
        if (!buf) throw std::invalid_argument("str");
    
        ar & sz & len & boost::serialization::make_array(buf, sz);
    }
    
    template <class Archive>
    inline void load(Archive &ar, icu_55::UnicodeString &str, const unsigned int)
    {
        size_t sz, len;
        ar & sz & len;
        auto buf = str.getBuffer(sz);
        if (!buf) throw std::invalid_argument("str");
    
        try {
            ar & boost::serialization::make_array(buf, sz);
        } 
        catch(...) {
            str.releaseBuffer(len);
            throw;
        }
        str.releaseBuffer(len);
    }
    

    It works for the simple test case provided:

    #include <fstream>
    #include <map>
    #include <sstream>
    #include <iostream>
    #include <unicode/unistr.h>
    #include <unicode/ustream.h>
    
    #include <boost/archive/text_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/serialization/map.hpp>
    
    namespace boost { namespace serialization {
    
        template <class Archive>
        inline void save(Archive &ar, icu_55::UnicodeString const &str, const unsigned int) {
            auto sz = str.getCapacity();
            auto len = str.length();
            auto buf = str.getBuffer();
    
            if (!buf) throw std::invalid_argument("str");
    
            ar & sz & len & boost::serialization::make_array(buf, sz);
        }
    
        template <class Archive>
        inline void load(Archive &ar, icu_55::UnicodeString &str, const unsigned int)
        {
            size_t sz, len;
            ar & sz & len;
            auto buf = str.getBuffer(sz);
            if (!buf) throw std::invalid_argument("str");
    
            try {
                ar & boost::serialization::make_array(buf, sz);
            } 
            catch(...) {
                str.releaseBuffer(len);
                throw;
            }
            str.releaseBuffer(len);
        }
    
    
        // split non-intrusive serialization function member into separate
        // non intrusive save/load member functions
        template <class Archive>
        inline void serialize(Archive &ar, icu_55::UnicodeString &str, const unsigned int file_version) {
            boost::serialization::split_free(ar, str, file_version);
        }
    
    } } // serialization // namespace boost
    
    int main() {
        std::map<icu::UnicodeString, int> const map = { { "asssdasd", 2 }, { "qwe", 1 }, { "Zxc", 55 } };
        std::stringstream ss;
        {
            boost::archive::text_oarchive oarch(ss);
            oarch << map;
        }
    
        {
            std::map<icu::UnicodeString, int> new_map;
            boost::archive::text_iarchive iarch(ss);
            iarch >> new_map;
    
            std::cout << (map == new_map) << std::endl;
        }
    }
    

    Prints

    1