1

I'm attempting to convert an app written in C++14 for linux/MacOS. It uses boost::filesystem, but not for certain iostream operations. For example:

boost::filesystem::path file = name;

std::ifstream fin(file.c_str());

This code was failing to compile on Windows 10 using MinGW with GCC 6.3 with the following:

error: no matching function for call to 'std::basic_ifstream::basic_ifstream(const value_type*)' std::ifstream fin(file.c_str());

I thought if I could convert the std::ifstream to boost::filesystem::ifstream I could get it to work... So I changed the code to this:

boost::filesystem::path file = name;

boost::filesystem::ifstream fin(file.c_str());
if (!fin)
{
    file = pathToAppData / "files/expansion/assets/resources/basestation/config/mapFiles/racing" / name;

    fin = boost::filesystem::ifstream(file.c_str());
    if (!fin)
        throw std::runtime_error(std::string("Cannot open Anki Overdrive map file ") + file.string() + ".");
}

fin >> (*this);

That resulted in this error:

error: 'const boost::filesystem::basic_ifstream& boost::filesystem::basic_ifstream::operator=(const boost::filesystem::basic_ifstream&) [with charT = char; traits = std::char_traits]' is private within this context
fin = boost::filesystem::ifstream(file.c_str());

It looks like I can't re-assigned a boost::filesystem::ifstream once created... I was able to change that line to the following and it compiled, but I'm wondering if it's the correct way to do it:

boost::filesystem::ifstream fin(file.c_str());

Bonus question: Should this code work on linux as well once I get it working?

Brad
  • 1,684
  • 4
  • 20
  • 36

1 Answers1

4

On Windows boost::filesystem::path::value_type is wchar_t, because Windows paths use strings of 16-bit UTF-16 characters. According to the C++ standard the std::ifstream class only has a constructor taking strings of narrow characters. The Visual Studio standard library adds extra constructors to ifstream which take wide strings, but the GCC standard library used by MinGW does not have those extra constructors. That means the const wchar_t* returned by file.c_str() is the wrong type for the constructor argument to std::ifstream.

You can convert the path to a narrow character string (by calling file.string()) and pass that to the ifstream constructor, although I don't know if it will work correctly:

boost::filesystem::path file = name;
std::ifstream fin(file.string());

As you say, the boost::filesystem::ifstream is not assignable (before C++11 streams were not movable or assignable, and it appears that the Boost.Filesystem streams haven't been updated). You can simply change you code to re-open a new file using the same stream object, instead of trying to re-assign to it:

fin.close();
fin.open(file);

(Note that you don't need to call c_str() because the boost::filesystem::ifstream constructor taks a path argument anyway, not a pointer to a string. By calling c_str() you just convert the path to a string and then it gets converted back to another path, which wastes time and memory.)

Bonus question: Should this code work on linux as well once I get it working?

Yes. On GNU/Linux filesystem::path::value_type is char and so the original code would have worked fine anyway. The modified code will work on GNU/Linux too.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521