[Bf-blender-cvs] [c23ec04b4e3] master: BLI: add scoped-defer utility to add RAII-like behavior to C types

Jacques Lucke noreply at git.blender.org
Wed Mar 2 12:51:29 CET 2022


Commit: c23ec04b4e30f300a670f1cb1dc882e0608d09ad
Author: Jacques Lucke
Date:   Wed Mar 2 12:51:21 2022 +0100
Branches: master
https://developer.blender.org/rBc23ec04b4e30f300a670f1cb1dc882e0608d09ad

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

===================================================================

M	source/blender/blenlib/BLI_memory_utils.hh
M	source/blender/blenlib/tests/BLI_memory_utils_test.cc
M	source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
M	source/blender/nodes/geometry/nodes/node_geo_raycast.cc

===================================================================

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 {



More information about the Bf-blender-cvs mailing list