Paul Eggert wrote:
> Perhaps it'd work without the "#define __libc_dynarray_resize 
> gl_dynarray_resize" etc. lines, because the two sets of 
> __libc_*dynarray* functions would be in different C namespaces? But it 
> might be confusing to debug.

No, there would be conflicts with glibc. In C, the namespace scope is
the compilation unit. Since each of the files malloc/dynarray_*.c is a
separate compilation unit, we would be colliding with the __libc_*
symbols defined by glibc (both with dynamic and with static linking).

Since dynarray looks useful, I wrote a small unit test, to see how the
module can be used on its own. I noticed three things:

I) A compilation error because "intprops.h" is included by not present.

II) It's not immediately clear which files to include. I define

#define DYNARRAY_STRUCT int_sequence
#define DYNARRAY_ELEMENT int
#define DYNARRAY_PREFIX intseq_

and then:

* With just <dynarray.h>:

In file included from ../../gltests/../gllib/dynarray.h:29:0,
                 from ../../gltests/test-dynarray.c:25:
../../gltests/../gllib/malloc/dynarray.h:171:1: warning: return type defaults 
to 'int' [-Wimplicit-int]
 libc_hidden_proto (__libc_dynarray_emplace_enlarge)
 ^
../../gltests/../gllib/malloc/dynarray.h: In function 'libc_hidden_proto':
../../gltests/../gllib/malloc/dynarray.h:172:1: error: expected declaration 
specifiers before 'libc_hidden_proto'
 libc_hidden_proto (__libc_dynarray_resize)
 ^


* With <libc-config.h> and <dynarray.h>:

../../gltests/test-dynarray.c: In function 'main':
../../gltests/test-dynarray.c:41:23: error: storage size of 's' isn't known
   struct int_sequence s;
                       ^


* With just <malloc/dynarray-skeleton.c>:

In file included from ../../gltests/../gllib/malloc/dynarray-skeleton.c:88:0,
                 from ../../gltests/test-dynarray.c:26:
../../gltests/../gllib/malloc/dynarray.h:171:1: warning: return type defaults 
to 'int' [-Wimplicit-int]
 libc_hidden_proto (__libc_dynarray_emplace_enlarge)
 ^
../../gltests/../gllib/malloc/dynarray.h: In function 'libc_hidden_proto':
../../gltests/../gllib/malloc/dynarray.h:172:1: error: expected declaration 
specifiers before 'libc_hidden_proto'
 libc_hidden_proto (__libc_dynarray_resize)
 ^


* With <libc-config.h> and <malloc/dynarray-skeleton.c>:

test-dynarray.o: In function `intseq_add__':
/media/develdata/devel/GNULIB/testdir4/build-64/gltests/../../gltests/../gllib/malloc/dynarray-skeleton.c:281:
 undefined reference to `__libc_dynarray_emplace_enlarge'
test-dynarray.o: In function `intseq_at':
/media/develdata/devel/GNULIB/testdir4/build-64/gltests/../../gltests/../gllib/malloc/dynarray-skeleton.c:253:
 undefined reference to `__libc_dynarray_at_failure'
collect2: error: ld returned 1 exit status


* So the result is that all of these files need to be included:

#include <libc-config.h>
#include <dynarray.h>
#include <malloc/dynarray-skeleton.c>

But that is not a proper interface for a module. To use a module, one should
need to include one file, not three.


III) The API documentation needs to be gathered from three places:
malloc/dynarray.h has tutorial documentation that is not consumable without
having read the reference documentation. The reference documentation is
in malloc/dynarray-skeleton.c, at the top of the file and then mixed with
the implementation. *shudder*


The following two patches bring this in order, and add a unit test.


2021-03-06  Bruno Haible  <br...@clisp.org>

        dynarray: Add tests.
        * tests/test-dynarray.c: New file.
        * modules/dynarray-tests: New file.

        dynarray: Make the module usable on its own.
        * lib/dynarray.h: Document the exported API. Comments taken from
        lib/malloc/dynarray-skeleton.c and lib/malloc/dynarray.h.
        Distinguish an internal include from an include for instantiation.
        In the latter case, include <libc-config.h> and
        <malloc/dynarray-skeleton.c>.
        * modules/dynarray (Depends-on): Add intprops.
        (Include): Reduce to just "dynarray.h".

>From 7ce163b7e92e127bb0d883df35ac9b642391ff8d Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Mar 2021 00:40:27 +0100
Subject: [PATCH 1/2] dynarray: Make the module usable on its own.

