How to Use mulle_allocator in Library Code vs Application Code: Best Practices

Library code should expose struct mulle_allocator * parameters or embed the allocator in data structures, while application code can rely on default convenience wrappers like mulle_malloc or install a custom global allocator.

The mulle_allocator library from the mulle-c project provides a flexible, interchangeable memory management layer for C programs. Whether you are building reusable libraries or standalone applications, understanding how to use mulle_allocator correctly ensures proper decoupling from the C standard library and supports custom allocation schemes. This guide explains the recommended patterns based on the actual implementation in mulle-c/mulle-allocator.

Library Code: Keep the Allocator Explicit

Library authors must avoid hardcoding dependencies on malloc and free. Instead, expose the allocator through your API to let callers provide custom allocation schemes such as shared memory, test allocators, or ABA-safe allocators.

Pattern 1: Embed the Allocator in Data Structures

When the allocation strategy remains constant for the lifetime of an object, store the struct mulle_allocator * pointer inside your data structure. This approach, shown in the README at lines 60-90, eliminates the need to pass the allocator to every subsequent operation.

struct my_object {
    struct mulle_allocator *allocator;
    int                     data;
};

struct my_object *my_object_alloc(struct mulle_allocator *allocator)
{
    struct my_object *obj = mulle_allocator_malloc(allocator, sizeof(*obj));
    obj->allocator = allocator;
    return obj;
}

void my_object_free(struct my_object *obj)
{
    mulle_allocator_free(obj->allocator, obj);
}

Embedding decouples the library from the C stdlib while reducing API surface area for allocation operations.

Pattern 2: Pass the Allocator as a Parameter

For maximum flexibility, require callers to pass the allocator to every function that allocates or frees memory. As documented in the README at lines 95-124, this pattern prevents storage overhead but risks mismatched allocators if the caller passes a different allocator than the one used for allocation.

void *my_library_alloc(struct mulle_allocator *allocator, size_t size)
{
    return mulle_allocator_malloc(allocator, size);
}

void my_library_free(struct mulle_allocator *allocator, void *ptr)
{
    mulle_allocator_free(allocator, ptr);
}

Both patterns are valid; choose embedding when the allocator stays constant, and passing when allocation strategies might vary per operation.

Application Code: Use Defaults or Set a Global Allocator

Application developers benefit from simplified memory management through convenience wrappers or global configuration, without needing to thread allocators through every function call.

Convenience Wrappers

For most programs, use the convenience wrappers defined in src/mulle-allocator.h at lines 75-78. These functions internally use mulle_default_allocator, abstracting away the explicit allocator parameter.

char *s = mulle_strdup("hello");
void *buf = mulle_malloc(1024);
void *zeroed = mulle_calloc(10, sizeof(int));
mulle_free(buf);

Custom Global Allocators

If your application requires a different allocation strategy—such as mulle_stdlib_allocator, a test allocator, or an ABA-safe allocator—you have two options:

  1. Replace the global pointer: Set mulle_default_allocator to your custom instance before any allocations occur.
  2. Pass explicitly to libraries: Supply a custom allocator to library functions that accept it, or pass NULL to use the default.
// Use standard library allocator explicitly
struct my_object *obj = my_library_create(&mulle_stdlib_allocator, args);

// Or use default (NULL means use mulle_default_allocator)
struct my_object *obj2 = my_library_create(NULL, args);

Memory Failure Handling

Application code does not need to check for NULL return values. According to the source code in src/mulle-allocator.h, the allocator's failure handler aborts on out-of-memory conditions, terminating the program with a diagnostic instead of returning NULL.

Summary

  • Library authors should expose struct mulle_allocator * in their APIs, either embedding it in data structures (preferred for constant allocators) or passing it to every allocation function.
  • Application developers can use convenience wrappers like mulle_malloc and mulle_free, which automatically use mulle_default_allocator.
  • Custom allocation schemes are supported by either replacing the global mulle_default_allocator pointer or passing custom allocators explicitly to library functions.
  • No NULL checks are required in application code because the allocator aborts on OOM failures.

Frequently Asked Questions

Should I check for NULL returns when using mulle_allocator?

No. The allocator's failure handler aborts the program on out-of-memory conditions rather than returning NULL. This design eliminates the need for defensive NULL checks in application code and ensures immediate failure with diagnostic information.

Is it better to embed the allocator or pass it every time?

Embedding the allocator in your data structure (as shown in README lines 60-90) is preferred when the allocation strategy remains constant for the object's lifetime. Passing the allocator to every function (README lines 95-124) offers more flexibility but increases the risk of allocator mismatches if callers are inconsistent.

How do I use a custom allocator without modifying library code?

Replace the global pointer mulle_default_allocator with your custom allocator instance at program startup. All subsequent calls to convenience wrappers like mulle_malloc will use your custom implementation. Alternatively, pass your custom allocator explicitly to library functions that accept a struct mulle_allocator * parameter.

What files define the allocator structure and global instances?

src/mulle-allocator-struct.h defines the struct mulle_allocator layout that you embed or pass around, while src/mulle-allocator.h declares the global allocator instances including mulle_default_allocator, mulle_stdlib_allocator, and mulle_stdlib_nofree_allocator, along with the public convenience API.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →