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(a)redhat.com>
---
Notes:
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 | 31 ++++++++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/common/utils/string-vector.h b/common/utils/string-vector.h
index 373eb319120a..d049a8d5b2b1 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 fb2482c853ff..38272925a65a 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"
@@ -180,6 +183,34 @@
#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); \
+ }
+
+/* Convenience macro tying together DEFINE_VECTOR_TYPE() and
+ * ADD_VECTOR_EMPTY_METHOD().
+ */
+#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;