Add "container canaries" to tcon. This allows information about a specific member of a container structure to be encoded with TCON or TCON_WRAP. Once that's done, tcon_container_of() and tcon_member_of() can be used to translate between member and container pointers based on the canary information, without having to repeat the type and member details.
Signed-off-by: David Gibson <da...@gibson.dropbear.id.au> --- ccan/tcon/tcon.h | 81 +++++++++++++++++++++++++++++++ ccan/tcon/test/compile_fail-container1.c | 39 +++++++++++++++ ccan/tcon/test/compile_fail-container1w.c | 35 +++++++++++++ ccan/tcon/test/compile_fail-container2.c | 39 +++++++++++++++ ccan/tcon/test/compile_fail-container2w.c | 35 +++++++++++++ ccan/tcon/test/compile_fail-container3.c | 40 +++++++++++++++ ccan/tcon/test/compile_fail-container3w.c | 36 ++++++++++++++ ccan/tcon/test/compile_fail-container4.c | 40 +++++++++++++++ ccan/tcon/test/compile_fail-container4w.c | 36 ++++++++++++++ ccan/tcon/test/run-container.c | 46 ++++++++++++++++++ 10 files changed, 427 insertions(+) create mode 100644 ccan/tcon/test/compile_fail-container1.c create mode 100644 ccan/tcon/test/compile_fail-container1w.c create mode 100644 ccan/tcon/test/compile_fail-container2.c create mode 100644 ccan/tcon/test/compile_fail-container2w.c create mode 100644 ccan/tcon/test/compile_fail-container3.c create mode 100644 ccan/tcon/test/compile_fail-container3w.c create mode 100644 ccan/tcon/test/compile_fail-container4.c create mode 100644 ccan/tcon/test/compile_fail-container4w.c create mode 100644 ccan/tcon/test/run-container.c diff --git a/ccan/tcon/tcon.h b/ccan/tcon/tcon.h index bfba23a..4f5897a 100644 --- a/ccan/tcon/tcon.h +++ b/ccan/tcon/tcon.h @@ -3,6 +3,8 @@ #define CCAN_TCON_H #include "config.h" +#include <stddef.h> + /** * TCON - declare a _tcon type containing canary variables. * @decls: the semi-colon separated list of type canaries. @@ -212,4 +214,83 @@ #define tcon_cast(x, canary, expr) ((tcon_type((x), canary))(expr)) #define tcon_cast_ptr(x, canary, expr) ((tcon_ptr_type((x), canary))(expr)) +/** + * TCON_CONTAINER - encode information on a specific member of a + * containing structure into a "type" canary + * @canary: name of the container canary + * @container: type of the container structure + * @member: name of the member + * + * Used in the declarations in TCON() or TCON_WRAP(), encode a + * "container canary". This encodes the type of @container, the type + * of @member within it (with sufficient compiler support) and the + * offset of @member within @container. + */ +#if HAVE_TYPEOF +#define TCON_CONTAINER(canary, container, member) \ + container *_container_##canary; \ + typeof(((container *)0)->member) *_member_##canary; \ + TCON_VALUE(_offset_##canary, offsetof(container, member)) +#else +#define TCON_CONTAINER(canary, container, member) \ + container *_container_##canary; \ + void *_member_##canary; \ + TCON_VALUE(_offset_##canary, offsetof(container, member)) +#endif + +/** + * tcon_container_of - get pointer to enclosing structure based on a + * container canary + * @x: the structure containing the TCON + * @canary: the name of the container canary + * @member_ptr: pointer to a member of the container + * + * @member_ptr must be a pointer to the member of a container + * structure previously recorded in @canary with TCON_CONTAINER. + * + * tcon_container_of() evaluates to a pointer to the container + * structure. With sufficient compiler support, the pointer will be + * correctly typed, and the type of @member_ptr will be verified. + * + * Returns NULL if @member_ptr is NULL. + */ +#define tcon_container_of(x, canary, member_ptr) \ + tcon_cast(tcon_check((x), _member_##canary, (member_ptr)), \ + _container_##canary, \ + tcon_container_of_((member_ptr), \ + tcon_value((x), _offset_##canary))) +static inline void *tcon_container_of_(void *member_ptr, size_t offset) +{ + return member_ptr ? (char *)member_ptr - offset : NULL; +} + + +/** + * tcon_member_of - get pointer to enclosed member structure based on a + * container canary + * @x: the structure containing the TCON + * @canary: the name of the container canary + * @container_ptr: pointer to a container + * + * @container_ptr must be a pointer to a container structure + * previously recorded in @canary with TCON_CONTAINER. + * + * tcon_member_of() evaluates to a pointer to the member of the + * container recorded in @canary. With sufficient compiler support, + * the pointer will be correctly typed, and the type of @container_ptr + * will be verified. + * + * Returns NULL if @container_ptr is NULL. + */ +#define tcon_member_of(x, canary, container_ptr) \ + tcon_cast(tcon_check((x), _container_##canary, (container_ptr)), \ + _member_##canary, \ + tcon_field_((container_ptr), \ + tcon_value((x), _offset_##canary))) +static inline void *tcon_field_(void *containerp, size_t offset) +{ + return containerp ? (char *)containerp + offset : NULL; +} + + #endif /* CCAN_TCON_H */ diff --git a/ccan/tcon/test/compile_fail-container1.c b/ccan/tcon/test/compile_fail-container1.c new file mode 100644 index 0000000..a67e209 --- /dev/null +++ b/ccan/tcon/test/compile_fail-container1.c @@ -0,0 +1,39 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +struct info_tcon { + struct info_base base; + TCON(TCON_CONTAINER(concan, struct outer, inner)); +}; + +int main(int argc, char *argv[]) +{ + struct info_tcon info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + int *innerp = &ovar.outer_val; +#else + struct inner *innerp = &ovar.inner; +#endif + + return tcon_container_of(&info, concan, innerp) == &ovar; +} diff --git a/ccan/tcon/test/compile_fail-container1w.c b/ccan/tcon/test/compile_fail-container1w.c new file mode 100644 index 0000000..0226b68 --- /dev/null +++ b/ccan/tcon/test/compile_fail-container1w.c @@ -0,0 +1,35 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +int main(int argc, char *argv[]) +{ + TCON_WRAP(struct info_base, + TCON_CONTAINER(concan, struct outer, inner)) info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + int *innerp = &ovar.outer_val; +#else + struct inner *innerp = &ovar.inner; +#endif + + return tcon_container_of(&info, concan, innerp) == &ovar; +} diff --git a/ccan/tcon/test/compile_fail-container2.c b/ccan/tcon/test/compile_fail-container2.c new file mode 100644 index 0000000..6cad734 --- /dev/null +++ b/ccan/tcon/test/compile_fail-container2.c @@ -0,0 +1,39 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +struct info_tcon { + struct info_base base; + TCON(TCON_CONTAINER(concan, struct outer, inner)); +}; + +int main(int argc, char *argv[]) +{ + struct info_tcon info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + char *outerp = NULL; +#else + struct outer *outerp = &ovar; +#endif + + return tcon_member_of(&info, concan, outerp) == &ovar.inner; +} diff --git a/ccan/tcon/test/compile_fail-container2w.c b/ccan/tcon/test/compile_fail-container2w.c new file mode 100644 index 0000000..c73123c --- /dev/null +++ b/ccan/tcon/test/compile_fail-container2w.c @@ -0,0 +1,35 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +int main(int argc, char *argv[]) +{ + TCON_WRAP(struct info_base, + TCON_CONTAINER(concan, struct outer, inner)) info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + char *outerp = NULL; +#else + struct outer *outerp = &ovar; +#endif + + return tcon_member_of(&info, concan, outerp) == &ovar.inner; +} diff --git a/ccan/tcon/test/compile_fail-container3.c b/ccan/tcon/test/compile_fail-container3.c new file mode 100644 index 0000000..97473c8 --- /dev/null +++ b/ccan/tcon/test/compile_fail-container3.c @@ -0,0 +1,40 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +struct info_tcon { + struct info_base base; + TCON(TCON_CONTAINER(concan, struct outer, inner)); +}; + +int main(int argc, char *argv[]) +{ + struct info_tcon info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + int *outerp; +#else + struct outer *outerp; +#endif + + outerp = tcon_container_of(&info, concan, &ovar.inner); + return outerp != NULL; +} diff --git a/ccan/tcon/test/compile_fail-container3w.c b/ccan/tcon/test/compile_fail-container3w.c new file mode 100644 index 0000000..6930b43 --- /dev/null +++ b/ccan/tcon/test/compile_fail-container3w.c @@ -0,0 +1,36 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +int main(int argc, char *argv[]) +{ + TCON_WRAP(struct info_base, + TCON_CONTAINER(concan, struct outer, inner)) info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + int *outerp; +#else + struct outer *outerp; +#endif + + outerp = tcon_container_of(&info, concan, &ovar.inner); + return outerp != NULL; +} diff --git a/ccan/tcon/test/compile_fail-container4.c b/ccan/tcon/test/compile_fail-container4.c new file mode 100644 index 0000000..838ce9b --- /dev/null +++ b/ccan/tcon/test/compile_fail-container4.c @@ -0,0 +1,40 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +struct info_tcon { + struct info_base base; + TCON(TCON_CONTAINER(concan, struct outer, inner)); +}; + +int main(int argc, char *argv[]) +{ + struct info_tcon info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + int *innerp; +#else + struct inner *innerp; +#endif + + innerp = tcon_member_of(&info, concan, &ovar); + return innerp != NULL; +} diff --git a/ccan/tcon/test/compile_fail-container4w.c b/ccan/tcon/test/compile_fail-container4w.c new file mode 100644 index 0000000..0d7b367 --- /dev/null +++ b/ccan/tcon/test/compile_fail-container4w.c @@ -0,0 +1,36 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +int main(int argc, char *argv[]) +{ + TCON_WRAP(struct info_base, + TCON_CONTAINER(concan, struct outer, inner)) info; + struct outer ovar; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + int *innerp; +#else + struct inner *innerp; +#endif + + innerp = tcon_member_of(&info, concan, &ovar); + return innerp != NULL; +} diff --git a/ccan/tcon/test/run-container.c b/ccan/tcon/test/run-container.c new file mode 100644 index 0000000..8e1a280 --- /dev/null +++ b/ccan/tcon/test/run-container.c @@ -0,0 +1,46 @@ +#include <stdlib.h> + +#include <ccan/tcon/tcon.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/tap/tap.h> + +struct inner { + int inner_val; +}; + +struct outer { + int outer_val; + struct inner inner; +}; + +struct info_base { + char *infop; +}; + +struct info_tcon { + struct info_base base; + TCON(TCON_CONTAINER(fi, struct outer, inner)); +}; + +int main(int argc, char *argv[]) +{ + struct info_tcon info; + TCON_WRAP(struct info_base, + TCON_CONTAINER(fi, struct outer, inner)) infow; + struct outer ovar; + + plan_tests(8); + + ok1(tcon_container_of(&info, fi, &ovar.inner) == &ovar); + ok1(tcon_member_of(&info, fi, &ovar) == &ovar.inner); + ok1(tcon_container_of(&infow, fi, &ovar.inner) == &ovar); + ok1(tcon_member_of(&infow, fi, &ovar) == &ovar.inner); + + /* Check handling of NULLs */ + ok1(tcon_container_of(&info, fi, NULL) == NULL); + ok1(tcon_member_of(&info, fi, NULL) == NULL); + ok1(tcon_container_of(&infow, fi, NULL) == NULL); + ok1(tcon_member_of(&infow, fi, NULL) == NULL); + + return 0; +} -- 2.4.3 _______________________________________________ ccan mailing list ccan@lists.ozlabs.org https://lists.ozlabs.org/listinfo/ccan