Implementing C realloc in a macro with new and delete
Asked Answered
F

1

1

I'm using a library that lets me override some of it's macros for how it allocates to the heap

#define STBI_MALLOC(sz)           malloc(sz)
#define STBI_REALLOC(p,newsz)     realloc(p,newsz)
#define STBI_FREE(p)              free(p)

I'd like to accept some of it's allocated heap into my program without wrapping it or worrying too much about how it's allocated, by making the STBI_FREE() macro synonymous with how I'd normally deallocate such memory, with a delete [](char*)p

#define STBI_MALLOC(sz)           ((void*)new char[sz])
#define STBI_REALLOC(p,newsz)     ???
#define STBI_FREE(p)              (delete [](char*)(p))

I'm unsure of what to use for STBI_REALLOC() though. I could do it in multiple lines if I had the previous size, but...

// STBI_REALLOC(p,newsz)
if (newsz <= ???) {  // don't know the allocation size
    return p;
} else {
    char * next = new char[newsz] 
    memcpy(next, p, ???); // don't know the allocaion size
    delete [] (char*)p;
    return (void*)next;
}

...without knowing the allocation's size intrinsically, it's not possible with the arguments the macro accepts within the library. Do I have an option here that I can slot into this macro definition?

--- edit ---

(The accepted answer still stands, and this is outside the scope of the original question, but I'm appending this in case anyone using the same library encounters it)

Turns out this library had a hidden section of preprocessor for this exact scenario, where the library will instead use STBI_REALLOC_SIZED(p,oldsz,newsz) if it's defined by the library user.

which allows me to define for the library the following Absolute Masterpiece:

#define STBI_MALLOC(sz) ((void*)(new char[sz]))
#define STBI_REALLOC_SIZED(p,oldsz,newsz)   \
    ((newsz) <= (oldsz) ? (void*)(p) : [](void* lp, size_t loldsz, size_t lnewsz)->void*{char*n = new char[lnewsz]; memcpy(n, lp, loldsz); delete [] (char*)lp; return (void*)n;}(p, oldsz, newsz))
#define STBI_FREE(p) (delete [](char*)(p))

Or this one works quite well too

#define REALLOC_SIZED(p,oldsz,newsz) \
    ((newsz) <= (oldsz) ? (void*)(p) : \
    memcpy(new char[newsz], (void*)std::unique_ptr<char>((char*)(p)).get(), oldsz))
Fertilize answered 23/4, 2021 at 7:2 Comment(7)
First off, I'd say this is a bad idea. If it requires realloc, then you should use the C library functions. Otherwise, you will need to change your calls to also store the size of the allocation and return a pointer following that. This is essentially what memory managers do internally and so you're duplicating behavior (i.e. wasting memory). new and delete are not direct replacements for malloc and free, and there is no realloc equivalent.Isabellisabella
I don't think there's a portable way to get the allocation size from a pointer, you would need to keep that information on the side, maybe by inserting extra data at the start of your allocated memory, that will keep the size of allocated data. Or you could write your own allocator that would keep track of all of its allocation, and you would expose it to the library.Chickenlivered
@Isabellisabella - ah, I know. I tried to avoid using malloc even though I like it's behavior in more situations, since mixing it with new allocations is a true source of mistakes, and using new is unavoidable. I suppose I should just embrace mixing the two and get good at pairing the correct allocation/deallocation callsFertilize
This seems like an xy problem. Can I ask what problem you're actually trying to solve with this? As it stands any use of STBI_MALLOC/STBI_REALLOC needs to be accompanied by a corresponding call to STBI_FREE. Redefining all three doesn't change that -- does it?Thynne
The thing is, if the library needs that, it doesn't need to be any of your business. Just let it use the C library functions and be done with it. The main reason for providing these kinds of macros is to provide hooks for invasive debugging techniques such as logging allocations, or for high-level memory management techniques such as custom pool allocators in more advanced projects.Isabellisabella
@Thynne I wanted to avoid needing to wrap the allocations by changing the library as unintrusively as possible, just for "ergonomics". Then ended up on a snag that made me more curious than anything, to if this could even be done.Fertilize
It's no problem to investigate this and discover the problem, like you've done. The outcome is that you've asked a fair question and gotten some feedback about it. This might not be quite the feedback you expected, but that is how you broaden your horizons.Isabellisabella
K
1

TL;DR - It can't be done, leave the code as is.

There is no realloc possibility in C++. realloc goes against the object-oriented nature of C++, as objects can't be moved around like that, so C++ outright dismissed the idea.

Nor can one be implemented in terms of new and delete, as you need to know the current array size to copy the data, which is impossible (in a portable way), not to mention it being inefficient to have to copy stuff around every time (realloc in many cases works in-place).

The good news is, there is nothing wrong with using malloc, realloc and free for allocating raw memory in a C++ program. Also the built-in new and delete in many cases delegate to malloc and free, so there is no issue really.

Kenric answered 23/4, 2021 at 7:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.