* lib/dynarray.h: Document the exported API. Comments taken from
lib/malloc/dynarray-skeleton.c and lib/malloc/dynarray.h.
Distinguish an internal include from an include for instantiation.
In the latter case, include <libc-config.h> and
<malloc/dynarray-skeleton.c>.
* modules/dynarray (Depends-on): Add intprops.
(Include): Reduce to just "dynarray.h".
---
 ChangeLog        |  11 +++
 lib/dynarray.h   | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 modules/dynarray |   4 +-
 3 files changed, 266 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e05cda2..8a86d1c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2021-03-06  Bruno Haible  <br...@clisp.org>
 
+	dynarray: Make the module usable on its own.
+	* lib/dynarray.h: Document the exported API. Comments taken from
+	lib/malloc/dynarray-skeleton.c and lib/malloc/dynarray.h.
+	Distinguish an internal include from an include for instantiation.
+	In the latter case, include <libc-config.h> and
+	<malloc/dynarray-skeleton.c>.
+	* modules/dynarray (Depends-on): Add intprops.
+	(Include): Reduce to just "dynarray.h".
+
+2021-03-06  Bruno Haible  <br...@clisp.org>
+
 	scratch_buffer: Add comment.
 	* lib/scratch_buffer.h: Add comment.
 
diff --git a/lib/dynarray.h b/lib/dynarray.h
index 37053d0..9a8d395 100644
--- a/lib/dynarray.h
+++ b/lib/dynarray.h
@@ -14,16 +14,267 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-/* Written by Paul Eggert, 2021.  */
+/* Written by Paul Eggert and Bruno Haible, 2021.  */
 
 #ifndef _GL_DYNARRAY_H
 #define _GL_DYNARRAY_H
 
