Understanding std::inout_ptr and std::out_ptr in C++23
Asked Answered
B

1

19

I've being reading the list of library changes proposed for C++23 and I'm quite curious about the std::out_ptr and std::inout_ptr (their _t siblings). As far as I understand they are some kind of wrapper for smart pointers to be compatible with raw pointers, but I haven't managed to understand them yet. Maybe someone here is familiar with the proposal or may give a less ISO-like explanation or examples?

Brendis answered 25/8, 2021 at 7:7 Comment(3)
I guess the best description you will get is from the proposal itself.Malaise
purpose is to use them for C-API which take/set owning pointer (void foo(SomeType**) whereas we would expect std::unique_ptr<SomeType> foo() C++-API).Rip
Also cppreference already contains some text regarding std::out_ptr_t and std::inout_ptr_t (the latter is a bit more elaborate and contains an example)Deathlike
T
19

TL;DR - it is for simpler and more seemless interoperability between C out/inout pointer parameters and smart pointers

Longer answer

Let's separate the stuff. std::out_ptr and std::inout_ptr are functions used to create objects of type std::out_ptr_t and std::inout_ptr_t respectively. What are those types and functions for? Let's look at an example inspired by this (for simplicity I replaced generic argument with good ol' int):

int foreign_resetter(int**);
auto up = std::make_unique<int>(5);
 
if (int ec = foreign_resetter(std::inout_ptr(up)) {
    return ec;
}

As you see std::inout_ptr_t created with std::inout_ptr is passed to function taking pointer to pointer to the template argument of std::unique_ptr. Before adding std::inout_ptr_t interoperation with old C in-out pointer parameters was much more cumbersome and error prone. It would look more less like this:

int foreign_resetter(int**);
auto up = std::make_unique<int>(5);
 
int* up_raw = up.release();
if (int ec = foreign_resetter(&up_raw)) {
    return ec;
}
up.reset(up_raw);

Differences out_ptr vs inout_ptr

From the proposal P1132:

inout_ptr's semantics are exactly like out_ptr's, just with the additional requirement that it calls .release() on the smart pointer upon constructing the temporary inout_ptr_t.

This is because the foreign_resetter might delete a pointer before setting a new one, calling .release() reserves that behavior and can be safer. Use inout_ptr if your pointer is already valid and allocated, and out_ptr if the smart pointer is empty.

Tarpon answered 25/8, 2021 at 7:57 Comment(10)
can you also mention what's the difference between out_ptr and inout_ptr?Digitalize
Your cumbersome version is buggy. You leak the object in case of an error. But that's actually easy to fix. The bigger problem is the inout_ptr version - it's actually more error-prone, since C APIs often don't guarantee the output is valid in case of an error. So you could end up with a garbage pointer in that case.Wylen
@Wylen In the example given, up can't be dereferenced in case of error, as we've returned away from it's scopeLymphangitis
@Caleth: The unique_ptr destructor runs unconditionally.Wylen
@Wylen So? It's fine to delete a null pointer. If you have an api that is writing an invalid pointer value then it's not an appropriate api to call like this, and I'd argue it's a bug in the api. Well behaved apis either don't change the pointer, or write a null pointer there.Lymphangitis
@Caleth: call it a buggy API if that makes you feel better, but APIs like that very much do exist, and people need to use them. Just one example: linux.die.net/man/3/vasprintf ("If memory allocation wasn't possible, or some other error occurs, these functions will return -1, and the contents of strp is undefined.")Wylen
@Wylen and you still can account for that case, up.release() in the error handler. I stand by my assertion that inventing an invalid pointer value out of nowhere is a bad api. From the link you gave, at least some implementations promise a valid pointer value even on error. It's also an api unsuitable for using with std::unique_ptr in the first place, because it requires you free() the pointer, not delete it.Lymphangitis
@Caleth: Nobody said you can't work around it, my entire point was that there was a bug in this pattern, which you seemed to have missed. And no, unique_ptr isn't just for delete, you can give it a different deleter for free() or whatever.Wylen
@Wylen In this example, it's the default deleter.Lymphangitis
@Caleth: In this example, you have no idea what foreign_resetter does either. It could very well allocate with new.Wylen

© 2022 - 2024 — McMap. All rights reserved.