Allocator: simplify using guarded allocator in C++ code

Using the `MEM_*` API from C++ code was a bit annoying:
* When converting C to C++ code, one often has to add a type cast on
  returned `void *`. That leads to having the same type name three times
  in the same line. This patch reduces the amount to two and removes the
  `sizeof(...)` from the line.
* The existing alternative of using `OBJECT_GUARDED_NEW` looks a out
  of place compared to other allocation methods. Sometimes
  `MEM_CXX_CLASS_ALLOC_FUNCS` can be used when structs are defined
  in C++ code. It doesn't look great but it's definitely better. The downside
  is that it makes the name of the allocation less useful. That's because
  the same name is used for all allocations of a type, independend of
  where it is allocated.

This patch introduces three new functions: `MEM_new`, `MEM_cnew` and
`MEM_delete`. These cover the majority of use cases (array allocation is
not covered).

The `OBJECT_GUARDED_*` macros are removed because they are not
needed anymore.

Differential Revision: https://developer.blender.org/D13502
This commit is contained in:
Jacques Lucke 2021-12-17 15:38:15 +01:00
parent c0d96ca9a5
commit a3ad5abf2f
Notes: blender-bot 2024-05-02 21:33:31 +02:00
Referenced by commit 6e0cf86e73, Cleanup: use new c++ guarded allocator API
13 changed files with 107 additions and 80 deletions

View File

@ -259,6 +259,49 @@ void MEM_use_guarded_allocator(void);
#endif /* __cplusplus */
#ifdef __cplusplus
# include <type_traits>
# include <utility>
/**
* Allocate new memory for and constructs an object of type #T.
* #MEM_delete should be used to delete the object. Just calling #MEM_freeN is not enough when #T
* is not a trivial type.
*/
template<typename T, typename... Args>
inline T *MEM_new(const char *allocation_name, Args &&...args)
{
void *buffer = MEM_mallocN(sizeof(T), allocation_name);
return new (buffer) T(std::forward<Args>(args)...);
}
/**
* Allocates zero-initialized memory for an object of type #T. The constructor of #T is not called,
* therefor this should only used with trivial types (like all C types).
* It's valid to call #MEM_freeN on a pointer returned by this, because a destructor call is not
* necessary, because the type is trivial.
*/
template<typename T> inline T *MEM_cnew(const char *allocation_name)
{
static_assert(std::is_trivial_v<T>, "For non-trivial types, MEM_new should be used.");
return static_cast<T *>(MEM_callocN(sizeof(T), allocation_name));
}
/**
* Destructs and deallocates an object previously allocated with any `MEM_*` function.
* Passing in null does nothing.
*/
template<typename T> inline void MEM_delete(const T *ptr)
{
if (ptr == nullptr) {
/* Support #ptr being null, because C++ `delete` supports that as well. */
return;
}
/* C++ allows destruction of const objects, so the pointer is allowed to be const. */
ptr->~T();
MEM_freeN(const_cast<T *>(ptr));
}
/* Allocation functions (for C++ only). */
# define MEM_CXX_CLASS_ALLOC_FUNCS(_id) \
public: \
@ -292,36 +335,6 @@ void MEM_use_guarded_allocator(void);
{ \
}
/* Needed when type includes a namespace, then the namespace should not be
* specified after ~, so using a macro fails. */
template<class T> inline void OBJECT_GUARDED_DESTRUCTOR(T *what)
{
what->~T();
}
# if defined __GNUC__
# define OBJECT_GUARDED_NEW(type, args...) new (MEM_mallocN(sizeof(type), __func__)) type(args)
# else
# define OBJECT_GUARDED_NEW(type, ...) \
new (MEM_mallocN(sizeof(type), __FUNCTION__)) type(__VA_ARGS__)
# endif
# define OBJECT_GUARDED_DELETE(what, type) \
{ \
if (what) { \
OBJECT_GUARDED_DESTRUCTOR((type *)what); \
MEM_freeN(what); \
} \
} \
(void)0
# define OBJECT_GUARDED_SAFE_DELETE(what, type) \
{ \
if (what) { \
OBJECT_GUARDED_DESTRUCTOR((type *)what); \
MEM_freeN(what); \
what = NULL; \
} \
} \
(void)0
#endif /* __cplusplus */
#endif /* __MEM_GUARDEDALLOC_H__ */

