Refactor: Replace MEM_cnew with a type-aware template version of MEM_callocN.
The general idea is to keep the 'old', C-style MEM_callocN signature, and slowly replace most of its usages with the new, C++-style type-safer template version. * `MEM_cnew<T>` allocation version is renamed to `MEM_callocN<T>`. * `MEM_cnew_array<T>` allocation version is renamed to `MEM_calloc_arrayN<T>`. * `MEM_cnew<T>` duplicate version is renamed to `MEM_dupallocN<T>`. Similar templates type-safe version of `MEM_mallocN` will be added soon as well. Following discussions in !134452. NOTE: For now static type checking in `MEM_callocN` and related are slightly different for Windows MSVC. This compiler seems to consider structs using the `DNA_DEFINE_CXX_METHODS` macro as non-trivial (likely because their default copy constructors are deleted). So using checks on trivially constructible/destructible instead on this compiler/system. Pull Request: https://projects.blender.org/blender/blender/pulls/134771
This commit is contained in:
committed by
Bastien Montagne
parent
65889672e2
commit
dd168a35c5
@@ -52,7 +52,7 @@ extern "C" {
|
||||
extern size_t (*MEM_allocN_len)(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Release memory previously allocated by the C-style and #MEM_cnew functions of this module.
|
||||
* Release memory previously allocated by the C-style functions of this module.
|
||||
*
|
||||
* It is illegal to call this function with data allocated by #MEM_new.
|
||||
*/
|
||||
@@ -69,7 +69,7 @@ extern short (*MEM_testN)(void *vmemh);
|
||||
* Duplicates a block of memory, and returns a pointer to the
|
||||
* newly allocated block.
|
||||
* NULL-safe; will return NULL when receiving a NULL pointer. */
|
||||
extern void *(*MEM_dupallocN)(const void *vmemh) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT;
|
||||
void *MEM_dupallocN(const void *vmemh) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Reallocates a block of memory, and returns pointer to the newly
|
||||
@@ -97,17 +97,17 @@ extern void *(*MEM_recallocN_id)(void *vmemh,
|
||||
* memory is cleared. The name must be static, because only a
|
||||
* pointer to it is stored!
|
||||
*/
|
||||
extern void *(*MEM_callocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
|
||||
void *MEM_callocN(size_t len, const char *str) ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1)
|
||||
ATTR_NONNULL(2);
|
||||
|
||||
/**
|
||||
* Allocate a block of memory of size (len * size), with tag name
|
||||
* str, aborting in case of integer overflows to prevent vulnerabilities.
|
||||
* The memory is cleared. The name must be static, because only a
|
||||
* pointer to it is stored! */
|
||||
extern void *(*MEM_calloc_arrayN)(size_t len,
|
||||
size_t size,
|
||||
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
||||
void *MEM_calloc_arrayN(size_t len,
|
||||
size_t size,
|
||||
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
||||
|
||||
/**
|
||||
@@ -325,8 +325,8 @@ inline T *MEM_new(const char *allocation_name, Args &&...args)
|
||||
*
|
||||
* As with the `delete` C++ operator, passing in `nullptr` is allowed and does nothing.
|
||||
*
|
||||
* It is illegal to call this function with data allocated by #MEM_cnew or the C-style allocation
|
||||
* functions of this module.
|
||||
* It is illegal to call this function with data allocated by the C-style allocation functions of
|
||||
* this module.
|
||||
*/
|
||||
template<typename T> inline void MEM_delete(const T *ptr)
|
||||
{
|
||||
@@ -356,23 +356,49 @@ template<typename T> inline void MEM_delete(const T *ptr)
|
||||
|
||||
/**
|
||||
* Allocate zero-initialized memory for an object of type #T. The constructor of #T is not called,
|
||||
* therefore this should only be used with trivial types (like all C types).
|
||||
* therefore this must only be used with trivial types (like all C types).
|
||||
*
|
||||
* When allocating an enforced specific amount of bytes, the C version of this function should be
|
||||
* used instead. While this should be avoided in C++ code, it is still required in some cases, e.g.
|
||||
* for ID allocation based on #IDTypeInfo::struct_size.
|
||||
*
|
||||
* #MEM_freeN must be used to free a pointer returned by this call. Calling #MEM_delete on it is
|
||||
* illegal.
|
||||
*/
|
||||
template<typename T> inline T *MEM_cnew(const char *allocation_name)
|
||||
template<typename T> inline T *MEM_callocN(const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(MEM_calloc_arrayN_aligned(1, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as MEM_cnew but for arrays, better alternative to #MEM_calloc_arrayN.
|
||||
* Type-safe version of #MEM_calloc_arrayN/#MEM_calloc_array_alignedN.
|
||||
*/
|
||||
template<typename T> inline T *MEM_cnew_array(const size_t length, const char *allocation_name)
|
||||
template<typename T> inline T *MEM_calloc_arrayN(const size_t length, const char *allocation_name)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
return static_cast<T *>(
|
||||
MEM_calloc_arrayN_aligned(length, sizeof(T), alignof(T), allocation_name));
|
||||
}
|
||||
@@ -385,11 +411,23 @@ template<typename T> inline T *MEM_cnew_array(const size_t length, const char *a
|
||||
* deprecated fields: some compilers will generate access deprecated field warnings in implicitly
|
||||
* defined copy constructors.
|
||||
*
|
||||
* This is a better alternative to #MEM_dupallocN.
|
||||
* This is a better alternative to the C-style implementation of #MEM_dupallocN, unless the source
|
||||
* is an array or of a non-fully-defined type.
|
||||
*/
|
||||
template<typename T> inline T *MEM_cnew(const char *allocation_name, const T &other)
|
||||
template<typename T> inline T *MEM_dupallocN(const char *allocation_name, const T &other)
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
/* MSVC considers C-style types using the DNA_DEFINE_CXX_METHODS as non-trivial (more
|
||||
* specifically, non-trivially copyable, likely because the default copy constructors are
|
||||
* deleted). GCC and clang (both on linux, OSX, and clang-cl on Windows on Arm) do not.
|
||||
*
|
||||
* So for now, use a more restricted check on MSVC, should still catch most of actual invalid
|
||||
* cases. */
|
||||
static_assert(std::is_trivially_constructible_v<T>,
|
||||
"For non-trivial types, MEM_new must be used.");
|
||||
# else
|
||||
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new must be used.");
|
||||
# endif
|
||||
T *new_object = static_cast<T *>(MEM_mallocN_aligned(sizeof(T), alignof(T), allocation_name));
|
||||
if (new_object) {
|
||||
memcpy(new_object, &other, sizeof(T));
|
||||
|
||||
@@ -36,11 +36,13 @@ const char *malloc_conf =
|
||||
size_t (*MEM_allocN_len)(const void *vmemh) = MEM_lockfree_allocN_len;
|
||||
void (*mem_guarded::internal::mem_freeN_ex)(void *vmemh,
|
||||
AllocationType allocation_type) = MEM_lockfree_freeN;
|
||||
void *(*MEM_dupallocN)(const void *vmemh) = MEM_lockfree_dupallocN;
|
||||
void *(*mem_guarded::internal::mem_dupallocN)(const void *vmemh) = MEM_lockfree_dupallocN;
|
||||
void *(*MEM_reallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_reallocN_id;
|
||||
void *(*MEM_recallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_recallocN_id;
|
||||
void *(*MEM_callocN)(size_t len, const char *str) = MEM_lockfree_callocN;
|
||||
void *(*MEM_calloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockfree_calloc_arrayN;
|
||||
void *(*mem_guarded::internal::mem_callocN)(size_t len, const char *str) = MEM_lockfree_callocN;
|
||||
void *(*mem_guarded::internal::mem_calloc_arrayN)(size_t len,
|
||||
size_t size,
|
||||
const char *str) = MEM_lockfree_calloc_arrayN;
|
||||
void *(*MEM_mallocN)(size_t len, const char *str) = MEM_lockfree_mallocN;
|
||||
void *(*MEM_malloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockfree_malloc_arrayN;
|
||||
void *(*mem_guarded::internal::mem_mallocN_aligned_ex)(size_t len,
|
||||
@@ -111,11 +113,26 @@ void MEM_freeN(void *vmemh)
|
||||
mem_freeN_ex(vmemh, AllocationType::ALLOC_FREE);
|
||||
}
|
||||
|
||||
void *MEM_callocN(size_t len, const char *str)
|
||||
{
|
||||
return mem_callocN(len, str);
|
||||
}
|
||||
|
||||
void *MEM_calloc_arrayN(size_t len, size_t size, const char *str)
|
||||
{
|
||||
return mem_calloc_arrayN(len, size, str);
|
||||
}
|
||||
|
||||
void *MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
|
||||
{
|
||||
return mem_mallocN_aligned_ex(len, alignment, str, AllocationType::ALLOC_FREE);
|
||||
}
|
||||
|
||||
void *MEM_dupallocN(const void *vmemh)
|
||||
{
|
||||
return mem_dupallocN(vmemh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform assert checks on allocator type change.
|
||||
*
|
||||
@@ -142,11 +159,11 @@ void MEM_use_lockfree_allocator()
|
||||
|
||||
MEM_allocN_len = MEM_lockfree_allocN_len;
|
||||
mem_freeN_ex = MEM_lockfree_freeN;
|
||||
MEM_dupallocN = MEM_lockfree_dupallocN;
|
||||
mem_dupallocN = MEM_lockfree_dupallocN;
|
||||
MEM_reallocN_id = MEM_lockfree_reallocN_id;
|
||||
MEM_recallocN_id = MEM_lockfree_recallocN_id;
|
||||
MEM_callocN = MEM_lockfree_callocN;
|
||||
MEM_calloc_arrayN = MEM_lockfree_calloc_arrayN;
|
||||
mem_callocN = MEM_lockfree_callocN;
|
||||
mem_calloc_arrayN = MEM_lockfree_calloc_arrayN;
|
||||
MEM_mallocN = MEM_lockfree_mallocN;
|
||||
MEM_malloc_arrayN = MEM_lockfree_malloc_arrayN;
|
||||
mem_mallocN_aligned_ex = MEM_lockfree_mallocN_aligned;
|
||||
@@ -178,11 +195,11 @@ void MEM_use_guarded_allocator()
|
||||
|
||||
MEM_allocN_len = MEM_guarded_allocN_len;
|
||||
mem_freeN_ex = MEM_guarded_freeN;
|
||||
MEM_dupallocN = MEM_guarded_dupallocN;
|
||||
mem_dupallocN = MEM_guarded_dupallocN;
|
||||
MEM_reallocN_id = MEM_guarded_reallocN_id;
|
||||
MEM_recallocN_id = MEM_guarded_recallocN_id;
|
||||
MEM_callocN = MEM_guarded_callocN;
|
||||
MEM_calloc_arrayN = MEM_guarded_calloc_arrayN;
|
||||
mem_callocN = MEM_guarded_callocN;
|
||||
mem_calloc_arrayN = MEM_guarded_calloc_arrayN;
|
||||
MEM_mallocN = MEM_guarded_mallocN;
|
||||
MEM_malloc_arrayN = MEM_guarded_malloc_arrayN;
|
||||
mem_mallocN_aligned_ex = MEM_guarded_mallocN_aligned;
|
||||
|
||||
@@ -720,7 +720,7 @@ static void *mem_guarded_malloc_arrayN_aligned(const size_t len,
|
||||
return nullptr;
|
||||
}
|
||||
if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
|
||||
return MEM_callocN(r_bytes_num, str);
|
||||
return mem_callocN(r_bytes_num, str);
|
||||
}
|
||||
return MEM_mallocN_aligned(r_bytes_num, alignment, str);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,22 @@ enum class AllocationType {
|
||||
/** Internal implementation of #MEM_freeN, exposed because #MEM_delete needs access to it. */
|
||||
extern void (*mem_freeN_ex)(void *vmemh, AllocationType allocation_type);
|
||||
|
||||
/**
|
||||
* Internal implementation of #MEM_callocN, exposed because public #MEM_callocN cannot be a
|
||||
* function pointer, to allow its overload by C++ template version.
|
||||
*/
|
||||
extern void *(*mem_callocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
|
||||
|
||||
/**
|
||||
* Internal implementation of #MEM_calloc_arrayN, exposed because public #MEM_calloc_arrayN cannot
|
||||
* be a function pointer, to allow its overload by C++ template version.
|
||||
*/
|
||||
extern void *(*mem_calloc_arrayN)(size_t len,
|
||||
size_t size,
|
||||
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3);
|
||||
|
||||
/** Internal implementation of #MEM_mallocN_aligned, exposed because #MEM_new needs access to it.
|
||||
*/
|
||||
extern void *(*mem_mallocN_aligned_ex)(size_t len,
|
||||
@@ -27,6 +43,12 @@ extern void *(*mem_mallocN_aligned_ex)(size_t len,
|
||||
const char *str,
|
||||
AllocationType allocation_type);
|
||||
|
||||
/**
|
||||
* Internal implementation of #MEM_dupallocN, exposed because public #MEM_dupallocN cannot be a
|
||||
* function pointer, to allow its overload by C++ template version.
|
||||
*/
|
||||
extern void *(*mem_dupallocN)(const void *vmemh) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Store a std::any into a static opaque storage vector. The only purpose of this call is to
|
||||
* control the lifetime of the given data, there is no way to access it from here afterwards. User
|
||||
|
||||
@@ -474,7 +474,7 @@ static void *mem_lockfree_malloc_arrayN_aligned(const size_t len,
|
||||
return nullptr;
|
||||
}
|
||||
if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
|
||||
return MEM_mallocN(r_bytes_num, str);
|
||||
return mem_callocN(r_bytes_num, str);
|
||||
}
|
||||
void *ptr = MEM_mallocN_aligned(r_bytes_num, alignment, str);
|
||||
return ptr;
|
||||
|
||||
Reference in New Issue
Block a user