I read the title as "don't force callers to allocate" not "don't do allocations behind the call" which is what the post is about. Totally agree on that point, but with nuance. Any call that allocates (or blocks) should be obvious in name and API.
For example a lot of C libs will have an API along these lines:
typedef void* my_api_t;
int create_api (my_api_t* api); // almost certainly allocates
typedef struct {
// yada yada yada
} api_result_t;
int get_result(my_api_t api, api_result_t* result); // does this allocate? I have no idea
I think every function that might block/allocate should be stupid obvious in the documentation and function naming convention, especially if you're distributing closed source libs with opaque types.
In your `get_result()` example, the caller won't be able obtain the address of the new allocation in case the callee allocates. The pointer itself is passed by value and any modifications on it by the callee are done on a local copy. If the function was allocating, it would require a double pointer like so:
int get_result(my_api_t api, api_result_t **result);
...
api_result_t *result;
get_result(api, &result);
A lot of confusion comes when API and library authors start typedef-ing pointer types into opaque types like that:
typedef api_result_t *API_RESULT;
...
API_RESULT res;
&res; // is this a single or a double pointer?
res; // is this a pointer at all?
If you meant that the function could possibly allocate fields of `result`, the API should always have a `free_result()`.
> If you meant that the function could possibly allocate fields of `result`, the API should always have a `free_result()`.
Yea that's what I meant. But this sort of gets into the weirdness of docs, if there is a `free_result` somewhere then authors should make sure to put a "see also free_result()" in their doc comments, especially when you don't have the niceties of constructors/destructors.
All depends on what "api_result_t" is, right? If the API's result is a bucket of scalars, then passing the pointer could be perfectly fine. Same if api_result_t contains a pointer to a buffer, and the call just makes the buffer pointer point to a previously allocated block.
Ideally memory handling and I/O should be overridable (and often is by creating some kind of "context"), but when it isn't then, yeah it should be obvious from the naming.
If it isn't - and especially if you're dealing with a closed source library - you have to assume the worst and even then you're often unpleasantly suprised :/
I read the title that way too, and the article almost seems like it's reinforcing that point until you get to the end. Kind of a weird almost unintentional form of clickbait.
Effectively what we have here is code allocating memory and handing it to you for care and feeding.
It's not unlike an unwanted gift. For some people this is an important and aggravating dynamic in their lives. I'm sure I've heard people complain about some aunt who "forces" plants or god forbid pets onto hapless relatives.
Are there tools that tell you where all your allocations come from? I imagine Valgrind but is there anything more lightweight?
I almost always write pool based memory allocators for my projects (CG, games), so I typically log that info myself. What would other folks use to profile that info?
As a nice bonus, it can also look for memory leaks by running what is essentially a conservative garbage collector on your heap and reporting unreachable allocations.
For example a lot of C libs will have an API along these lines:
I think every function that might block/allocate should be stupid obvious in the documentation and function naming convention, especially if you're distributing closed source libs with opaque types.