I'm working on code that involves loading a file from a path which is constructed as a concatenation of a given "base" path and a secondary relative path, loaded from another file. For example (and where I'm running into an issue), the base path is "assets/models/", and the secondary path is "maps\map.png". Straight concatenation of these two strings gives "assets/models/maps\map.png". When running on POSIX systems this fails to load. Up to now I've been sorting this out by just replacing the backslashes with forward slashes with
std::replace( path.begin(), path.end(), '\\', '/' );
but I'd like to use C++17's std::filesystem::path to do this instead.
The description of std::filesystem::path::make_preferred() suggests that it should replace the separators:
"Converts all directory separators in the generic-format view of the path to the preferred directory separator. For example, on Windows, where \ is the preferred separator, the path foo/bar will be converted to foo\bar"
When implemented in the code, however, it doesn't convert anything. I've also verified that std::filesystem::path::preferred_separator is as expected - '/'.
Am I misunderstanding the purpose of make_preferred()? Or am I just using it wrong?
Here's a cut down version of the code that doesn't work (this isn't the implemented code, but is close enough to it):
const char * loadedPath = "maps\\map.png"
std::string loadedPathStr = std::string( loadedPath );
auto wPath = std::filesystem::path( loadedPathStr );
wPath = wPath.make_preferred();
basePath = std::filesystem::path( "./a/b/" );
auto totalPath = basePath / wPath;
auto wStr = totalPath.generic_string();
std::cout << wStr << std::endl;
This outputs "./a/b/maps\\map.png"
When debugging the implemented code, it looks like wPath is optimised out; there's no way of inspecting it.
Strangely, when I compile and run this standalone test program, it works as expected:
int main(){
assert( std::filesystem::path::preferred_separator == '/' );
const char * cPath = "maps\\map.png";
std::string path = std::string( cPath );
auto wPath = std::filesystem::path( path );
wPath = wPath.make_preferred();
std::string wStr = wPath.generic_string();
std::cout << wStr << std::endl;
}
This outputs "maps/map.png". I can't read. This also outputs the incorrect value.
Anyone know whats going on here?
EDIT:
Tried compiling with clang (using gcc before), and it works as expected (separator is converted). Ignore this, made a mistake in recompiling.
I'm running this on Linux, and the path exists.
maps\map.pngin theassetssubdirectorymodels?\and/are both path separators, and the\is the preferred one. On POSIX,/is a path separator, while\is not a path separator, so make_preferred won't convert\(which is just a valid, normal filename character).make_preferredandgeneric_stringboth basically do the same thing, so you shouldn't be using both of them.make_preferredis for when you want the path's internally stored string to use the generic separator.generic_stringis for when you want a copy of the path string in the generic format.