tal/autoptr: new module.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 29 Dec 2016 04:34:30 +0000 (15:04 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 29 Dec 2016 23:12:44 +0000 (09:42 +1030)
Helps with the common case of wanting to NULL out a pointer when the object
freed.  We could also track it if resized, but that's TODO.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/tal/autoptr/LICENSE [new symlink]
ccan/tal/autoptr/_info [new file with mode: 0644]
ccan/tal/autoptr/autoptr.c [new file with mode: 0644]
ccan/tal/autoptr/autoptr.h [new file with mode: 0644]
ccan/tal/autoptr/test/run.c [new file with mode: 0644]

diff --git a/ccan/tal/autoptr/LICENSE b/ccan/tal/autoptr/LICENSE
new file mode 120000 (symlink)
index 0000000..2b1feca
--- /dev/null
@@ -0,0 +1 @@
+../../../licenses/BSD-MIT
\ No newline at end of file
diff --git a/ccan/tal/autoptr/_info b/ccan/tal/autoptr/_info
new file mode 100644 (file)
index 0000000..b233ce5
--- /dev/null
@@ -0,0 +1,44 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * tal/autoptr - automatic updates of pointers to tal objects.
+ *
+ * This code updates pointers when the pointed-to object is freed.
+ *
+ * Maintainer: Rusty Russell <rusty@rustcorp.com.au>
+ * License: BSD-MIT
+ * Example:
+ *     #include <ccan/tal/autoptr/autoptr.h>
+ *     #include <assert.h>
+ *
+ *     static void *p;
+ *
+ *     int main(void)
+ *     {
+ *             char *c = tal(NULL, char);
+ *
+ *             // Sets p to point to c.
+ *             autonull_set_ptr(NULL, &p, c);
+ *             assert(p == c);
+ *
+ *             // Automatically clears p.
+ *             tal_free(c);
+ *             assert(p == NULL);
+ *             return 0;
+ *     }
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/tal\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/tal/autoptr/autoptr.c b/ccan/tal/autoptr/autoptr.c
new file mode 100644 (file)
index 0000000..5f22ef2
--- /dev/null
@@ -0,0 +1,35 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#include <ccan/tal/autoptr/autoptr.h>
+
+struct autonull {
+       void **pp;
+};
+
+static void autonull_remove(struct autonull *a);
+static void autonull_null_out(tal_t *p UNNEEDED, struct autonull *a)
+{
+       void **pp = a->pp;
+       tal_del_destructor(a, autonull_remove);
+       tal_free(a);
+       *pp = NULL;
+}
+
+static void autonull_remove(struct autonull *a)
+{
+       /* Don't NULL us out now. */
+       tal_del_destructor2(*a->pp, autonull_null_out, a);
+}
+
+struct autonull *autonull_set_ptr_(const tal_t *ctx, void *pp, const tal_t *p)
+{
+       struct autonull *a = tal(ctx, struct autonull);
+       a->pp = (void **)pp;
+       *a->pp = (void *)p;
+
+       /* If p is freed, NULL out a->pp */
+       tal_add_destructor2(*a->pp, autonull_null_out, a);
+
+       /* If they free autonull, it removes other destructor. */
+       tal_add_destructor(a, autonull_remove);
+       return a;
+}
diff --git a/ccan/tal/autoptr/autoptr.h b/ccan/tal/autoptr/autoptr.h
new file mode 100644 (file)
index 0000000..5799665
--- /dev/null
@@ -0,0 +1,47 @@
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_TAL_AUTOPTR_H
+#define CCAN_TAL_AUTOPTR_H
+#include <ccan/tal/tal.h>
+
+struct autonull;
+
+/**
+ * autonull_set_ptr - set a pointer, NULL it when pointer tal_free'd.
+ * @ctx: the tal context which owns this autonull.
+ * @pp: pointer to tal pointer
+ * @p: the tal pointer to set *@pp to.
+ *
+ * *@pp is set to @p.  When @p is tal_free'd directly or indirectly, *@pp will
+ * be set to NULL.  Or, if the returned object is freed, the callback is
+ * deactivated.
+ *
+ * Example:
+ *     struct parent {
+ *             struct child *c;
+ *     };
+ *     struct child {
+ *             const char *name;
+ *     };
+ *
+ *     int main(void)
+ *     {
+ *             struct parent *p = tal(NULL, struct parent);
+ *             struct child *c = tal(p, struct child);
+ *             c->name = "Child";
+ *
+ *             autonull_set_ptr(p, &p->c, c);
+ *             assert(p->c == c);
+ *
+ *             // Automatically clears p->c.
+ *             tal_free(c);
+ *             assert(p->c == NULL);
+ *             return 0;
+ *     }
+ *
+ */
+#define autonull_set_ptr(ctx, pp, p)           \
+       autonull_set_ptr_((ctx), (pp) + 0*sizeof(*(pp) = (p)), (p))
+
+struct autonull *autonull_set_ptr_(const tal_t *ctx, void *pp, const tal_t *p);
+
+#endif /* CCAN_TAL_AUTOPTR_H */
diff --git a/ccan/tal/autoptr/test/run.c b/ccan/tal/autoptr/test/run.c
new file mode 100644 (file)
index 0000000..33f1b04
--- /dev/null
@@ -0,0 +1,49 @@
+#include <ccan/tal/autoptr/autoptr.h>
+/* Include the C files directly. */
+#include <ccan/tal/autoptr/autoptr.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       char *p1, *p2, *p3;
+       struct autonull *a;
+
+       /* This is how many tests you plan to run */
+       plan_tests(8);
+
+       p1 = tal(NULL, char);
+
+       // Sets p1 to point to p2.
+       autonull_set_ptr(NULL, &p2, p1);
+       ok1(p2 == p1);
+       tal_free(p1);
+       ok1(p2 == NULL);
+
+       // Using p1 as the parent is the same. */
+       p1 = tal(NULL, char);
+       autonull_set_ptr(p1, &p2, p1);
+       ok1(p2 == p1);
+       tal_free(p1);
+       ok1(p2 == NULL);
+
+       // Freeing autodata deactivates it.
+       p1 = tal(NULL, char);
+       a = autonull_set_ptr(NULL, &p2, p1);
+       ok1(p2 == p1);
+       tal_free(a);
+       tal_free(p1);
+       ok1(p2 == p1);
+
+       // Making p3 the parent means freeing p3 deactivates it.
+       p3 = tal(NULL, char);
+       p1 = tal(NULL, char);
+       autonull_set_ptr(p3, &p2, p1);
+       ok1(p2 == p1);
+       tal_free(p3);
+       tal_free(p1);
+       ok1(p2 == p1);
+
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}