+/* Before including this file, you need to define:
+
+   DYNARRAY_STRUCT
+      The struct tag of dynamic array to be defined.
+
+   DYNARRAY_ELEMENT
+      The type name of the element type.  Elements are copied
+      as if by memcpy, and can change address as the dynamic
+      array grows.
+
+   DYNARRAY_PREFIX
+      The prefix of the functions which are defined.
+
+   The following parameters are optional:
+
+   DYNARRAY_ELEMENT_FREE
+      DYNARRAY_ELEMENT_FREE (E) is evaluated to deallocate the
+      contents of elements. E is of type  DYNARRAY_ELEMENT *.
+
+   DYNARRAY_ELEMENT_INIT
+      DYNARRAY_ELEMENT_INIT (E) is evaluated to initialize a new
+      element.  E is of type  DYNARRAY_ELEMENT *.
+      If DYNARRAY_ELEMENT_FREE but not DYNARRAY_ELEMENT_INIT is
+      defined, new elements are automatically zero-initialized.
+      Otherwise, new elements have undefined contents.
+
+   DYNARRAY_INITIAL_SIZE
+      The size of the statically allocated array (default:
+      at least 2, more elements if they fit into 128 bytes).
+      Must be a preprocessor constant.  If DYNARRAY_INITIAL_SIZE is 0,
+      there is no statically allocated array at, and all non-empty
+      arrays are heap-allocated.
+
+   DYNARRAY_FINAL_TYPE
+      The name of the type which holds the final array.  If not
+      defined, is PREFIX##finalize not provided.  DYNARRAY_FINAL_TYPE
+      must be a struct type, with members of type DYNARRAY_ELEMENT and
+      size_t at the start (in this order).
+
+   These macros are undefined after this header file has been
+   included.
+
+   The following types are provided (their members are private to the
+   dynarray implementation):
+
+     struct DYNARRAY_STRUCT
+
+   The following functions are provided:
+ */
+
+/* Initialize a dynamic array object.  This must be called before any
+   use of the object.  */
+#if 0
+static void
+       DYNARRAY_PREFIX##init (struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Deallocate the dynamic array and its elements.  */
+#if 0
+static void
+       DYNARRAY_PREFIX##free (struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Return true if the dynamic array is in an error state.  */
+#if 0
+static bool
+       DYNARRAY_PREFIX##has_failed (const struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Mark the dynamic array as failed.  All elements are deallocated as
+   a side effect.  */
+#if 0
+static void
+       DYNARRAY_PREFIX##mark_failed (struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Return the number of elements which have been added to the dynamic
+   array.  */
+#if 0
+static size_t
+       DYNARRAY_PREFIX##size (const struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Return a pointer to the first array element, if any.  For a
+   zero-length array, the pointer can be NULL even though the dynamic
+   array has not entered the failure state.  */
+#if 0
+static DYNARRAY_ELEMENT *
+       DYNARRAY_PREFIX##begin (const struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Return a pointer one element past the last array element.  For a
+   zero-length array, the pointer can be NULL even though the dynamic
+   array has not entered the failure state.  */
+#if 0
+static DYNARRAY_ELEMENT *
+       DYNARRAY_PREFIX##end (const struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Return a pointer to the array element at INDEX.  Terminate the
+   process if INDEX is out of bounds.  */
+#if 0
+static DYNARRAY_ELEMENT *
+       DYNARRAY_PREFIX##at (struct DYNARRAY_STRUCT *list, size_t index);
+#endif
+
+/* Add ITEM at the end of the array, enlarging it by one element.
+   Mark *LIST as failed if the dynamic array allocation size cannot be
+   increased.  */
+#if 0
+static void
+       DYNARRAY_PREFIX##add (struct DYNARRAY_STRUCT *list,
+                             DYNARRAY_ELEMENT item);
+#endif
+
+/* Allocate a place for a new element in *LIST and return a pointer to
+   it.  The pointer can be NULL if the dynamic array cannot be
+   enlarged due to a memory allocation failure.  */
+#if 0
+static DYNARRAY_ELEMENT *
+       DYNARRAY_PREFIX##emplace (struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Change the size of *LIST to SIZE.  If SIZE is larger than the
+   existing size, new elements are added (which can be initialized).
+   Otherwise, the list is truncated, and elements are freed.  Return
+   false on memory allocation failure (and mark *LIST as failed).  */
+#if 0
+static bool
+       DYNARRAY_PREFIX##resize (struct DYNARRAY_STRUCT *list, size_t size);
+#endif
+
+/* Remove the last element of LIST if it is present.  */
+#if 0
+static void
+       DYNARRAY_PREFIX##remove_last (struct DYNARRAY_STRUCT *list);
+#endif
+
+/* Remove all elements from the list.  The elements are freed, but the
+   list itself is not.  */
+#if 0
+static void
+       DYNARRAY_PREFIX##clear (struct DYNARRAY_STRUCT *list);
+#endif
+
+#if defined DYNARRAY_FINAL_TYPE
+/* Transfer the dynamic array to a permanent location at *RESULT.
+   Returns true on success on false on allocation failure.  In either
+   case, *LIST is re-initialized and can be reused.  A NULL pointer is
+   stored in *RESULT if LIST refers to an empty list.  On success, the
+   pointer in *RESULT is heap-allocated and must be deallocated using
+   free.  */
+#if 0
+static bool
+       DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *list,
+                                  DYNARRAY_FINAL_TYPE *result);
+#endif
+#else /* !defined DYNARRAY_FINAL_TYPE */
+/* Transfer the dynamic array to a heap-allocated array and return a
+   pointer to it.  The pointer is NULL if memory allocation fails, or
+   if the array is empty, so this function should be used only for
+   arrays which are known not be empty (usually because they always
+   have a sentinel at the end).  If LENGTHP is not NULL, the array
+   length is written to *LENGTHP.  *LIST is re-initialized and can be
+   reused.  */
+#if 0
+static DYNARRAY_ELEMENT *
+       DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *list,
+                                  size_t *lengthp);
+#endif
+#endif
+
+/* A minimal example which provides a growing list of integers can be
+   defined like this:
+
+     struct int_array
+     {
+       // Pointer to result array followed by its length,
+       // as required by DYNARRAY_FINAL_TYPE.
+       int *array;
+       size_t length;
+     };
+
+     #define DYNARRAY_STRUCT dynarray_int
+     #define DYNARRAY_ELEMENT int
+     #define DYNARRAY_PREFIX dynarray_int_
+     #define DYNARRAY_FINAL_TYPE struct int_array
+     #include <malloc/dynarray-skeleton.c>
+
+   To create a three-element array with elements 1, 2, 3, use this
+   code:
+
+     struct dynarray_int dyn;
+     dynarray_int_init (&dyn);
+     for (int i = 1; i <= 3; ++i)
+       {
+         int *place = dynarray_int_emplace (&dyn);
+         assert (place != NULL);
+         *place = i;
+       }
+     struct int_array result;
+     bool ok = dynarray_int_finalize (&dyn, &result);
+     assert (ok);
+     assert (result.length == 3);
+     assert (result.array[0] == 1);
+     assert (result.array[1] == 2);
+     assert (result.array[2] == 3);
+     free (result.array);
+
+   If the elements contain resources which must be freed, define
+   DYNARRAY_ELEMENT_FREE appropriately, like this:
+
+     struct str_array
+     {
+       char **array;
+       size_t length;
+     };
+
+     #define DYNARRAY_STRUCT dynarray_str
+     #define DYNARRAY_ELEMENT char *
+     #define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
+     #define DYNARRAY_PREFIX dynarray_str_
+     #define DYNARRAY_FINAL_TYPE struct str_array
+     #include <malloc/dynarray-skeleton.c>
+ */
+
+
+/* The implementation is imported from glibc.  */
+
+/* Avoid possible conflicts with symbols exported by the GNU libc.  */
 #define __libc_dynarray_at_failure gl_dynarray_at_failure
 #define __libc_dynarray_emplace_enlarge gl_dynarray_emplace_enlarge
 #define __libc_dynarray_finalize gl_dynarray_finalize
 #define __libc_dynarray_resize_clear gl_dynarray_resize_clear
 #define __libc_dynarray_resize gl_dynarray_resize
