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.
Not entirely, but subtly yes. A directory separator (in the generic format) is either the preferred separator, or the fallback separator: /. On systems where the preferred separator is / (e.g. POSIX), directory separator is only /. On such systems make_preferred doesn't modify the path, which explains why it would be optimised out completely.
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.
The path name has the following syntax: 1 root-name(optional): identifies the root on a filesystem with multiple roots (such as "C:" or "//myserver" ). In case of... 2 root-directory(optional): a directory separator that, if present, marks this path as absolute. If it is missing (and the... 3 Zero or more of the following: More ...
An implementation that allows alternative directory separators will treat them as directory separators. But other, perfectly valid, implementations that don't recognize those separators will not recognize them. "/" is always a directory separator; whether "\" is a separator or not depends on the implementation.
Am I misunderstanding the purpose of
make_preferred()
?
Not entirely, but subtly yes. A directory separator (in the generic format) is either the preferred separator, or the fallback separator: /
. On systems where the preferred separator is /
(e.g. POSIX), directory separator is only /
. On such systems make_preferred
doesn't modify the path, which explains why it would be optimised out completely.
The simplest way to replace back slashes with forward slashes is std::replace
. However, do note that back slashes are valid characters in file names in POSIX, so such conversion may break the use of file names that use it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With