snippetcppMinor
Implementing `create` and `destroy` functions to replace `new` and `delete` operators
Viewed 0 times
newcreatedeletereplaceandfunctionsimplementingoperatorsdestroy
Problem
The environment I'm working in requires that all allocation is done through a special set of
For this reason, I have tried to write a couple of C++ functions which make it easier to create and destroy C++ objects:
Can this be done smarter? And are there any cases where my
malloc/calloc functions and that a user ID is provided when allocating. The environment has no implementation of exceptions, so errors are signaled by returning NULL.For this reason, I have tried to write a couple of C++ functions which make it easier to create and destroy C++ objects:
namespace details {
template
typename enable_if::value, T>::type *
create(uint32_t module_id) {
// use module_id
return (T *)calloc(1, sizeof(T));
}
template
typename enable_if::value, T>::type *
create(uint32_t module_id, Args&&... args) {
// use module_id
T *p = (T *)calloc(1, sizeof(T));
if (p)
new (p) T(forward(args)...);
return p;
}
template
void destroy(typename enable_if::value, T>::type *t) {
if (t)
free(t);
}
template
void destroy(typename enable_if::value, T>::type *t) {
if (!t)
return;
t->~T();
free(t);
}
};
template
T *create(uint32_t module_id, Args&&... args) {
return details::create(module_id, forward(args)...);
}
template
void destroy(T *t) {
details::destroy(t);
}Can this be done smarter? And are there any cases where my
create/destroy functions are not handling as one would expect?Solution
After some research, I'm convinced Christopher Creutzig is right. If you want all your allocations to go through customized functions, you're much better off replacing the global operator new. This will catch all calls to
If you need to pass a module id,
As an aside, if you're particularly tied to calling functions, I'd be tempted to remove the SFINAE specialization via
new for the translation unit, whether you replace them with create or not. That means when you create a std::vector myvec(3), it will use your operator new for any heap allocations it needs to do, instead of sidestepping your global policy.If you need to pass a module id,
operator new can handle that as well by accepting extra parameters. I'm unclear how that mixes with examples such as the std::vector heap allocations; it may require using a custom allocator for each STL container. Setting that aside, here's an example like yours following the lead on cppreference.com:void* operator new(std::size_t cb, uint32_t module_id)
{
// use module_id
return calloc(1, cb);
}
void operator delete(void* ptr)
{
free(ptr); // I'm unsure if this requires a null check
}
type *p = new(module_id) type;As an aside, if you're particularly tied to calling functions, I'd be tempted to remove the SFINAE specialization via
is_pod and just make it an inline check via is_trivially_constructible and is_trivially_destructible...that is if checking is necessary at all.template
T* create(uint32_t module_id)
{
// use module_id
T* p = calloc(1, sizeof(T));
if (!is_trivially_constructable::value && p)
{
new (p) T(forward(args)...);
}
return p;
}Code Snippets
void* operator new(std::size_t cb, uint32_t module_id)
{
// use module_id
return calloc(1, cb);
}
void operator delete(void* ptr)
{
free(ptr); // I'm unsure if this requires a null check
}
type *p = new(module_id) type;template<typename T>
T* create(uint32_t module_id)
{
// use module_id
T* p = calloc(1, sizeof(T));
if (!is_trivially_constructable<T>::value && p)
{
new (p) T(forward<Args>(args)...);
}
return p;
}Context
StackExchange Code Review Q#33858, answer score: 2
Revisions (0)
No revisions yet.