Search code examples
c++serializationiostreamcorrupt

Wide char input stream std::wifstream is getting corrupt while deserializing object which was serialized using std::wofstream


I am trying to write my own custom serialize and de-serialize for an object of my application. I know there are plenty of libraries like boost serialize etc. available for ready use but I wanted to learn this serialize and de-serialize hence this effort.

Problem occurs when I try to de-serialize(using std::wifstream) the object I had serialized(using std::wofstream). Not able to read even one class member correctly. First 3 members I am trying to de-serialize are bool, but they read incorrect values from the file stream. Can someone please suggest any pointers as to what could be the problem here. Thanks for your time.

Typedefs in application:

namespace fc
{
#ifdef UNICODE
    typedef std::wstring tstring;
    typedef std::wfstream tfstream;
    typedef std::wofstream tofstream;
    typedef std::wifstream tifstream;
    typedef std::wstringstream tstringstream;
#else
    typedef std::string tstring;
    typedef std::fstream tfstream;
    typedef std::ofstream tofstream;
    typedef std::ifstream tifstream;
    typedef std::stringstream tstringstream;
#endif

    typedef std::vector<fc::tstring> tstrvec;
    typedef std::vector<fc::tstring> tstrvec;
}

Object being serialized and de-serialize:

struct FastCopyCfg
{
    bool mOverwriteFiles;
    bool mCopySystemFiles;
    bool mCopyHiddenFiles;
    fc::tstring mDstFolder;
    fc::tstrvec mSrcFiles;
    fc::tstrvec mSrcFolders;
    fc::tstrvec mIncludeWildcards;
    fc::tstrvec mExcludeWildcards;

    FastCopyCfg()
    {
        mOverwriteFiles = false;
        mCopySystemFiles = false;
        mCopyHiddenFiles = false;
        mDstFolder.clear();
        mSrcFiles.clear();
        mSrcFolders.clear();
        mIncludeWildcards.clear();
        mExcludeWildcards.clear();
    }

    template<typename Archive>
    void Serialize(Archive& ar, const std::uint16_t = 0)
    {
        ar & mCopySystemFiles & mCopyHiddenFiles & mOverwriteFiles &
            mDstFolder & mSrcFiles & mSrcFolders & mIncludeWildcards & mExcludeWildcards;
    }
};

Global insertion and extraction operators overloaded:

void operator >>(IArchive& ar, FastCopyCfg& cfg)
{
    cfg.Serialize(ar);
}

void operator <<(OArchive& ar, FastCopyCfg& cfg)
{
    cfg.Serialize(ar);
}

Archiver classes

class OArchive
{
    std::wostream& mStream;

public:
    OArchive(std::wostream& stream) :mStream(stream){}

    template<typename Val>
    OArchive& operator &(std::vector<Val>& vtr)
    {
        mStream << vtr.size();
        std::for_each(vtr.begin(), vtr.end(), [&](auto e) {mStream << e; });
        return *this;
    }

    template<typename DT>
    OArchive& operator &(DT& data)
    {
        mStream << data;
        return *this;
    }
};

class IArchive
{
    std::wistream& mStream;

public:
    IArchive(std::wistream& stream) :mStream(stream) {}

    template<typename Val>
    IArchive& operator &(std::vector<Val>& vtr)
    {
        std::vector<Val>::value_type vElement;
        std::vector<Val>::size_type vSize = 0;

        mStream >> vSize;
        for (std::vector<Val>::size_type i = 0; i < vSize; i++)
        {
            mStream >> vElement;
            vtr.push_back(vElement);
        }
        return *this;
    }

    template<typename DT>
    IArchive& operator &(DT& data)
    {
        try
        {
            mStream >> data;
        }
        catch (const std::exception& e)
        {
            Logger::logDebug("Exception while reading config file: ", e.what());
        }
        catch (...)
        {
        }
        return *this;
    }
};

Serialization using code. I set the flag to true and false to debug serialization and de-serialization.

bool flag = false;
void CFastCopyDlg::OnBnClickedButtonSaveConfiguration()
{
    if (flag)
    {
        fc::tofstream file(fc::CONFIG_FILENAME, fc::tofstream::binary);
        OArchive ar(file);
        ar << getCopyConfig();
    }
    else
    {
        fc::tifstream file(fc::CONFIG_FILENAME, fc::tifstream::binary);
        IArchive ar(file);
        FastCopyCfg cfg;
        ar >> cfg;
    }
}

Solution

  • I think I found the problem. The issue is, de-serialization using any std fstream when file is opened in binary mode CAN NOT be done using the extraction operator >>. Similar topic was discussed HERE. Please refer below code to see where the problem existed in the question code.

    class IArchive
    {
        std::wistream& mStream;
    
    public:
        IArchive(std::wistream& stream) :mStream(stream) {}
    
        template<typename Val>
        IArchive& operator &(std::vector<Val>& vtr)
        {
            std::vector<Val>::value_type vElement;
            std::vector<Val>::size_type vSize = 0;
    
            mStream >> vSize;
            for (std::vector<Val>::size_type i = 0; i < vSize; i++)
            {
                mStream >> vElement;
                vtr.push_back(vElement);
            }
            return *this;
        }
    
        template<typename DT>
        IArchive& operator &(DT& data)
        {
            try
            {
                mStream >> data; // PROBLEM - BINARY DATA CANNOT BE READ USING >>
            }
            catch (const std::exception& e)
            {
                Logger::logDebug("Exception while reading config file: ", e.what());
            }
            catch (...)
            {
            }
            return *this;
        }
    };