(Commit message below adapted from libnbd commit fda38988b86b, "vector:
introduce DEFINE_POINTER_VECTOR_TYPE()", 2023-02-28).
The "name##_iter" function is used 5 times in nbdkit as follows:
string_vector_iter (..., (void *) free);
Casting "free" to (void*) is ugly. (Well-defined by POSIX, but still.)
Furthermore, in those 5 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 nbdkit: 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.
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 5
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(a)redhat.com>
(cherry picked from libnbd commit fda38988b86bb133b56fc6251b2d581e809b3b67)
---
Makefile.am | 1 +
common/utils/string-vector.h | 2 +-
common/utils/vector.h | 35 ++++++++++++++++++++
3 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/Makefile.am b/Makefile.am
index caa206064008..9bd942538d0d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,6 +58,7 @@ noinst_PROGRAMS = nbdkit
nbdkit_SOURCES = wrapper.c server/options.h
nbdkit_CPPFLAGS = \
-I$(top_srcdir)/server \
+ -I$(top_srcdir)/common/include \
-I$(top_srcdir)/common/utils \
-I$(top_srcdir)/common/replacements \
-Dbuilddir=\"$(abs_top_builddir)\" \
diff --git a/common/utils/string-vector.h b/common/utils/string-vector.h
index 7309ca4aa13d..40446008b6a9 100644
--- a/common/utils/string-vector.h
+++ b/common/utils/string-vector.h
@@ -37,7 +37,7 @@
#include "vector.h"
-DEFINE_VECTOR_TYPE (string_vector, char *);
+DEFINE_POINTER_VECTOR_TYPE (string_vector, char *);
/* This frees both the array and the strings. */
#define CLEANUP_FREE_STRING_VECTOR \
diff --git a/common/utils/vector.h b/common/utils/vector.h
index 5f5f96e97ab2..9549fa9e6f2f 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"
@@ -200,6 +203,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;