View File

@ -27,9 +27,21 @@
#ifdef WITH_LIBMV_GUARDED_ALLOC
# include "MEM_guardedalloc.h"
# define LIBMV_OBJECT_NEW OBJECT_GUARDED_NEW
# define LIBMV_OBJECT_DELETE OBJECT_GUARDED_DELETE
# define LIBMV_OBJECT_DELETE OBJECT_GUARDED_DELETE
# if defined __GNUC__
# define LIBMV_OBJECT_NEW(type, args...) \
new (MEM_mallocN(sizeof(type), __func__)) type(args)
# else
# define LIBMV_OBJECT_NEW(type, ...) \
new (MEM_mallocN(sizeof(type), __FUNCTION__)) type(__VA_ARGS__)
# endif
# define LIBMV_OBJECT_DELETE(what, type) \
{ \
if (what) { \
((type*)what)->~type(); \
MEM_freeN(what); \
} \
} \
(void)0
# define LIBMV_STRUCT_NEW(type, count) \
(type*)MEM_mallocN(sizeof(type) * count, __func__)
# define LIBMV_STRUCT_DELETE(what) MEM_freeN(what)

View File

@ -67,7 +67,7 @@ static void OCIO_reportException(Exception &exception)
OCIO_ConstConfigRcPtr *OCIOImpl::getCurrentConfig(void)
{
ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
ConstConfigRcPtr *config = MEM_new<ConstConfigRcPtr>(__func__);
try {
*config = GetCurrentConfig();
@ -79,7 +79,7 @@ OCIO_ConstConfigRcPtr *OCIOImpl::getCurrentConfig(void)
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
MEM_delete(config);
return NULL;
}
@ -96,7 +96,7 @@ void OCIOImpl::setCurrentConfig(const OCIO_ConstConfigRcPtr *config)
OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromEnv(void)
{
ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
ConstConfigRcPtr *config = MEM_new<ConstConfigRcPtr>(__func__);
try {
*config = Config::CreateFromEnv();
@ -108,14 +108,14 @@ OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromEnv(void)
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
MEM_delete(config);
return NULL;
}
OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromFile(const char *filename)
{
ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
ConstConfigRcPtr *config = MEM_new<ConstConfigRcPtr>(__func__);
try {
*config = Config::CreateFromFile(filename);
@ -127,14 +127,14 @@ OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromFile(const char *filename)
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
MEM_delete(config);
return NULL;
}
void OCIOImpl::configRelease(OCIO_ConstConfigRcPtr *config)
{
OBJECT_GUARDED_DELETE((ConstConfigRcPtr *)config, ConstConfigRcPtr);
MEM_delete((ConstConfigRcPtr *)config);
}
int OCIOImpl::configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config)
@ -164,7 +164,7 @@ const char *OCIOImpl::configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *conf
OCIO_ConstColorSpaceRcPtr *OCIOImpl::configGetColorSpace(OCIO_ConstConfigRcPtr *config,
const char *name)
{
ConstColorSpaceRcPtr *cs = OBJECT_GUARDED_NEW(ConstColorSpaceRcPtr);
ConstColorSpaceRcPtr *cs = MEM_new<ConstColorSpaceRcPtr>(__func__);
try {
*cs = (*(ConstConfigRcPtr *)config)->getColorSpace(name);
@ -176,7 +176,7 @@ OCIO_ConstColorSpaceRcPtr *OCIOImpl::configGetColorSpace(OCIO_ConstConfigRcPtr *
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(cs, ConstColorSpaceRcPtr);
MEM_delete(cs);
return NULL;
}
@ -380,7 +380,7 @@ const char *OCIOImpl::configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, in
OCIO_ConstLookRcPtr *OCIOImpl::configGetLook(OCIO_ConstConfigRcPtr *config, const char *name)
{
ConstLookRcPtr *look = OBJECT_GUARDED_NEW(ConstLookRcPtr);
ConstLookRcPtr *look = MEM_new<ConstLookRcPtr>(__func__);
try {
*look = (*(ConstConfigRcPtr *)config)->getLook(name);
@ -392,7 +392,7 @@ OCIO_ConstLookRcPtr *OCIOImpl::configGetLook(OCIO_ConstConfigRcPtr *config, cons
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(look, ConstLookRcPtr);
MEM_delete(look);
return NULL;
}
@ -404,7 +404,7 @@ const char *OCIOImpl::lookGetProcessSpace(OCIO_ConstLookRcPtr *look)
void OCIOImpl::lookRelease(OCIO_ConstLookRcPtr *look)
{
OBJECT_GUARDED_DELETE((ConstLookRcPtr *)look, ConstLookRcPtr);
MEM_delete((ConstLookRcPtr *)look);
}
int OCIOImpl::colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs_)
@ -521,14 +521,14 @@ void OCIOImpl::colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config_,
void OCIOImpl::colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs)
{
OBJECT_GUARDED_DELETE((ConstColorSpaceRcPtr *)cs, ConstColorSpaceRcPtr);
MEM_delete((ConstColorSpaceRcPtr *)cs);
}
OCIO_ConstProcessorRcPtr *OCIOImpl::configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config,
const char *srcName,
const char *dstName)
{
ConstProcessorRcPtr *processor = OBJECT_GUARDED_NEW(ConstProcessorRcPtr);
ConstProcessorRcPtr *processor = MEM_new<ConstProcessorRcPtr>(__func__);
try {
*processor = (*(ConstConfigRcPtr *)config)->getProcessor(srcName, dstName);
@ -540,20 +540,20 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::configGetProcessorWithNames(OCIO_ConstConfig
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(processor, ConstProcessorRcPtr);
MEM_delete(processor);
return 0;
}
void OCIOImpl::processorRelease(OCIO_ConstProcessorRcPtr *processor)
{
OBJECT_GUARDED_DELETE(processor, ConstProcessorRcPtr);
MEM_delete(processor);
}
OCIO_ConstCPUProcessorRcPtr *OCIOImpl::processorGetCPUProcessor(
OCIO_ConstProcessorRcPtr *processor)
{
ConstCPUProcessorRcPtr *cpu_processor = OBJECT_GUARDED_NEW(ConstCPUProcessorRcPtr);
ConstCPUProcessorRcPtr *cpu_processor = MEM_new<ConstCPUProcessorRcPtr>(__func__);
*cpu_processor = (*(ConstProcessorRcPtr *)processor)->getDefaultCPUProcessor();
return (OCIO_ConstCPUProcessorRcPtr *)cpu_processor;
}
@ -636,7 +636,7 @@ void OCIOImpl::cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_
void OCIOImpl::cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor)
{
OBJECT_GUARDED_DELETE(cpu_processor, ConstCPUProcessorRcPtr);
MEM_delete(cpu_processor);
}
const char *OCIOImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs)
@ -725,7 +725,7 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr
/* Create processor from transform. This is the moment were OCIO validates
* the entire transform, no need to check for the validity of inputs above. */
ConstProcessorRcPtr *p = OBJECT_GUARDED_NEW(ConstProcessorRcPtr);
ConstProcessorRcPtr *p = MEM_new<ConstProcessorRcPtr>(__func__);
try {
*p = config->getProcessor(group);
@ -737,7 +737,7 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr
OCIO_reportException(exception);
}
OBJECT_GUARDED_DELETE(p, ConstProcessorRcPtr);
MEM_delete(p);
return NULL;
}
@ -771,7 +771,7 @@ OCIO_PackedImageDesc *OCIOImpl::createOCIO_PackedImageDesc(float *data,
void OCIOImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id)
{
OBJECT_GUARDED_DELETE((PackedImageDesc *)id, PackedImageDesc);
MEM_delete((PackedImageDesc *)id);
}
const char *OCIOImpl::getVersionString(void)

View File

@ -156,7 +156,7 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator)
OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
OpenSubdiv_TopologyRefiner *topology_refiner)
{
OpenSubdiv_Evaluator *evaluator = OBJECT_GUARDED_NEW(OpenSubdiv_Evaluator);
OpenSubdiv_Evaluator *evaluator = MEM_new<OpenSubdiv_Evaluator>(__func__);
assignFunctionPointers(evaluator);
evaluator->impl = openSubdiv_createEvaluatorInternal(topology_refiner);
return evaluator;
@ -165,5 +165,5 @@ OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator *evaluator)
{
openSubdiv_deleteEvaluatorInternal(evaluator->impl);
OBJECT_GUARDED_DELETE(evaluator, OpenSubdiv_Evaluator);
MEM_delete(evaluator);
}

