проблема ускорения десериализации juce::String с архивом формата XML

#c #serialization #boost

#c #сериализация #ускорьте #c

Вопрос:

Я использую boost::serialize для сериализации документа, в котором я использую, например, juce::String. Вот так:

 template<class Archive>
void serialize( Archive amp; ar, const unsigned int version )
{
    ar amp; boost::serialization::make_nvp("title", m_docTitle);
    ...
}
  

Для boost::serialize, чтобы принять juce::String в качестве примитивного типа, я сделал:

 #include <boost/serialization/string.hpp>

template<class IStream>
inline IStreamamp; operator >> (IStreamamp; stream, juce::Stringamp; s)
{
    std::wstring t;
    stream >> t;
    s = juce::String(t.c_str());
    return stream;
}

BOOST_CLASS_IMPLEMENTATION(juce::String, boost::serialization::primitive_type)
  

который отлично компилируется. Сериализация работает нормально, я получаю запись в XML:

 <title>DocumentTitle</title>
  

так и должно быть. Однако при десериализации я могу отследить в операторе >>, что возвращаемая строка является:

 "DocumentTitle</title>"
  

т.е. часть XML была «разжевана», что позже, конечно, приводит к исключению «ошибка входного потока».

Однако самое странное, что у меня это работало еще неделю назад … : ( и я понятия не имею, почему это НЕ работает сейчас…

Редактировать: небольшой пример кода, который показывает, воспроизводит поведение, только зависимость 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::wstringamp; str = L"") : m_str(str) {;}
    JuceString(const JuceStringamp; other) : m_str(other.m_str) {;}
    JuceStringamp; operator = (const JuceStringamp; other)
    {
        if (this != amp;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>
OStreamamp; operator<< (OStreamamp; stream, const JuceStringamp; stringToWrite)
{
    return stream << stringToWrite.toWideCharPointer();
}

template <class IStream>
IStreamamp; operator>> (IStreamamp; stream, JuceStringamp; 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 amp; ar, const unsigned int version )
    {
        ar amp; boost::serialization::make_nvp("title", m_title);
    }

public:
    Doc() {;}
    Doc(const std::wstringamp; 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::exceptionamp; e)
        {
            std::cerr << e.what() << std::endl;
        }

    }
    return 0;
}
  

Использование текстового архива вместо этого прекрасно работает в обоих направлениях…

Комментарии:

1.Можете ли вы свести это к примеру из одного файла, который мы все можем скомпилировать и запустить на наших рабочих станциях? Чтобы попробовать это прямо сейчас, каждому читателю, который хочет попробовать, потребуется немного повозиться. Это почти готово, но с int main() несколькими #includes у вас был бы отличный sscce и, вероятно, еще немало людей, заинтересованных в том, чтобы попробовать это.

2. Это немного сложно, поскольку потребуется библиотека juce… но я исправлю это с учетом этого в качестве предпосылки.

3. Хорошо, обновил вопрос кодом, который воспроизводит проблему.

Ответ №1:

Наконец-то я заставил это работать (для удобства XML-архива), используя boost basic_xml_grammar для разбора строки в operator>> , вот так:

 typedef boost::archive::basic_xml_grammar<wchar_t> xml_wgrammar;

template <class IStream>
IStreamamp; operator>> (IStreamamp; stream, JuceStringamp; 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;
}
  

Это гарантирует, что строка проанализирована правильно.

Комментарии:

1. 1, я кое-что узнал из этого. Я думаю, вам следует принять этот ответ вместо моего, поскольку это кажется правильным способом сделать это.

Ответ №2:

Проблема заключается в вашем operator >>

Когда вы делаете:

 std::wstring t;
stream >> t;
  

Вы читаете до следующего пробела. В XML это после закрывающего тега, поэтому вы читаете часть потока, которую boost ожидает, что сможет прочитать там.

Решение состоит в том, чтобы либо принудительно ввести дополнительный пробел в выходные данные, либо изменить operator >> , чтобы отказаться от чтения чего-либо вроде ‘<‘ . Я предполагаю, что ранее у вас было дополнительное пространство в строке, которую вы сохраняли и восстанавливали.

Предполагая, что вы можете установить / присвоить из std::wstring и получить какое-то исходное значение, которое можно использовать для создания std::wstring, это работает на моей машине:

 #include <boost/serialization/split_free.hpp>
  

 namespace boost {
namespace serialization {

  template<class Archive>
  void load(Archive amp; ar, JuceString amp; j, const unsigned int version)
  {
    std::wstring tmp;
    ar amp; boost::serialization::make_nvp("value", tmp);
    j = tmp;
  }

  template<class Archive>
  void save(Archive amp; ar, const JuceString amp; j, const unsigned int version)
  {
    const std::wstring tmp(j.toWideCharPointer());
    ar amp; boost::serialization::make_nvp("value", tmp);
  }

} // namespace serialization                                                                                                                                            
} // namespace boost                                                                                                                                                    
BOOST_SERIALIZATION_SPLIT_FREE(JuceString)
  

Это избавляет от необходимости записывать operator >> и позволяет boost напрямую обрабатывать чтение / запись wstring в архив. Вам нужно записать его только один раз, чтобы избежать проблем с дублированием, а затем вы можете использовать JuceString так часто, как вам нравится.

Комментарии:

1. Дело в том, что раньше это работало нормально. И, кроме того, оператор std::basic_istream для std::wstring работает практически таким же образом, поэтому std:: wstring должен показывать ту же проблему, чего не происходит.

2. Я думаю, что на самом деле есть более приятное исправление, подождите

3. К сожалению, нет, у меня тоже не было пробела раньше … : (

4. @Robert — обновлено, разделив загрузку / сохранение, вы можете позволить архиву boost позаботиться о том, как читать std::wstring за вас.

5. @awoodland: Ценю, но это именно то, чего я не хотел. Поскольку я собираюсь использовать большое количество juce :: String на протяжении сериализации, этот подход вскоре станет утомительным и подверженным ошибкам… О, и, кроме того, в (реальном) juce::String нет std::wstring для использования (в моем примере используется std::wstring для простоты)

Ответ №3:

Я не знаю, есть ли у вас ответ для вас, но при сравнении кода в моей рабочей области для сериализации ATL CString я получаю следующее:

 inline void serialize(Archive amp; ar, CString amp; s, const unsigned int file_version) 
{
    std::wstring ss( s );
    ar amp; BOOST_SERIALIZATION_NVP(ss);
    s = ss.c_str();
}
  

Мне интересно, могли бы вы сделать что-то подобное в своем operator << — не уверен, применимо ли это решение к вам или нет. Заранее приношу извинения, если нет.

Комментарии:

1. Спасибо, это действительно работает, но затем он добавляет (используя XML) дополнительный элемент «ss», чего я хотел бы избежать. Хотя чисто косметически … 🙂