[Libguestfs] [libnbd PATCH v5 4/5] vector: introduce DEFINE_POINTER_VECTOR_TYPE()

Laszlo Ersek lersek at redhat.com
Sun Feb 26 11:45:28 UTC 2023


The "name##_iter" function is used 11 times in libnbd; in all those cases,
"name" is "string_vector", and the "f" callback is "free":

  string_vector_iter (..., (void *) free);

Casting "free" to (void*) is ugly. (Well-defined by POSIX, but still.)

Furthermore, in all 11 cases, the freeing of the vector's strings is
immediately followed by the release of the vector's array-of-pointers too.
(This additional step is not expressed consistently across libnbd: it's
sometimes spelled as free(vec.ptr), sometimes as
string_vector_reset(&vec).)

Introduce "name##_empty", which performs both steps at the same time.

Keep the generic "name##_iter" function definition, as we'll want to synch
this patch to nbdkit, and in nbdkit, "name##_iter" has other uses as well.

Expose the "name##_empty" function definition with a new, separate macro:
ADD_VECTOR_EMPTY_METHOD(). The existent DEFINE_VECTOR_TYPE() macro permits
such element types that are not pointers, or are pointers to const- and/or
volatile-qualified objects. Whereas "name##_empty" requires that the
elements be pointers to dynamically allocated, non-const, non-volatile
objects.

Add DEFINE_POINTER_VECTOR_TYPE() that expands to both DEFINE_VECTOR_TYPE()
and the additive ADD_VECTOR_EMPTY_METHOD().

(

For example, after

  typedef char foobar[5];

the following compiles (as expected):

  DEFINE_VECTOR_TYPE (foobar_vector, foobar);

and the following fails to build (as expected):

  DEFINE_POINTER_VECTOR_TYPE (foobar_vector_bad, foobar);

with the diagnostics including

> In function ‘foobar_vector_bad_empty’:
> error: size of array ‘_vector_contains_pointers1’ is negative

)

Switch "string_vector" to DEFINE_POINTER_VECTOR_TYPE(). The 11
string_vector_iter() call sites continue working; they will be converted
to string_vector_empty() in a subsequent patch.

Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---

Notes:
    v5:
    
    - resolve conflict from update to previous patch
    
    - force semicolon after ADD_VECTOR_EMPTY_METHOD() and
      DEFINE_POINTER_VECTOR_TYPE() too
    
    - update example macro invocations in the commit message
    
    v4:
    
    - rename DEFINE_VECTOR_EMPTY to ADD_VECTOR_EMPTY_METHOD
    
    - introduce DEFINE_POINTER_VECTOR_TYPE as a convenience macro for
      DEFINE_VECTOR_TYPE plus ADD_VECTOR_EMPTY_METHOD
    
    - keep "name##_iter"
    
    - statically assert in "name##_empty" that the vector contains pointers
    
    - redefine string_vector with DEFINE_POINTER_VECTOR_TYPE now, rather
      than augmenting it as DEFINE_VECTOR_TYPE + (pre-rename, additive)
      DEFINE_VECTOR_EMPTY
    
    - split the call site conversions to a separate patch

 common/utils/string-vector.h |  2 +-
 common/utils/vector.h        | 35 ++++++++++++++++++++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/common/utils/string-vector.h b/common/utils/string-vector.h
index aa33fd48ceb5..115d7d484c71 100644
--- a/common/utils/string-vector.h
+++ b/common/utils/string-vector.h
@@ -37,6 +37,6 @@
 
 #include "vector.h"
 
-DEFINE_VECTOR_TYPE (string_vector, char *);
+DEFINE_POINTER_VECTOR_TYPE (string_vector, char *);
 
 #endif /* STRING_VECTOR_H */
diff --git a/common/utils/vector.h b/common/utils/vector.h
index b9b88ba02e7d..bdba02f7ebee 100644
--- a/common/utils/vector.h
+++ b/common/utils/vector.h
@@ -44,6 +44,9 @@
 #include <assert.h>
 #include <string.h>
 
+#include "compiler-macros.h"
+#include "static-assert.h"
+
 #ifdef __clang__
 #pragma clang diagnostic ignored "-Wunused-function"
 #pragma clang diagnostic ignored "-Wduplicate-decl-specifier"
@@ -183,6 +186,38 @@
 
 #define empty_vector { .ptr = NULL, .len = 0, .cap = 0 }
 
+/* This macro should only be used if:
+ * - the vector contains pointers, and
+ * - the pointed-to objects are:
+ *   - neither const- nor volatile-qualified, and
+ *   - allocated with malloc() or equivalent.
+ */
+#define ADD_VECTOR_EMPTY_METHOD(name)                                  \
+  /* Call free() on each element of the vector, then reset the vector. \
+   */                                                                  \
+  static inline void __attribute__ ((__unused__))                      \
+  name##_empty (name *v)                                               \
+  {                                                                    \
+    size_t i;                                                          \
+    for (i = 0; i < v->len; ++i) {                                     \
+      STATIC_ASSERT (TYPE_IS_POINTER (v->ptr[i]),                      \
+                     _vector_contains_pointers);                       \
+      free (v->ptr[i]);                                                \
+    }                                                                  \
+    name##_reset (v);                                                  \
+  }                                                                    \
+                                                                       \
+  /* Force callers to supply ';'. */                                   \
+  struct name
+
+/* Convenience macro tying together DEFINE_VECTOR_TYPE() and
+ * ADD_VECTOR_EMPTY_METHOD(). Inherit and forward the requirement for a
+ * trailing semicolon from ADD_VECTOR_EMPTY_METHOD() to the caller.
+ */
+#define DEFINE_POINTER_VECTOR_TYPE(name, type) \
+  DEFINE_VECTOR_TYPE (name, type);             \
+  ADD_VECTOR_EMPTY_METHOD (name)
+
 struct generic_vector {
   void *ptr;
   size_t len;



More information about the Libguestfs mailing list