View File

@ -225,7 +225,7 @@ void assignFunctionPointers(OpenSubdiv_TopologyRefiner *topology_refiner)
OpenSubdiv_TopologyRefiner *allocateTopologyRefiner()
{
OpenSubdiv_TopologyRefiner *topology_refiner = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefiner);
OpenSubdiv_TopologyRefiner *topology_refiner = MEM_new<OpenSubdiv_TopologyRefiner>(__func__);
assignFunctionPointers(topology_refiner);
return topology_refiner;
}
@ -252,7 +252,7 @@ OpenSubdiv_TopologyRefiner *openSubdiv_createTopologyRefinerFromConverter(
void openSubdiv_deleteTopologyRefiner(OpenSubdiv_TopologyRefiner *topology_refiner)
{
delete topology_refiner->impl;
OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefiner);
MEM_delete(topology_refiner);
}
bool openSubdiv_topologyRefinerCompareWithConverter(

View File

@ -537,7 +537,7 @@ static void volume_copy_data(Main *UNUSED(bmain),
#ifdef WITH_OPENVDB
if (volume_src->runtime.grids) {
const VolumeGridVector &grids_src = *(volume_src->runtime.grids);
volume_dst->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector, grids_src);
volume_dst->runtime.grids = MEM_new<VolumeGridVector>(__func__, grids_src);
}
#endif
@ -551,7 +551,8 @@ static void volume_free_data(ID *id)
BKE_volume_batch_cache_free(volume);
MEM_SAFE_FREE(volume->mat);
#ifdef WITH_OPENVDB
OBJECT_GUARDED_SAFE_DELETE(volume->runtime.grids, VolumeGridVector);
MEM_delete(volume->runtime.grids);
volume->runtime.grids = nullptr;
#endif
}
@ -683,7 +684,7 @@ void BKE_volume_init_grids(Volume *volume)
{
#ifdef WITH_OPENVDB
if (volume->runtime.grids == nullptr) {
volume->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector);
volume->runtime.grids = MEM_new<VolumeGridVector>(__func__);
}
#else
UNUSED_VARS(volume);
@ -1129,16 +1130,16 @@ void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, co
if (!grids->is_loaded()) {
/* No grids loaded in CoW datablock, nothing lost by discarding. */
OBJECT_GUARDED_DELETE(grids, VolumeGridVector);
MEM_delete(grids);
}
else if (!STREQ(volume->filepath, filepath)) {
/* Filepath changed, discard grids from CoW datablock. */
OBJECT_GUARDED_DELETE(grids, VolumeGridVector);
MEM_delete(grids);
}
else {
/* Keep grids from CoW datablock. We might still unload them a little
* later in BKE_volume_eval_geometry if the frame changes. */
OBJECT_GUARDED_DELETE(volume->runtime.grids, VolumeGridVector);
MEM_delete(volume->runtime.grids);
volume->runtime.grids = grids;
}
#else

