Home > Mobile >  A memory-polymorphic slice function in C
A memory-polymorphic slice function in C

Time:01-09

Imagine an array_slice method which returns a part of an array. Imagine also that your program is using multiple memory allocation strategies, like Boehm, ref count, pools, alloc/free. Is there an idiomatic or somewhat non-contrived way to do this array_slice function in a way that it's agnostic about the memory allocation?

Small code-snippet for context.

typedef struct array array;
struct array {
    size_t length;
    uintptr_t* thing;
};
array array_slice(array old, int offset)
{
    size_t new_length = old.length - offset;
    array new = {
        .length = new_length,
        // TODO: This line needs to use same allocation strategy as for argument "old"
        .thing = malloc(sizeof(uintptr_t) * new_length)
    };
    size_t j = 0;
    for (size_t i = offset; i < old.length; i  ) {
        new.thing[j] = old.thing[i];
        j  ;
    }
    return new;
}

One solution could be for the array struct to carry with it a function pointer and other information. Another maybe using _Generic? Open for suggestions or links. :) Thank you.

One problem is that each memory allocation strategy requires slightly different ceremony around it, like ref count needs to bump or decrease the counter, pools need to pass around a pointer to the used pool object, normal malloc needs to be freed at the correct place.

CodePudding user response:

For dynamic polymorphism, you could define an abstract 'allocator' class:

struct Allocator {
    void *(*alloc)(struct Allocator *, size_t);
    void (*dealloc)(struct Allocator *, void *);
}
extern struct Allocator *malloc_allocator;
/* other allocator types here */

with an implementation like

void *malloc_alloc(struct Allocator *, size_t sz) {
    return malloc(sz);
}
void malloc_dealloc(struct Allocator *, void *ptr) {
    return free(ptr);
}

static struct Allocator malloc_alloc_obj = { malloc_alloc, malloc_dealloc };
struct Allocator *malloc_allocator = &malloc_alloc_obj;

You can define other allocator types similarly -- those that need extra data cn define a private struct that begins with a struct Allocator field and has the extra data following. Then you just need to add a pointer to your struct Array.

C does not provide good ways of doing static polymorphism. You can fake it with macros, but the result is pretty fragile.

CodePudding user response:

Imagine an array_slice method which returns a part of an array

You may be using the wrong abstraction here. Do all the users of array actually need to own a copy of the elements in the slice?

Separating the "slicing" from copying and ownership of the underlying memory would allow you to make and pass around slices very cheaply, and when a copy is actually needed, the owner of that copy can decide which memory allocation strategy is most appropriate for that particular copy.

In C , this light-weight slicing is implemented by the std::span class.

Your struct array could remain exactly the same, with the only difference that thing points to memory owned by someone else. The array_slice becomes much simpler and cheaper:

array array_slice(array old, int offset)
{
    assert(offset <= old.length); 
    array new = {
        .length = old.length - offset,
        .thing = old.thing   offset;
    };
    return new;
}
  • Related