Hacker News new | past | comments | ask | show | jobs | submit login

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()`.


Good point, but the call could result in an allocation through the modification of a field of result

  typedef struct {
    void* some_data_pointer;
  } api_result_t;


  int get_result(my_api_t api, api_result_t* result) {
    result->some_data_pointer = realloc(foo_size + MAGIC_VALUE);
    // ...
  }


> 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.


If there is no void free_result(api_result_t *) in the API, then it probably doesn't allocate.


Be careful: free(3) could be the deallocation function.


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.


I did the same thing as you.

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?


On Apple's platforms, Instruments has a memory allocation logging tool that's very nice to use. Just attach to a program, no need to recompile:

https://www.agnosticdev.com/blog-entry/ios/profiling-memory-...

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.


Go's pprof comes with a great memory (and cpu + more) profiler: https://blog.golang.org/profiling-go-programs


jemalloc has a lot of profiling features you can turn on.


This falls into your "functions that might block" stipulation, but it should also be made obvious whether or not a function acquires a lock.


The HN appears to be different from the original.

"Let the callers of your API control allocations"

vs

"Don’t force allocations on the callers of your API"




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: