diff options
author | Jacques Lucke <jacques@blender.org> | 2022-03-02 14:51:21 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-03-02 14:51:21 +0300 |
commit | c23ec04b4e30f300a670f1cb1dc882e0608d09ad (patch) | |
tree | fbc914a9ccc72a03c894ec91b5c3a16e3537d04c | |
parent | 721335553ccb5ce4f7a374b958b7d65befa319df (diff) |
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
4 files changed, 58 insertions, 7 deletions
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index dd6b8463d84..a7cad5461b4 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -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))}; diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index 993434ddeba..ab716e5d011 100644 --- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -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 diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 51932d341bc..bdd4d74fe4b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -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( diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 1797364ad72..231ef547a8b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -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 { |