-#include <malloc/dynarray.h>
+
+#if defined DYNARRAY_STRUCT || defined DYNARRAY_ELEMENT || defined DYNARRAY_PREFIX
+
+# include <libc-config.h>
+
+/* Define auxiliary structs and declare auxiliary functions, common to all
+   instantiations of dynarray.  */
+# include <malloc/dynarray.h>
+
+/* Define the instantiation, specified through
+     DYNARRAY_STRUCT
+     DYNARRAY_ELEMENT
+     DYNARRAY_PREFIX
+   etc.  */
+# include <malloc/dynarray-skeleton.c>
+
+#else
+
+/* This file is being included from one of the malloc/dynarray_*.c files.  */
+# include <malloc/dynarray.h>
+
+#endif
 
 #endif /* _GL_DYNARRAY_H */
diff --git a/modules/dynarray b/modules/dynarray
index 36a08ad..dcdcba4 100644
--- a/modules/dynarray
+++ b/modules/dynarray
@@ -16,6 +16,7 @@ c99
 libc-config
 stdbool
 stddef
+intprops
 
 configure.ac:
 
@@ -27,8 +28,7 @@ lib_SOURCES += malloc/dynarray_at_failure.c \
                malloc/dynarray_resize_clear.c
 
 Include:
-<dynarray.h>
-<malloc/dynarray-skeleton.c>
+"dynarray.h"
 
 License:
 LGPLv2+
-- 
2.7.4

>From 84b97a7332d6eae7b446645aea04f43eec473e8d Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Mar 2021 00:41:44 +0100
Subject: [PATCH 2/2] dynarray: Add tests.

* tests/test-dynarray.c: New file.
* modules/dynarray-tests: New file.
---
 ChangeLog              |  4 ++++
 modules/dynarray-tests | 11 +++++++++++
 tests/test-dynarray.c  | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 68 insertions(+)
 create mode 100644 modules/dynarray-tests
 create mode 100644 tests/test-dynarray.c

diff --git a/ChangeLog b/ChangeLog
index 8a86d1c..5a100d7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2021-03-06  Bruno Haible  <br...@clisp.org>
 
+	dynarray: Add tests.
+	* tests/test-dynarray.c: New file.
+	* modules/dynarray-tests: New file.
+
 	dynarray: Make the module usable on its own.
 	* lib/dynarray.h: Document the exported API. Comments taken from
 	lib/malloc/dynarray-skeleton.c and lib/malloc/dynarray.h.
diff --git a/modules/dynarray-tests b/modules/dynarray-tests
new file mode 100644
index 0000000..2791b9c
--- /dev/null
+++ b/modules/dynarray-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-dynarray.c
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-dynarray
+check_PROGRAMS += test-dynarray
diff --git a/tests/test-dynarray.c b/tests/test-dynarray.c
new file mode 100644
index 0000000..4308148
--- /dev/null
+++ b/tests/test-dynarray.c
@@ -0,0 +1,53 @@
+/* Test of type-safe arrays that grow dynamically.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2021.  */
+
+#include <config.h>
+
+#define DYNARRAY_STRUCT int_sequence
+#define DYNARRAY_ELEMENT int
+#define DYNARRAY_PREFIX intseq_
+#include "dynarray.h"
+
+#include "macros.h"
+
+#define N 100000
+
+static int
+value_at (int i)
+{
+  return (i % 13) + ((i * i) % 251);
+}
+
+int
+main ()
+{
+  struct int_sequence s;
+  int i;
+
+  intseq_init (&s);
+  for (i = 0; i < N; i++)
+    intseq_add (&s, value_at (i));
+  for (i = N - 1; i >= N / 2; i--)
+    {
+      ASSERT (* intseq_at (&s, i) == value_at (i));
+      intseq_remove_last (&s);
+    }
+  intseq_free (&s);
+
+  return 0;
+}
-- 
2.7.4

Reply via email to