Sunday, June 1, 2025

Careful with std::wfstream

wchar_t file streams

 The std::wfstream is similar to std::fstream except it accepts wchar_t. However it does not write std::wchar_t characters to file. Suppose the following code:

   std::wofstream ofs;
   ofs.open(L"c:\\temp\\1.txt" , std::ios_base::out | std::ios_base::binary) ;
   ofs.write(L"ABC", 3);

 On the Windows platform this writes just single bytes characters to the file. The reason is that the locale bound to the file stream translates wchar_t to char. This can be fixed by using a custom locale which doesn't do this. This translation is quite unexpected behavior since the function prototypes are defined in terms of wchar_t. It is also different compared to the wchar_t string streams: std::wstringstream does write wchar_t strings unaffected.

 This page was inspired by a YouTube comment of me where I stated that the C FILE api is less surprising. Of course there is always a clown who thinks better but probably doesn't know anything about above issue. With C stream I/O FILE and 'fwrite' the bytes are transferred to the file without interpretation and alteration.

FILE wrapper

 Jason Turner goes a lengthy way of wrapping the C FILE api but using a wrapper class would probably be simpler:

class c_file
{
public:
   c_file()
      : m_fp(nullptr)
   {
   }
   
   explicit c_file(const std::filesystem::path& rpth, const char* pszMode)
      : m_fp(fopen(rpth.string().c_str(), pszMode))
   {
   }

   ~c_file()
   {
      if (m_fp)
      {
         fclose(m_fp);
      }
   }

   c_file(const c_file&) = delete;

   c_file(c_file&& rOther) noexcept
      : m_fp(std::exchange(rOther.m_fp, nullptr))
   {
   }

   c_file& operator=(const c_file&) = delete;

   c_file& operator=(c_file&& rOther) noexcept
   {
      std::swap(m_fp, rOther.m_fp);
   }

   explicit operator bool() const
   {
      return m_fp;
   }

   size_t read(void* pBuffer, size_t size, size_t count)
   {
      return fread(pBuffer, size, count, m_fp);
   }

   size_t write(const void* pBuffer, size_t size, size_t count)
   {
      return fwrite(pBuffer, size, count, m_fp);
   }

   // etc.

private:
   FILE*    m_fp;
};

Links

No comments:

Post a Comment

Watch out for an old VC++ runtime

 VC_redist.x64.exe  For C/ C++ applications the VC++ runtime needs to be installed on the computer. The other day we experienced crashes whe...