[ccan] [PATCHv2 4/6] tcon: Encode information on container members in "type" canaries

David Gibson david at gibson.dropbear.id.au
Thu Oct 22 17:47:37 AEDT 2015


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 <david at gibson.dropbear.id.au>
---
 ccan/tcon/tcon.h                          | 152 ++++++++++++++++++++++++++++++
 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            |  59 ++++++++++++
 10 files changed, 511 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 c33070d..1dbadaf 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,154 @@
 #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;					\
+	TCON_VALUE(_offset_##canary, offsetof(container, member))
+#endif
+
+/**
+ * tcon_container_check
+ * tcon_container_check_ptr
+ * tcon_container_type
+ * tcon_container_ptr_type
+ * tcon_container_sizeof
+ * tcon_container_cast
+ * tcon_container_cast_ptr
+ * @x: the structure containing the TCON.
+ * @canary: which container canary to check against.
+ *
+ * As tcon_check / tcon_check_ptr / tcon_type / tcon_ptr_type /
+ * tcon_sizeof / tcon_cast / tcon_cast_ptr, but use the type of the
+ * "container" type declared with TCON_CONTAINER, instead of a simple
+ * canary.
+ */
+#define tcon_container_check(x, canary, expr)		\
+	tcon_check(x, _container_##canary, expr)
+#define tcon_container_check_ptr(x, canary, expr)	\
+	tcon_check_ptr(x, _container_##canary, expr)
+#define tcon_container_type(x, canary)		\
+	tcon_type(x, _container_##canary)
+#define tcon_container_ptr_type(x, canary)	\
+	tcon_ptr_type(x, _container_##canary)
+#define tcon_container_sizeof(x, canary)	\
+	tcon_sizeof(x, _container_##canary)
+#define tcon_container_cast(x, canary, expr)	\
+	tcon_cast(x, _container_##canary, expr)
+#define tcon_container_cast_ptr(x, canary, expr)	\
+	tcon_cast_ptr(x, _container_##canary, expr)
+
+/**
+ * tcon_member_check
+ * tcon_member_check_ptr
+ * tcon_member_type
+ * tcon_member_ptr_type
+ * tcon_member_sizeof
+ * tcon_member_cast
+ * tcon_member_cast_ptr
+ * @x: the structure containing the TCON.
+ * @canary: which container canary to check against.
+ *
+ * As tcon_check / tcon_check_ptr / tcon_type / tcon_ptr_type /
+ * tcon_sizeof / tcon_cast / tcon_cast_ptr, but use the type of the
+ * "member" type declared with TCON_CONTAINER, instead of a simple
+ * canary.
+ */
+#define tcon_member_check(x, canary, expr)	\
+	tcon_check(x, _member_##canary, expr)
+#define tcon_member_check_ptr(x, canary, expr)		\
+	tcon_check_ptr(x, _member_##canary, expr)
+#define tcon_member_type(x, canary)		\
+	tcon_type(x, _member_##canary)
+#define tcon_member_ptr_type(x, canary)	\
+	tcon_ptr_type(x, _member_##canary)
+#define tcon_member_sizeof(x, canary)	\
+	tcon_sizeof(x, _member_##canary)
+#define tcon_member_cast(x, canary, expr)	\
+	tcon_cast(x, _member_##canary, expr)
+#define tcon_member_cast_ptr(x, canary, expr)	\
+	tcon_cast_ptr(x, _member_##canary, expr)
+
+/**
+ * tcon_offset - the offset of a member within a container, as
+ *               declared with TCON_CONTAINER
+ * @x: the structure containing the TCON.
+ * @canary: which container canary to check against.
+ */
+#define tcon_offset(x, canary)			\
+	tcon_value((x), _offset_##canary)
+
+/**
+ * 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_container_cast_ptr(					\
+		tcon_member_check_ptr((x), canary, (member_ptr)),	\
+		canary, tcon_container_of_((member_ptr),		\
+					   tcon_offset((x), 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_member_cast_ptr(						\
+		tcon_container_check_ptr((x), canary, (container_ptr)),	\
+		canary, tcon_member_of_((container_ptr),		\
+					tcon_offset((x), canary)))
+static inline void *tcon_member_of_(void *container_ptr, size_t offset)
+{
+	return container_ptr ? (char *)container_ptr + 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..88bb2a1
--- /dev/null
+++ b/ccan/tcon/test/run-container.c
@@ -0,0 +1,59 @@
+#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 outer0 {
+	struct inner inner;
+	int outer0_val;
+};
+
+struct info_base {
+	char *infop;
+};
+
+struct info_tcon {
+	struct info_base base;
+	TCON(TCON_CONTAINER(fi, struct outer, inner);
+	     TCON_CONTAINER(fi2, struct outer0, inner));
+};
+
+int main(int argc, char *argv[])
+{
+	struct info_tcon info;
+	TCON_WRAP(struct info_base,
+		  TCON_CONTAINER(fi, struct outer, inner);
+		  TCON_CONTAINER(fi2, struct outer0, inner)) infow;
+	struct outer ovar;
+	struct outer0 ovar2;
+
+	plan_tests(12);
+
+	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);
+
+	ok1(tcon_container_of(&info, fi2, &ovar2.inner) == &ovar2);
+	ok1(tcon_member_of(&info, fi2, &ovar2) == &ovar2.inner);
+	ok1(tcon_container_of(&infow, fi2, &ovar2.inner) == &ovar2);
+	ok1(tcon_member_of(&infow, fi2, &ovar2) == &ovar2.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



More information about the ccan mailing list