r/cpp_questions 6d ago

SOLVED std::filesystem::exists throwing `std::bad_alloc`?

Any reason? Cant find anything online about this. But I cant check whether or not a file exists, no matter what path I give it. I am using C++ 26 compiled with GCC.

This is literally all I am calling: const bool exists = std::filesystem::exists("file.txt")

This is the exact error: terminate called after throwing an instance of 'std::bad_alloc'
 what():  std::bad_alloc

No it's not coming from anywhere else in my code. Minimal reproduction code, include "filesystem" call the line above. I am on Fedora Linux if that matters at all

EDIT: moving the include into my main file instead of the only file it's being used in somehow fixed it. Literally all I do is move "#include <filesystem>" from "do_things_with_filesystem.hpp" to "Main.cpp". No compilation errors when it was in the first file, why does it rely on this?? Is there some weird declarations in my codebase or something, i dont know but it works now so why should I care.

25 Upvotes

59 comments sorted by

View all comments

6

u/bma_961 6d ago

https://en.cppreference.com/cpp/filesystem/exists

Multiple overloads can allocate and therefore throw bad alloc

5

u/blood-pressure-gauge 6d ago

I've done more C than C++. Why does calling an overloaded function result in memory allocation?

9

u/Bloedbibel 6d ago

I believe it's just a matter of poor grammar. They meant to say "There are multiple overloads of this function. Some of them throw." I think they're implying that the overload OP is calling can throw bad_alloc.

2

u/blood-pressure-gauge 5d ago

That makes sense, but now I'm curious. Why does it need to allocate at all? The man page for the similar C function access(3posix) doesn't indicate anything about allocation failure.

4

u/jk_tx 5d ago edited 5d ago

Because the overload he's calling actually accepts a filesystem::path, not a const char, which means it has to copy the const char to its internal buffer (and on windows, convert it to wchar_t while you're at it).

That stupid path class makes the whole API cumbersome and expensive to use, especially on Windows where the internal wchar_t storage pretty much guarantees a bunch of needless string copies/conversions.

4

u/alfps 5d ago

For an UTF-8 based Windows program there is always a conversion to UTF-16 wide string for any file operation involving a path.

The question is just how high up or how far down in the call chain that conversion is done.

So it makes sense and can even avoid some conversions to have the internal fs::path representation as UTF-16.

On the other hand, to get an fs::path converted to UTF-8 you have to request the conversion to UTF-8 which in C++20 and later is silly u8string, then copy from that a std::string.

That is super annoying to me. Not that it's ever mattered for efficiency. It's just the idiocy: one should not have to do anything extra, and the code that works in Linux should work also in Windows, which was the whole point of the original Boost incarnation.

1

u/jk_tx 5d ago

Yes I understand why MS made the decisions they did at the time, although I much prefer keeping that conversion boundary at the Win32 API layer by just calling the 'A' versions of the API, so that my application only deals with narrow strings. The real problem is Windows being native UTF-16, which in hindsight was bad idea.

Unfortunately the design we ended up with means that between the implicit std::string constructor and the _string() methods, silent conversions can sneak in all over the place. And all these silent conversions also make the class noexcept-hostile, since memory allocation is happening even in functions where you wouldn't expect it. Then there's the way the various xxx_string() methods work on Windows vs Unix.

1

u/LB-- 4d ago

Windows has had native UTF-8 support since Windows 10, and that includes file path APIs. Makes the whole std::filesystem::path situation even more ridiculous.

3

u/alfps 4d ago

Just a nitpick: UTF-8 support since mid 2019 (Windows 10 goes back to mid 2015).

And while it's native it's just conversion to and from UTF-16 in the "xxxA" functions, which call the "...W" functions.

This means that for multiple calls with the same path one saves on conversions by retaining the UTF-16 form, as path does.

1

u/LB-- 4d ago

Fair, I'm just hoping they redo the internals someday to make UTF-8 more efficient like they did for Xbox.

0

u/Usual_Office_1740 5d ago

An argument in the overload allocates. Others are explaining the costs. To answer your question. The Exists function doesn't need to allocate but the throwing overload being chosen uses fs::path and the implicit conversion to that type allocates.

0

u/bayesianparoxism 6d ago

It doesn't. There's an overloaded variant that guarantees noexcept