OK, I'll give you that one, but that's a rather unusual case. It has complicated semantics. It is unlikely to interoperate well with any smart pointer without careful, manual work.
It doesn't take a `T**` so it doesn't seem relevant to discussions of std::out_ptr though? You won't be able to use std::out_ptr with realloc so there's no chance of making a mistake with it.
> Generally when you have an in-out parameter, it's doing more than just releasing resources
I'm still confused. What function do you have that is both releasing resources and also doing other work which might fail? Even something like `fclose` which needs to flush buffers and then release resources will always release the resources, even if the flush fails.
I'm more familiar with APIs that have:
- One (or more) functions to explicitly allocate the object
- Many functions that do work with that object and that might fail but that will never deallocate the object
- One function to explicitly deallocate the object
For example, SQLite has sqlite3_open to create a new database object, sqlite3_prepare, sqlite3_exec, etc. to operate on the database object, and sqlite3_close to deallocate the database object.
> It doesn't take a `T**` so it doesn't seem relevant to discussions of std::out_ptr though? You won't be able to use std::out_ptr with realloc so there's no chance of making a mistake with it. [...]
The problem is the same, it just happens to not take a T**. The problem is the same for any function that takes T**, I just couldn't think of one off the top of my head. I listed some below now. But also note that the absence of many APIs taking T** would also be an argument for these smart pointers not being so useful, so it's not really a counterargument!
> What function do you have that is both releasing resources and also doing other work which might fail?
See for example getdelim() or even asprintf(). asprintf() isn't guaranteed to return a valid pointer due to an error, so you can't free it unconditionally. getdelim() also isn't guaranteed to free the input if there is an I/O error, so you can't release that unconditionally either.
It would be helpful if you try to list some functions you believe you can use std::inout_ptr on.
I'm not sure what issue you're seeing with getdelim(). It never releases the underlying memory, AFAICT. You always need to call free at the end of the loop, regardless of whether there was a read error, a realloc failure, you hit EOF, or you broke out of the loop early. So why wouldn't the following be OK?
std::unique_ptr<char, free_deleter> line;
size_t len = 0;
while ((read = getdelim(std::inout_ptr(line), &len, delim, fp)) != -1) {
// ...
}
If the getdelim call ends up calling realloc and succeeding then we want to forget about the old pointer and not call `free` on it. We want to remember the new pointer and call `free` on that when we're done with the loop. That's what std::inout_ptr gets for us. It calls release() before the getdelim call and then calls reset() with the new value after the getdelim call.
If there is an I/O error or a realloc failure at some point in the file then `line` is left unmodified, pointing to the existing buffer. We still need to free that so keeping it under the control of the smart pointer is the right thing to do.
This seems like a perfect fit for std::inout_ptr.
The situation asprintf() is unfortunate. If it had been designed to set the out parameter to NULL on error or just to guarantee that it was unmodified on error then it would work with std::out_ptr. Unfortunately they decided that the out pointer would be instead be undefined on error so it is not safe to use with std::out_ptr.
OK, I'll give you that one, but that's a rather unusual case. It has complicated semantics. It is unlikely to interoperate well with any smart pointer without careful, manual work.
It doesn't take a `T**` so it doesn't seem relevant to discussions of std::out_ptr though? You won't be able to use std::out_ptr with realloc so there's no chance of making a mistake with it.
> Generally when you have an in-out parameter, it's doing more than just releasing resources
I'm still confused. What function do you have that is both releasing resources and also doing other work which might fail? Even something like `fclose` which needs to flush buffers and then release resources will always release the resources, even if the flush fails.
I'm more familiar with APIs that have:
- One (or more) functions to explicitly allocate the object
- Many functions that do work with that object and that might fail but that will never deallocate the object
- One function to explicitly deallocate the object
For example, SQLite has sqlite3_open to create a new database object, sqlite3_prepare, sqlite3_exec, etc. to operate on the database object, and sqlite3_close to deallocate the database object.