BLI: add scoped-defer utility to add RAII-like behavior to C types

This utility is useful when using C types that own some resource in
a C++ file. It mainly helps in functions that have multiple return
statements, but also simplifies code by moving construction and
destruction closer together.

Differential Revision: https://developer.blender.org/D14215
This commit is contained in:
Jacques Lucke 2022-03-02 12:51:21 +01:00
parent 721335553c
commit c23ec04b4e
Notes: blender-bot 2023-02-14 10:37:50 +01:00
Referenced by issue #96123, Crash when click-dragging across the visibility icons
4 changed files with 58 additions and 7 deletions

View File

@ -544,3 +544,31 @@ Container &move_assign_container(Container &dst, Container &&src) noexcept(
}
} // namespace blender
namespace blender::detail {
template<typename Func> struct ScopedDeferHelper {
Func func;
~ScopedDeferHelper()
{
func();
}
};
} // namespace blender::detail
#define BLI_SCOPED_DEFER_NAME1(a, b) a##b
#define BLI_SCOPED_DEFER_NAME2(a, b) BLI_SCOPED_DEFER_NAME1(a, b)
#define BLI_SCOPED_DEFER_NAME(a) BLI_SCOPED_DEFER_NAME2(_scoped_defer_##a##_, __LINE__)
/**
* Execute the given function when the current scope ends. This can be used to cheaply implement
* some RAII-like behavior for C types that don't support it. Long term, the types we want to use
* this with should either be converted to C++ or get a proper C++ API. Until then, this function
* can help avoid common resource leakages.
*/
#define BLI_SCOPED_DEFER(function_to_defer) \
auto BLI_SCOPED_DEFER_NAME(func) = (function_to_defer); \
blender::detail::ScopedDeferHelper<decltype(BLI_SCOPED_DEFER_NAME(func))> \
BLI_SCOPED_DEFER_NAME(helper){std::move(BLI_SCOPED_DEFER_NAME(func))};

View File

@ -176,4 +176,29 @@ static_assert(!is_same_any_v<int, float, bool>);
static_assert(!is_same_any_v<int, float>);
static_assert(!is_same_any_v<int>);
TEST(memory_utils, ScopedDefer1)
{
int a = 0;
{
BLI_SCOPED_DEFER([&]() { a -= 5; });
{
BLI_SCOPED_DEFER([&]() { a *= 10; });
a = 5;
}
}
EXPECT_EQ(a, 45);
}
TEST(memory_utils, ScopedDefer2)
{
std::string s;
{
BLI_SCOPED_DEFER([&]() { s += "A"; });
BLI_SCOPED_DEFER([&]() { s += "B"; });
BLI_SCOPED_DEFER([&]() { s += "C"; });
BLI_SCOPED_DEFER([&]() { s += "D"; });
}
EXPECT_EQ(s, "DCBA");
}
} // namespace blender::tests

View File

@ -152,6 +152,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
}
KDTree_3d *kdtree = build_kdtree(positions);
BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); });
for (const int i : positions.index_range()) {
if (elimination_mask[i]) {
@ -176,8 +177,6 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
},
&callback_data);
}
BLI_kdtree_3d_free(kdtree);
}
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(

View File

@ -141,10 +141,13 @@ static void raycast_to_mesh(IndexMask mask,
{
BVHTreeFromMesh tree_data;
BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4);
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&tree_data); });
if (tree_data.tree == nullptr) {
free_bvhtree_from_mesh(&tree_data);
return;
}
/* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */
BLI_assert(tree_data.cached);
for (const int i : mask) {
const float ray_length = ray_lengths[i];
@ -197,10 +200,6 @@ static void raycast_to_mesh(IndexMask mask,
}
}
}
/* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */
BLI_assert(tree_data.cached);
free_bvhtree_from_mesh(&tree_data);
}
class RaycastFunction : public fn::MultiFunction {