13

The C++17 filesystem is based on boost.filesystem.

I am using it on Windows with VS2017 right now.

#include <filesystem>
namespace fs = std::experimental::filesystem;

I iterate over a directory

for (auto& p: fs::directory_iterator("media"))

I want to pass the paths to a function that takes filepaths as const char *

I found a similar question about the boost filesystem here. A core difference is that the path in C++17 is based on a value_type. where

value_type: character type used by the native encoding of the filesystem: char on POSIX, wchar_t on Windows

So what I get is a const wchar_t * string.

The following "works" for me:

    char file[2000];
    wcstombs(file, p.path().c_str(), 2000);
    auto image = SDL_LoadBMP(file);

I am looking for a different version since this implementation is all sorts of messy (decaying array to pointer and _CRT_SECURE_NO_WARNINGS).

I am looking for a prettier version that can go straight from path to const char * using the new C++17 filesystem on windows.

Here is my SDL2 project that I used to explore this with.

#define _CRT_SECURE_NO_WARNINGS 1
#include <SDL.h>
#include <vector>
#include <filesystem>
namespace fs = std::experimental::filesystem;

int main(int argc, char* argv[])
{
    SDL_Init(SDL_INIT_EVERYTHING);
    auto window = SDL_CreateWindow("Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 400, SDL_WINDOW_SHOWN);
    auto scrrenSurface = SDL_GetWindowSurface(window);

    auto images = std::vector<SDL_Surface*>();

    for (auto& p: fs::directory_iterator("media"))
    {
        char file[2000];
        wcstombs(file, p.path().c_str(), 2000);
        auto image = SDL_LoadBMP(file);
        images.push_back(image);
    }

    for (auto&image : images)
    {
        SDL_BlitSurface(image, NULL, scrrenSurface, NULL);
        SDL_UpdateWindowSurface(window);
        SDL_Delay(2000);
    }

    for (auto&image : images)
        SDL_FreeSurface(image);

    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

EDIT: In the comment section there is a simmilar question linked so-question That questions is a different manifestation of the same core issue which is converting path to a format that can be consumed by another method. I would argue against deleting this question because of the way both are found with different searches.

Johannes
  • 6,490
  • 10
  • 59
  • 108
  • There should be a definition you can use in a header called MAX_PATH. – SPlatten Jan 09 '19 at 11:41
  • Seems like a duplicate of https://stackoverflow.com/q/54007437/560648, potentially? – Lightness Races in Orbit Jan 09 '19 at 12:31
  • Unfortunately, MAX_PATH has the old limit, not the current limit of 32768 UTF-16 encoding units. – Eljay Jan 09 '19 at 12:38
  • 1
    @Eljay this is only partially true. At least [according to MSDN](https://learn.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation), one can exceed the `MAX_PATH` limit only for absolute paths with a \\?\ prefix. And even then, the limit will not necessarily be exactly 32768 but a bit less… – Michael Kenzel Jan 09 '19 at 13:02
  • 2
    @MichaelKenzel a little further down on that same MSDN page: "*Starting in Windows 10, version 1607, MAX_PATH limitations have been removed from common Win32 file and directory functions. However, you must opt-in to the new behavior.*" So you don't need \\?\ anymore if you opt in. – Remy Lebeau Jan 09 '19 at 16:57
  • @RemyLebeau oh, that's cool, no idea how I missed that. Thanks for pointing that out! – Michael Kenzel Jan 09 '19 at 21:38

1 Answers1

27

You can use the *string() and generic_*string() member functions of the std::filesystem::path object to convert the path into either a native or a generic (i.e., POSIX-style) string. For example:

SDL_LoadBMP(p.path().string().c_str());

Note that these methods return an std::basic_string<T> value. So one must make sure that, e.g., a pointer obtained from calling .c_str() directly on the return value—like in the example above—does not outlive the string it points to…

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • 1
    I tested it and this is what I was looking for... now I need to wait 7 Minutes to accept your answer. :) – Johannes Jan 09 '19 at 11:41
  • 11
    Note that the returned string is a temporary, and therefore will be destroyed at the end of the full expression. This code may be fine assuming SDL doesn't store the pointer for later use, but it should be made clear to future visitors that storing `string().c_str()` for later use won't work. – eerorika Jan 09 '19 at 11:50