From: Rusty Russell Date: Thu, 22 Sep 2011 06:30:30 +0000 (+0930) Subject: tcon: routines for creating typesafe generic containers X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=5c451bbbed80e3bcb1f6be4301026ece01ab1309 tcon: routines for creating typesafe generic containers --- diff --git a/ccan/tcon/_info b/ccan/tcon/_info new file mode 100644 index 00000000..67923a0a --- /dev/null +++ b/ccan/tcon/_info @@ -0,0 +1,26 @@ +#include "config.h" +#include + +/** + * 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 + */ +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 index 00000000..5b38c719 --- /dev/null +++ b/ccan/tcon/tcon.h @@ -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 index 00000000..f80ef42c --- /dev/null +++ b/ccan/tcon/test/compile_fail-tcon_cast.c @@ -0,0 +1,29 @@ +#include +#include + +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 index 00000000..683bbd62 --- /dev/null +++ b/ccan/tcon/test/compile_fail.c @@ -0,0 +1,25 @@ +#include +#include + +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 index 00000000..26b712f6 --- /dev/null +++ b/ccan/tcon/test/compile_ok-void.c @@ -0,0 +1,21 @@ +#include +#include + +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 index 00000000..447f0ee5 --- /dev/null +++ b/ccan/tcon/test/compile_ok.c @@ -0,0 +1,27 @@ +#include +#include + +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; +}