tcon: routines for creating typesafe generic containers
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 22 Sep 2011 06:30:30 +0000 (16:00 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 22 Sep 2011 06:30:30 +0000 (16:00 +0930)
ccan/tcon/_info [new file with mode: 0644]
ccan/tcon/tcon.h [new file with mode: 0644]
ccan/tcon/test/compile_fail-tcon_cast.c [new file with mode: 0644]
ccan/tcon/test/compile_fail.c [new file with mode: 0644]
ccan/tcon/test/compile_ok-void.c [new file with mode: 0644]
ccan/tcon/test/compile_ok.c [new file with mode: 0644]

diff --git a/ccan/tcon/_info b/ccan/tcon/_info
new file mode 100644 (file)
index 0000000..67923a0
--- /dev/null
@@ -0,0 +1,26 @@
+#include "config.h"
+#include <string.h>
+
+/**
+ * tcon - routines for creating typesafe generic containers
+ *
+ * This code lets users create a structure with a typecanary; your API
+ * is then a set of macros which check the type canary before calling
+ * the generic routines.
+ *
+ * License: Public domain
+ *
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+       /* Expect exactly one argument */
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/tcon/tcon.h b/ccan/tcon/tcon.h
new file mode 100644 (file)
index 0000000..5b38c71
--- /dev/null
@@ -0,0 +1,81 @@
+/* Placed into the public domain */
+#ifndef CCAN_TCON_H
+#define CCAN_TCON_H
+#include "config.h"
+
+/**
+ * TCON - declare a _tcon type containing canary variables.
+ * @decls: the semi-colon separated list of type canaries.
+ *
+ * This declares a _tcon member for a structure.  It should be the
+ * last element in your structure; with sufficient compiler support it
+ * will not use any actual storage.  tcon_to_raw() will compare
+ * expressions with one of these "type canaries" to cause warnings if
+ * the container is misused.
+ *
+ * A type of "void *" will allow tcon_to_raw() to pass on any (pointer) type.
+ *
+ * Example:
+ *     // Simply typesafe linked list.
+ *     struct list_head {
+ *             struct list_head *prev, *next;
+ *     };
+ *
+ *     struct string_list {
+ *             struct list_head raw;
+ *             TCON(char *canary);
+ *     };
+ *
+ *     // More complex: mapping from one type to another.
+ *     struct map {
+ *             void *contents;
+ *     };
+ *
+ *     struct int_to_string_map {
+ *             struct map raw;
+ *             TCON(char *charp_canary; int int_canary);
+ *     };
+ */
+#if HAVE_FLEXIBLE_ARRAY_MEMBER
+#define TCON(decls) struct { decls; } _tcon[]
+#else
+#define TCON(decls) struct { decls; } _tcon[1]
+#endif
+
+/**
+ * tcon_check - typecheck a typed container
+ * @x: the structure containing the TCON.
+ * @canary: which canary to check against.
+ * @expr: the expression whose type must match the TCON (not evaluated)
+ *
+ * This macro is used to check that the expression is the type
+ * expected for this structure (note the "useless" sizeof() argument
+ * which contains this comparison with the type canary).
+ *
+ * It evaluates to @x so you can chain it.
+ *
+ * Example:
+ *     #define tlist_add(h, n, member) \
+ *             list_add(&tcon_check((h), canary, (n))->raw, &(n)->member)
+ */
+#define tcon_check(x, canary, expr)                            \
+       (sizeof((x)->_tcon[0].canary == (expr)) ? (x) : (x))
+
+
+/**
+ * tcon_cast - cast to a canary type for this container (or void *)
+ * @x: a structure containing the TCON.
+ * @canary: which canary to cast to.
+ * @expr: the value to cast
+ *
+ * This is used to cast to the correct type for this container.  If the
+ * platform doesn't HAVE_TYPEOF, then it casts to void * (which will
+ * cause a warning if the user doesn't expect a pointer type).
+ */
+#if HAVE_TYPEOF
+#define tcon_cast(x, canary, expr) ((__typeof__((x)->_tcon[0].canary))(expr))
+#else
+#define tcon_cast(x, canary, expr) ((void *)(expr))
+#endif
+
+#endif /* CCAN_TCON_H */
diff --git a/ccan/tcon/test/compile_fail-tcon_cast.c b/ccan/tcon/test/compile_fail-tcon_cast.c
new file mode 100644 (file)
index 0000000..f80ef42
--- /dev/null
@@ -0,0 +1,29 @@
+#include <ccan/tcon/tcon.h>
+#include <stdlib.h>
+
+struct container {
+       void *p;
+};
+
+struct int_and_charp_container {
+       struct container raw;
+       TCON(int *tc1; char *tc2);
+};
+
+int main(int argc, char *argv[])
+{
+       struct int_and_charp_container icon;
+#ifdef FAIL
+#if !HAVE_TYPEOF
+#error We cannot detect type problems without HAVE_TYPEOF
+#endif
+       char *
+#else
+       int *
+#endif
+               x;
+
+       icon.raw.p = NULL;
+       x = tcon_cast(&icon, tc1, icon.raw.p);
+       return 0;
+}
diff --git a/ccan/tcon/test/compile_fail.c b/ccan/tcon/test/compile_fail.c
new file mode 100644 (file)
index 0000000..683bbd6
--- /dev/null
@@ -0,0 +1,25 @@
+#include <ccan/tcon/tcon.h>
+#include <stdlib.h>
+
+struct container {
+       void *p;
+};
+
+struct int_container {
+       struct container raw;
+       TCON(int *canary);
+};
+
+int main(int argc, char *argv[])
+{
+       struct int_container icon;
+#ifdef FAIL
+       char *
+#else
+       int *
+#endif
+               x = NULL;
+
+       tcon_check(&icon, canary, x)->raw.p = x;
+       return 0;
+}
diff --git a/ccan/tcon/test/compile_ok-void.c b/ccan/tcon/test/compile_ok-void.c
new file mode 100644 (file)
index 0000000..26b712f
--- /dev/null
@@ -0,0 +1,21 @@
+#include <ccan/tcon/tcon.h>
+#include <stdlib.h>
+
+struct container {
+       void *p;
+};
+
+struct void_container {
+       struct container raw;
+       TCON(void *canary);
+};
+
+int main(int argc, char *argv[])
+{
+       struct void_container vcon;
+
+       tcon_check(&vcon, canary, NULL)->raw.p = NULL;
+       tcon_check(&vcon, canary, argv[0])->raw.p = NULL;
+       tcon_check(&vcon, canary, main)->raw.p = NULL;
+       return 0;
+}
diff --git a/ccan/tcon/test/compile_ok.c b/ccan/tcon/test/compile_ok.c
new file mode 100644 (file)
index 0000000..447f0ee
--- /dev/null
@@ -0,0 +1,27 @@
+#include <ccan/tcon/tcon.h>
+#include <stdlib.h>
+
+struct container {
+       void *p;
+};
+
+struct int_container {
+       struct container raw;
+       TCON(int tc);
+};
+
+struct charp_and_int_container {
+       struct container raw;
+       TCON(int tc1; char *tc2);
+};
+
+int main(int argc, char *argv[])
+{
+       struct int_container icon;
+       struct charp_and_int_container cicon;
+
+       tcon_check(&icon, tc, 7)->raw.p = NULL;
+       tcon_check(&cicon, tc1, 7)->raw.p = argv[0];
+       tcon_check(&cicon, tc2, argv[0])->raw.p = argv[0];
+       return 0;
+}