Home > database >  Idiomatic way to handle T[]-like objects in C
Idiomatic way to handle T[]-like objects in C

Time:01-25

I am using some C-library in my C code. The library wants me to allocate some amount of the memory and pass the pointer to the library. Unfortunately, the exact required memory size is not known in advance, so the library also requires me to provide C-callback with the following signature:

void* callback_realloc(void* ptr, size_t new_size);

where ptr is previously passed memory and new_size is required size, and the callback must return the pointer to newly allocated memory. There is no direct way to store the allocator state. Instead, I need to rely on pointer arithmetic somehow as the following:

template<class T>
struct o_s {
    std::aligned_storage_t<sizeof(T), alignof(T)> data;
};

template<class Alloc>
struct o_i: private Alloc {
    std::size_t allocated_size;

    const Alloc& get_allocator() const { return *this; }
};

template<class T>
struct o: public o_i, public o_s<T> {
    void* ptr() {
        return &data;
    }

    // Additionally, override class operator new and operator delete...
};

void* my_callback(void* ptr, size_t new_size) {
    auto meta = static_cast<o*>(reinterpret_cast<o_s<char>*>(ptr));
    // access to the allocator state ...
}

Then sizeof(o_i) initial_size memory is allocated and ptr() is passed to the C library.

At this point, I understand that I am not the first person in the world who needs this pattern. Unfortunately (and surprisingly) I have not found anything suitable for this in Boost or STL. I would like to use ready implementation to avoid possible underwater rocks.

CodePudding user response:

The simplest solution is to allocate the memory using std::malloc, and use std::realloc as the callback. C API such as this are the case where using those makes sense in C .

You don't necessarily have to use std::malloc however. You can implement a custom allocator if you want to. Using some of the allocated storage is one way of storing allocation metadata, and it's an efficient way. That's not necessary either though, since you can also store the metadata separately in a map-like structure.

CodePudding user response:

I don't think you'd find an idiomatic way to do this in C , as this is a C idiom. Conceptually, you have two ways of going about this:

  1. Store the metadata alongside the buffer you've allocated (as you seem to be doing). I feel using double inheritance etc. is a bit overkill here, when you could just allocate a char buffer of sizeof(size_t) allocation_size and use the first part for your metadata.
  2. Allocate and return to the API a raw buffer as needed, and use a separate static data structure to manage this with a map of ptr->allocation size. I suppose this is what malloc/realloc is doing behind the scenes anyway.

Both are valid, and the one you chose depends on the specific details of your application. When dealing with memory it's ok to actually deal with memory, be it pointer arithmetic or what not. The important thing is probably to keep the ugly bit to a single location, and provide a C style API to this on the C side of things, so that the client code isn't exposed to implementation details.

  •  Tags:  
  • Related