But this is far fetched because people doing manual memory management don't usually rely on exceptions, and just abort the program when this happens, so the leak is pointless.
I think you really, really underestimate the amount of codebases where a large part of the code is "normal" C++ with exceptions and someone does one specific module with manual malloc / free out of cargo-culting some RAII fears.
Hell, even OP above talks about using vector along with their custom memory management scheme (which throws no matter if you use -fno-exceptions or not, as the actual "throw" call is in the precompiled libc++.so ; the code in <vector> just calls std::__throw_bad_array_new_length() indiscriminately : https://gcc.godbolt.org/z/fno8fYP7E )
I think the actual problem is that std::vector is doing its own memory management under the hood, which makes it hard to control what's going on from the outside. E.g. a design flaw of the C++ stdlib, not related to RAII.
plenty of cases where this isn't the case.
-fno-exceptions just means that you want to not be able to throw for your own code.
-fno-unwind-tables / -fno-asynchronous-unwind-tables makes your own code un-unwindable which is only safe if you are in entire control of the whole codebase, which is very rare in anything that is not simple games - no plug-in system, or any dynamic code loading or JIT'ing is safe as you don't know which user-provided function you call is going to throw.
Likewise, you could be in the opposite case: imagine you're making a C++ plug-in for, say, 3DS Max, where error reporting is done through exceptions ; even if your own plug-in code is built with -fno-exceptions and does not itself throw at any point, you still want exceptions that may be coming from a 3DSMax function you called to propagate to your code back to the exception handler provided by 3DS Max - and for this RAII of your own types is needed if you don't want your own resources to be leaked.
std::vector::push_back can throw an exception, if for instance you just allocated 2 gigabytes of memory before and it does not have space to reallocate its internal storage:
std::vector<int> allocation_counts;
auto get_mem(int n) {
auto* memory = your_malloc(n);
// if this throws: the function exits there and "memory" is lost forever
allocation_counts.push_back(n);
return memory;
}
A simple way to "RAII-fy" it would be, assuming a `your_free`: // Typedef this somewhere
using your_ptr = std::unique_ptr<void, decltype([] (auto p) { your_free(p); })>;
auto get_mem(int n) {
// encapsulate in your_ptr
auto memory = your_ptr{your_malloc(n)};
// if this throws: memory's destructor is called which frees the memory
allocation_counts.push_back(n);
return memory;
}
Or if you want your custom RAII type for some reason: // memory.h or something
struct mem {
mem() = default;
// copy is not meaningful here
mem(const mem&) = delete;
mem& operator=(const mem&) = delete;
// move
mem(mem&& other): ptr{std::exchange(other.ptr, nullptr)} { }
mem& operator=(mem&& other) { ptr = std::exchange(other.ptr, nullptr); return *this; }
// allocation & deallocation
explicit mem(int n): ptr{your_malloc(n)} { }
~mem() { your_free(ptr); }
void* ptr{};
};
// your code
std::vector<int> allocation_counts;
auto get_mem(int n) {
auto memory = mem{n};
// if this throws: mem's destructor is called which frees the memory
allocation_counts.push_back(n);
return memory;
}
...assuming that there's a matching free_mem(ptr, n) which undoes the actions of get_mem() of course. Also I'm ignoring the cofusing use of auto vs auto*, I haven't caught up with modern C++ enough to know whether this even makes sense ;)
...also, what would the better RAII version of this function look like?