I'm using boost::serialize to serialize a document wherein I use f.i. a juce::String. Like so:
template<class Archive>
void serialize( Archive & ar, const unsigned int version )
{
ar & boost::serialization::make_nvp("title", m_docTitle);
...
}
For boost::serialize to accept juce::String as a primitive type I did:
#include <boost/serialization/string.hpp>
template<class IStream>
inline IStream& operator >> (IStream& stream, juce::String& s)
{
std::wstring t;
stream >> t;
s = juce::String(t.c_str());
return stream;
}
BOOST_CLASS_IMPLEMENTATION(juce::String, boost::serialization::primitive_type)
which compiles nicely. Serialization works fine, I get the entry in the XML:
<title>DocumentTitle</title>
as it should be. However when deserializing I can trace in the >> operator that the string returned is:
"DocumentTitle</title>"
i.e. some of the XML has been "chewed up", which later of course leads to an "input stream error" exception.
The oddest part is though that I had this working up until a week ago... :( and I have no idea what makes it NOT work now...
Edit: A little example code that shows reproduces the behavior, only dependency i开发者_如何学JAVAs boost:
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/string.hpp>
#include <boost/archive/xml_woarchive.hpp>
#include <boost/archive/xml_wiarchive.hpp>
#include <sstream>
class JuceString
{
public:
JuceString(const std::wstring& str = L"") : m_str(str) {;}
JuceString(const JuceString& other) : m_str(other.m_str) {;}
JuceString& operator = (const JuceString& other)
{
if (this != &other)
{
m_str = other.m_str;
}
return *this;
}
const wchar_t* toWideCharPointer() const {
return m_str.c_str();
}
private:
std::wstring m_str;
};
template <class OStream>
OStream& operator<< (OStream& stream, const JuceString& stringToWrite)
{
return stream << stringToWrite.toWideCharPointer();
}
template <class IStream>
IStream& operator>> (IStream& stream, JuceString& s)
{
std::wstring t;
stream >> t;
s = JuceString(t.c_str());
return stream;
}
BOOST_CLASS_IMPLEMENTATION(JuceString, boost::serialization::primitive_type)
class Doc
{
friend class boost::serialization::access;
template<class Archive>
void serialize( Archive & ar, const unsigned int version )
{
ar & boost::serialization::make_nvp("title", m_title);
}
public:
Doc() {;}
Doc(const std::wstring& s) : m_title(s) {;}
private:
JuceString m_title;
};
int main (int argc, char* argv[])
{
std::wstringstream stream;
{
// Serializing document
Doc doc(L"DocumentTitle");
boost::archive::xml_woarchive oa(stream);
oa << boost::serialization::make_nvp("Document", doc);
}
{
// Deserializing document
Doc doc;
try
{
boost::archive::xml_wiarchive ia(stream);
ia >> boost::serialization::make_nvp("Document", doc);
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
return 0;
}
Using the text archive instead works just fine both ways...
Finally I got it to work (for XML archive nota bene), by using boost basic_xml_grammar to parse the string in operator>> , like so:
typedef boost::archive::basic_xml_grammar<wchar_t> xml_wgrammar;
template <class IStream>
IStream& operator>> (IStream& stream, JuceString& s)
{
std::wstring t;
xml_wgrammar grammar;
if (!grammar.parse_string(stream, t))
{
boost::serialization::throw_exception(
boost::archive::xml_archive_exception(boost::archive::xml_archive_exception::xml_archive_parsing_error)
);
}
s = JuceString(t.c_str());
return stream;
}
That will make sure that the string is parsed correctly.
The problem is in your operator >>
When you do:
std::wstring t;
stream >> t;
You read in until the next space. In XML that's after the closing tag, so you are reading part of the stream that boost expects to be able to read there.
The solution is to either force an extra space into the output, or modify operator >>
to refuse to read anything like '<'. My guess would be that previously you had an extra space in the string you were saving and restoring.
Assuming you can set/assign from std::wstring
and get some kind of raw value that can be used to create std::wstring this works on my machine:
#include <boost/serialization/split_free.hpp>
...
namespace boost {
namespace serialization {
template<class Archive>
void load(Archive & ar, JuceString & j, const unsigned int version)
{
std::wstring tmp;
ar & boost::serialization::make_nvp("value", tmp);
j = tmp;
}
template<class Archive>
void save(Archive & ar, const JuceString & j, const unsigned int version)
{
const std::wstring tmp(j.toWideCharPointer());
ar & boost::serialization::make_nvp("value", tmp);
}
} // namespace serialization
} // namespace boost
BOOST_SERIALIZATION_SPLIT_FREE(JuceString)
This escapes the need to write an operator >>
and lets the boost handle reading/writing the wstring to the archive directly. You only need to write it once too, so it avoids the duplication issues and then you can use JuceString
as often as you like.
I don't know if have an answer for you, but when comparing the code in my workspace to serialize out ATL CString I have the following:
inline void serialize(Archive & ar, CString & s, const unsigned int file_version)
{
std::wstring ss( s );
ar & BOOST_SERIALIZATION_NVP(ss);
s = ss.c_str();
}
I wonder if you could do something like this in your operator <<
- unsure if this solution is applicable to you or not. Apologies in advance if not.
精彩评论