View File

@ -50,8 +50,8 @@ void BLI_task_scheduler_init()
if (num_threads_override > 0) {
/* Override number of threads. This settings is used within the lifetime
* of tbb::global_control, so we allocate it on the heap. */
task_scheduler_global_control = OBJECT_GUARDED_NEW(
tbb::global_control, tbb::global_control::max_allowed_parallelism, num_threads_override);
task_scheduler_global_control = MEM_new<tbb::global_control>(
__func__, tbb::global_control::max_allowed_parallelism, num_threads_override);
task_scheduler_num_threads = num_threads_override;
}
else {
@ -69,7 +69,7 @@ void BLI_task_scheduler_init()
void BLI_task_scheduler_exit()
{
#ifdef WITH_TBB_GLOBAL_CONTROL
OBJECT_GUARDED_DELETE(task_scheduler_global_control, tbb::global_control);
MEM_delete(task_scheduler_global_control);
#endif
}

View File

@ -742,16 +742,15 @@ static void update_index(const char *filename, FileIndexerEntries *entries, void
static void *init_user_data(const char *root_directory, size_t root_directory_maxlen)
{
AssetLibraryIndex *library_index = OBJECT_GUARDED_NEW(
AssetLibraryIndex,
StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxlen)));
AssetLibraryIndex *library_index = MEM_new<AssetLibraryIndex>(
__func__, StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxlen)));
library_index->init_unused_index_files();
return library_index;
}
static void free_user_data(void *user_data)
{
OBJECT_GUARDED_DELETE(user_data, AssetLibraryIndex);
MEM_delete((AssetLibraryIndex *)user_data);
}
static void filelist_finished(void *user_data)

