-1

I have this simple ReadTextFile(full_path) function:

std::wstring CEngine::load_text_file(std::wstring &full_path)
{
    std::wstring buffer = m_text_file_cache[full_path];

    if (buffer.empty()) // Load file:
    {
        std::wifstream wif(full_path);

        wif.seekg(0, std::ios::end);
        buffer.resize(wif.tellg()); // On Debug/Release x86: Warning C4244: 'argument': conversion from 'std::streamoff' to 'const unsigned int', possible loss of data
        wif.seekg(0);
        wif.read(buffer.data() , buffer.size()); // On Debug/Release x86: Error C2664: 'std::basic_istream<wchar_t,std::char_traits<wchar_t>> &std::basic_istream<wchar_t,std::char_traits<wchar_t>>::read(_Elem *,std::streamsize)': cannot convert argument 1 from 'const wchar_t *' to 'wchar_t *'

        m_text_file_cache[full_path] = buffer;
    }

    return buffer;
}
  • m_text_file_cache is just a std::map cache to reduce disk I/O. Ignore it.

When I compile to x64 (main track) there are no issues, but when I compile to x86 (for curiosity) there are 2 issues that I marked with comments in the code: Warning C4244 & Error C2664.

Amit G.
  • 1,842
  • 2
  • 17
  • 25
  • 2
    `std::string::data` returns a `const` pointer until C++17. I wonder if the x64 library implementation is a bit ahead of the x86 library implementation. You should be able to take a look at the definition of `data` in the header (or headers) to see what's up. – user4581301 Jul 11 '18 at 20:48
  • 3
    The reason for the warnings should be obvious. In a 32bit app, a string uses 32bit indexes and sizes, but `tellg()` returns a 64bit value instead, so you won't be able to store files > 4GB. And `wstring::data()` does not return a non-`const` pointer until C++17. There are many different ways that you can [read a file into a string](https://stackoverflow.com/questions/116038/), try one of the other alternatives that doesn't rely on 32bit/64bit data types. – Remy Lebeau Jul 11 '18 at 20:48
  • `tellg` is a bit of a weirdo. All it guarantees is you get a token you can use to return to the same point in the stream. This may or may not be relative to the beginning of the file, so you can't count on seeking to the end and then calling `tellg` to provide the size of the file. You're clearly targeting MSCV, and the seek and tell trick works (for now anyway), but this may bite if you you have to port the code elsewhere. – user4581301 Jul 11 '18 at 20:53
  • @user4581301 The problem was that I set C++17, in VS IDE, unintentionally just for the x64, instead of for all configurations. Xirema guessed it correctly. I fixed it in the IDE and the error gone. – Amit G. Jul 11 '18 at 21:15
  • @AmitG. Thanks. I gathered that from you comment below. Good that it's not quite the nightmare case I thought it could be. Didn't know you could set the standard in MSVC. Don't use it all that much, and for the cases I do, I'm still trying to claw my company above MSVC 2010 where standard compliance really doesn't matter all that much. – user4581301 Jul 11 '18 at 21:34

2 Answers2

3

The warning is being caused because, even though you're in x86 mode, Files are still presumed to (possibly) be larger than 4GB, which means their size types are 64-bit. 64-bit integers truncate to 32-bit integers with a warning, which your compiler is providing. static_cast to unsigned int to fix.

The Error is being caused because your x86 configuration probably isn't compiling in C++17 mode, and as a result, the data() function returns wchar_t const*, instead of C++17's behavior of returning wchar_t *. Change your compiler flags to compile in C++17 mode, and you won't need the cast anymore. It's probably best if you set the flag for "all" configurations so you won't have to manually change both in the future.

Also, don't use C-style casts to fix this issue, like (wchar_t*)buffer.data(), as that can potentially hide this issue until it explodes at a later part of your code. Prefer const_cast if you're forced with using pre-C++17 for this code:

wif.read(const_cast<wchar*>(buffer.data()) , buffer.size());
Xirema
  • 18,577
  • 4
  • 26
  • 60
  • Instead of using `const_cast`, you can use `operator[]` instead: `wif.read(&buffer[0], buffer.size());` – Remy Lebeau Jul 11 '18 at 20:51
  • @RemyLebeau You can, but I would never recommend it, due to the kludge it produces. This is a perfectly valid and standard-compliant use of `const_cast`. – Xirema Jul 11 '18 at 20:52
  • @Xirema You are right. I set C++17 in the IDE unintentionally just for the x64, instead of for all configurations. Now it make sense; error gone. – Amit G. Jul 11 '18 at 21:06
  • 1
    @Xirema what kludge would that be? `operator[]` does not perform bounds checking, so `buffer[0]` would represent the same address as `buffer.end()` when `buffer.size()` is 0, and it is safe to refer to past-the-end of a memory buffer as long as you don't dereference it, which `read()` wouldn't when `buffer.size()` is 0. And besides, `operator[]` is guaranteed to return a valid address in C++11 and later even if `size()` is 0 (before C++11, it would return an undefined address, but that is OK since `read()` wouldn't access the address anyway). – Remy Lebeau Jul 11 '18 at 21:06
0

(The warning issue)

It seems the warning on x86 is due to differences in the definition of size_t on x86 vs x64: "size_t (unsigned __int64 or unsigned integer, depending on the target platform)" - MSDN. "size_t is a 64-bit value on 64-bit Windows operating systems" - MSDN. And MSVC determines that tellg() return-type is std::streamoff (typically, a typedef to long long).

std::wstring :: resize (size_type n);

From the standard perspective, size_t definition discussed here.

Amit G.
  • 1,842
  • 2
  • 17
  • 25
  • `size_t` is for sizes of objects (in memory). No reason to expect it can hold file offsets on systems with large-file support. – Peter Cordes Jul 12 '18 at 00:33