View File

@ -88,12 +88,13 @@ AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle)
}
BLI_assert(handle->file_data->asset_data != nullptr);
return reinterpret_cast<AssetTempIDConsumer *>(
OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle));
MEM_new<AssetTemporaryIDConsumer>(__func__, *handle));
}
void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer)
{
OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer);
MEM_delete(reinterpret_cast<AssetTemporaryIDConsumer *>(*consumer));
*consumer = nullptr;
}
ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,

View File

@ -60,7 +60,7 @@ AbstractTreeView *UI_block_add_view(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractTreeView> tree_view)
{
ViewLink *view_link = OBJECT_GUARDED_NEW(ViewLink);
ViewLink *view_link = MEM_new<ViewLink>(__func__);
BLI_addtail(&block.views, view_link);
view_link->view = std::move(tree_view);
@ -72,7 +72,7 @@ AbstractTreeView *UI_block_add_view(uiBlock &block,
void ui_block_free_views(uiBlock *block)
{
LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
OBJECT_GUARDED_DELETE(link, ViewLink);
MEM_delete(link);
}
}

View File

@ -679,7 +679,7 @@ using namespace blender::ed::asset_browser;
FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings()
{
AssetCatalogFilterSettings *filter_settings = OBJECT_GUARDED_NEW(AssetCatalogFilterSettings);
AssetCatalogFilterSettings *filter_settings = MEM_new<AssetCatalogFilterSettings>(__func__);
return reinterpret_cast<FileAssetCatalogFilterSettingsHandle *>(filter_settings);
}
@ -688,7 +688,8 @@ void file_delete_asset_catalog_filter_settings(
{
AssetCatalogFilterSettings **filter_settings = reinterpret_cast<AssetCatalogFilterSettings **>(
filter_settings_handle);
OBJECT_GUARDED_SAFE_DELETE(*filter_settings, AssetCatalogFilterSettings);
MEM_delete(*filter_settings);
*filter_settings = nullptr;
}
bool file_set_asset_catalog_filter_settings(

View File

@ -125,8 +125,8 @@ void node_geometry_add_attribute_search_button(const bContext &UNUSED(C),
0.0f,
"");
AttributeSearchData *data = OBJECT_GUARDED_NEW(
AttributeSearchData, {&node_tree, &node, (bNodeSocket *)socket_ptr.data});
AttributeSearchData *data = MEM_new<AttributeSearchData>(
__func__, AttributeSearchData{&node_tree, &node, (bNodeSocket *)socket_ptr.data});
UI_but_func_search_set_results_are_suggestions(but, true);
UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);

View File

@ -1294,7 +1294,7 @@ static void add_attribute_search_button(const bContext &C,
return;
}
AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData);
AttributeSearchData *data = MEM_new<AttributeSearchData>(__func__);
data->object_session_uid = object->id.session_uuid;
STRNCPY(data->modifier_name, nmd.modifier.name);
STRNCPY(data->socket_identifier, socket.identifier);