public inbox for ccan@ozlabs.org
 help / color / mirror / Atom feed
* [PATCH 0/3] generators in C
@ 2016-02-15 12:13 David Gibson
  2016-02-15 12:13 ` [PATCH 1/3] configurator: Add test for ucontext.h David Gibson
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: David Gibson @ 2016-02-15 12:13 UTC (permalink / raw)
  To: ccan, rusty

This series add's the 'generator' module which implements Python-like
generators in C.  By use of some extremely hairy macros, it's able to
have a surprisingly natural interface, even with arbitrary parameters
and return types.

David Gibson (3):
  configurator: Add test for ucontext.h
  generator: Generators for C
  generator: Allow generators to take arguments

 ccan/generator/LICENSE               |   1 +
 ccan/generator/_info                 |  72 +++++++++++
 ccan/generator/generator.c           |  54 ++++++++
 ccan/generator/generator.h           | 239 +++++++++++++++++++++++++++++++++++
 ccan/generator/test/api.c            |  98 ++++++++++++++
 ccan/generator/test/compile_fail-1.c |  21 +++
 ccan/generator/test/compile_fail-2.c |  21 +++
 ccan/generator/test/compile_fail-3.c |  24 ++++
 ccan/generator/test/compile_fail-4.c |  23 ++++
 ccan/generator/test/compile_fail-5.c |  21 +++
 ccan/generator/test/example-gens.c   |  28 ++++
 ccan/generator/test/example-gens.h   |  10 ++
 tools/configurator/configurator.c    |  46 +++++++
 13 files changed, 658 insertions(+)
 create mode 120000 ccan/generator/LICENSE
 create mode 100644 ccan/generator/_info
 create mode 100644 ccan/generator/generator.c
 create mode 100644 ccan/generator/generator.h
 create mode 100644 ccan/generator/test/api.c
 create mode 100644 ccan/generator/test/compile_fail-1.c
 create mode 100644 ccan/generator/test/compile_fail-2.c
 create mode 100644 ccan/generator/test/compile_fail-3.c
 create mode 100644 ccan/generator/test/compile_fail-4.c
 create mode 100644 ccan/generator/test/compile_fail-5.c
 create mode 100644 ccan/generator/test/example-gens.c
 create mode 100644 ccan/generator/test/example-gens.h

-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/3] configurator: Add test for ucontext.h
  2016-02-15 12:13 [PATCH 0/3] generators in C David Gibson
@ 2016-02-15 12:13 ` David Gibson
  2016-02-15 12:13 ` [PATCH 2/3] generator: Generators for C David Gibson
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: David Gibson @ 2016-02-15 12:13 UTC (permalink / raw)
  To: ccan, rusty

This adds a new HAVE_UCONTEXT define, which indicates that ucontext.h
is present, and more-or-less works.

It also adds HAVE_POINTER_SAFE_MAKECONTEXT, which indicates whether
pointer valued arguments can be passed through the varargs parameters
to makecontext().

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 tools/configurator/configurator.c | 46 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/tools/configurator/configurator.c b/tools/configurator/configurator.c
index 021fc0f..370170b 100644
--- a/tools/configurator/configurator.c
+++ b/tools/configurator/configurator.c
@@ -319,6 +319,52 @@ static struct test tests[] = {
 	  "-Werror -fopenmp" },
 	{ "HAVE_VALGRIND_MEMCHECK_H", OUTSIDE_MAIN, NULL, NULL,
 	  "#include <valgrind/memcheck.h>\n" },
+	{ "HAVE_UCONTEXT", DEFINES_EVERYTHING|EXECUTE,
+	  NULL, NULL,
+	  "#include <ucontext.h>\n"
+	  "static int x = 0;\n"
+	  "static char stack[1024];\n"
+	  "static ucontext_t a, b;\n"
+	  "static void fn(void) {\n"
+	  "	x |= 2;\n"
+	  "	setcontext(&b);\n"
+	  "	x |= 4;\n"
+	  "}\n"
+	  "int main(int argc, char *argv[]) {\n"
+	  "	x |= 1;\n"
+	  "	getcontext(&a);\n"
+	  "	a.uc_stack.ss_sp = stack;\n"
+	  "	a.uc_stack.ss_size = sizeof(stack);\n"
+	  "	makecontext(&a, fn, 0);\n"
+	  "	swapcontext(&b, &a);\n"
+	  "	return (x == 3) ? 0 : 1;\n"
+	  "}\n"
+	},
+	{ "HAVE_POINTER_SAFE_MAKECONTEXT", DEFINES_EVERYTHING|EXECUTE,
+	  "HAVE_UCONTEXT", NULL,
+	  "#include <stddef.h>\n"
+	  "#include <ucontext.h>\n"
+	  "static int worked = 0;\n"
+	  "static char stack[1024];\n"
+	  "static ucontext_t a, b;\n"
+	  "static void fn(void *p, void *q) {\n"
+	  "	void *cp = &worked;\n"
+	  "	void *cq = (void *)(~((ptrdiff_t)cp));\n"
+	  "	if ((p == cp) && (q == cq))\n"
+	  "		worked = 1;\n"
+	  "	setcontext(&b);\n"
+	  "}\n"
+	  "int main(int argc, char *argv[]) {\n"
+	  "	void *ap = &worked;\n"
+	  "	void *aq = (void *)(~((ptrdiff_t)ap));\n"
+	  "	getcontext(&a);\n"
+	  "	a.uc_stack.ss_sp = stack;\n"
+	  "	a.uc_stack.ss_size = sizeof(stack);\n"
+	  "	makecontext(&a, (void (*)(void))fn, 2, ap, aq);\n"
+	  "	swapcontext(&b, &a);\n"
+	  "	return worked ? 0 : 1;\n"
+	  "}\n"
+	},
 };
 
 static char *grab_fd(int fd)
-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/3] generator: Generators for C
  2016-02-15 12:13 [PATCH 0/3] generators in C David Gibson
  2016-02-15 12:13 ` [PATCH 1/3] configurator: Add test for ucontext.h David Gibson
@ 2016-02-15 12:13 ` David Gibson
  2016-02-15 12:13 ` [PATCH 3/3] generator: Allow generators to take arguments David Gibson
  2016-02-25 11:26 ` [PATCH 0/3] generators in C David Gibson
  3 siblings, 0 replies; 5+ messages in thread
From: David Gibson @ 2016-02-15 12:13 UTC (permalink / raw)
  To: ccan, rusty

Generators are a limited form of co-routine, which people may be familiar
with from Python.  This module adds an implementation of generators for C.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 ccan/generator/LICENSE               |   1 +
 ccan/generator/_info                 |  71 ++++++++++++
 ccan/generator/generator.c           |  54 ++++++++++
 ccan/generator/generator.h           | 202 +++++++++++++++++++++++++++++++++++
 ccan/generator/test/api.c            |  62 +++++++++++
 ccan/generator/test/compile_fail-1.c |  21 ++++
 ccan/generator/test/compile_fail-2.c |  21 ++++
 ccan/generator/test/compile_fail-3.c |  24 +++++
 ccan/generator/test/compile_fail-4.c |  23 ++++
 ccan/generator/test/compile_fail-5.c |  21 ++++
 ccan/generator/test/example-gens.c   |  10 ++
 ccan/generator/test/example-gens.h   |   8 ++
 12 files changed, 518 insertions(+)
 create mode 120000 ccan/generator/LICENSE
 create mode 100644 ccan/generator/_info
 create mode 100644 ccan/generator/generator.c
 create mode 100644 ccan/generator/generator.h
 create mode 100644 ccan/generator/test/api.c
 create mode 100644 ccan/generator/test/compile_fail-1.c
 create mode 100644 ccan/generator/test/compile_fail-2.c
 create mode 100644 ccan/generator/test/compile_fail-3.c
 create mode 100644 ccan/generator/test/compile_fail-4.c
 create mode 100644 ccan/generator/test/compile_fail-5.c
 create mode 100644 ccan/generator/test/example-gens.c
 create mode 100644 ccan/generator/test/example-gens.h

diff --git a/ccan/generator/LICENSE b/ccan/generator/LICENSE
new file mode 120000
index 0000000..dc314ec
--- /dev/null
+++ b/ccan/generator/LICENSE
@@ -0,0 +1 @@
+../../licenses/LGPL-2.1
\ No newline at end of file
diff --git a/ccan/generator/_info b/ccan/generator/_info
new file mode 100644
index 0000000..a6570e3
--- /dev/null
+++ b/ccan/generator/_info
@@ -0,0 +1,71 @@
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * generator - generators for C
+ *
+ * Generators are a limited form of coroutines, which provide a useful
+ * way of expressing certain problems, while being much simpler to
+ * understand than general coroutines.
+ *
+ * Instead of returning a single value, a generator can "yield" a
+ * value at various points during its execution.  Whenever it yields,
+ * the "calling" function resumes and obtains the newly yielded value
+ * to work with.  When the caller asks for the next value from the
+ * generator, the generator resumes execution from the last yield and
+ * continues onto the next.
+ *
+ * Example:
+ *	#include <stdio.h>
+ *	#include <ccan/generator/generator.h>
+ *
+ *	generator_def_static(simple_gen, int)
+ *	{
+ *      	generator_yield(1);
+ *		generator_yield(3);
+ *		generator_yield(17);
+ *	}
+ *
+ *	int main(int argc, char *argv[])
+ *	{
+ *		generator_t(int) gen = simple_gen();
+ *		int *ret;
+ *
+ *		while ((ret = generator_next(gen)) != NULL) {
+ *			printf("Generator returned %d\n", *ret);
+ *		}
+ *
+ *		return 0;
+ *	}
+ *
+ * Author: David Gibson <david@gibson.dropbear.id.au>
+ * License: LGPL (v2.1 or any later version)
+ *
+ * Ccanlint:
+ *      // We need several gcc extensions
+ *	objects_build_without_features FAIL
+ *      tests_compile_without_features FAIL
+ *	tests_helpers_compile_without_features FAIL
+ */
+int main(int argc, char *argv[])
+{
+	/* Expect exactly one argument */
+	if (argc != 2)
+		return 1;
+
+	if (strcmp(argv[1], "depends") == 0) {
+		printf("ccan/build_assert\n");
+		printf("ccan/ptrint\n");
+		printf("ccan/alignof\n");
+		printf("ccan/cppmagic\n");
+		return 0;
+	}
+
+	if (strcmp(argv[1], "testdepends") == 0) {
+		printf("ccan/str\n");
+		return 0;
+	}
+
+	return 1;
+}
diff --git a/ccan/generator/generator.c b/ccan/generator/generator.c
new file mode 100644
index 0000000..d217665
--- /dev/null
+++ b/ccan/generator/generator.c
@@ -0,0 +1,54 @@
+/* Licensed LGPLv2.1+ - see LICENSE file for details */
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <ccan/alignof/alignof.h>
+
+#include <ccan/generator/generator.h>
+
+#define DEFAULT_STATE_SIZE	8192
+#define STATE_ALIGN		ALIGNOF(struct generator_)
+
+void *generator_new_(generator_wrapper_ *fn, size_t retsize)
+{
+	char *base;
+	size_t size = DEFAULT_STATE_SIZE;
+	void *ret;
+	struct generator_ *gen;
+
+	base = malloc(size);
+	if (!base)
+		abort();
+
+	retsize = (retsize + STATE_ALIGN) & ~(STATE_ALIGN - 1);
+	ret = base + size - retsize;
+	gen = (struct generator_ *)ret - 1;
+
+	gen->base = base;
+	gen->complete = false;
+
+	getcontext(&gen->gen);
+
+	gen->gen.uc_stack.ss_sp = gen->base;
+	gen->gen.uc_stack.ss_size = (char *)gen - base;
+
+	if (HAVE_POINTER_SAFE_MAKECONTEXT) {
+		makecontext(&gen->gen, (void *)fn, 1, ret);
+	} else {
+		ptrdiff_t si = ptr2int(ret);
+		ptrdiff_t mask = (1UL << (sizeof(int) * 8)) - 1;
+		int lo = si & mask;
+		int hi = si >> (sizeof(int) * 8);
+
+		makecontext(&gen->gen, (void *)fn, 2, lo, hi);
+	}
+
+	return ret;
+}
+
+void generator_free_(void *ret)
+{
+	struct generator_ *gen = generator_state_(ret);
+	free(gen->base);
+}
diff --git a/ccan/generator/generator.h b/ccan/generator/generator.h
new file mode 100644
index 0000000..6b2bd92
--- /dev/null
+++ b/ccan/generator/generator.h
@@ -0,0 +1,202 @@
+/* Licensed under LGPLv2.1+ - see LICENSE file for details */
+#ifndef CCAN_GENERATOR_H
+#define CCAN_GENERATOR_H
+#include "config.h"
+
+#if !HAVE_UCONTEXT
+#error Generators require working ucontext.h functions
+#endif
+
+#if !HAVE_TYPEOF
+#error Generators require typeof
+#endif
+
+#if !HAVE_STATEMENT_EXPR
+#error Generators require statement expressions
+#endif
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <ucontext.h>
+
+#include <ccan/ptrint/ptrint.h>
+#include <ccan/build_assert/build_assert.h>
+#include <ccan/cppmagic/cppmagic.h>
+
+/*
+ * Internals - included just for the use of inlines and macros
+ */
+
+struct generator_ {
+	ucontext_t gen;
+	ucontext_t caller;
+	bool complete;
+	void *base;
+};
+
+static inline struct generator_ *generator_state_(const void *ret)
+{
+	return (struct generator_ *)ret - 1;
+}
+
+struct generator_incomplete_;
+
+#define generator_rtype_(gen_)			\
+	typeof((*(gen_))((struct generator_incomplete_ *)NULL))
+
+#if HAVE_POINTER_SAFE_MAKECONTEXT
+#define generator_wrapper_args_()	void *ret
+#else
+#define generator_wrapper_args_()	int lo, int hi
+#endif
+typedef void generator_wrapper_(generator_wrapper_args_());
+
+void *generator_new_(generator_wrapper_ *fn, size_t retsize);
+void generator_free_(void *ret);
+
+/*
+ * API
+ */
+
+/**
+ * generator_t - type for an in-progress generator
+ * @rtype: type of values the generator yield
+ */
+#define generator_t(rtype_)			\
+	typeof(rtype_ (*)(struct generator_incomplete_ *))
+
+/**
+ * generator_declare - declare (but don't define) a generator function
+ * @name: name for the generator
+ * @rtype: return type for the generator
+ *
+ * Declares (as an extern) a generator function named @name, which
+ * will yield return values of type @rtype.
+ *
+ * Example:
+ *	generator_declare(count_to_3, int);
+ */
+#define generator_declare(name_, rtype_)	\
+	generator_t(rtype_) name_(void)
+
+/**
+ * generator_def - define a generator function
+ * @name: name for the generator
+ * @rtype: return type for the generator
+ *
+ * Define a generator function named @name yielding return values of
+ * type @rtype.  The generator_def() line is followed immediately by a
+ * block containing the generator's code.
+ *
+ * Example:
+ *	generator_def(count_to_3, int)
+ *	{
+ *		generator_yield(1);
+ *		generator_yield(2);
+ *		generator_yield(3);
+ *	}
+ */
+#define generator_def_(name_, rtype_, storage_)				\
+	static void name_##_generator_(rtype_ *ret_);			\
+	static void name_##_generator__(generator_wrapper_args_())	\
+	{								\
+		struct generator_ *gen;					\
+		CPPMAGIC_IFELSE(HAVE_POINTER_SAFE_MAKECONTEXT)		\
+			()						\
+			(ptrdiff_t hilo = ((ptrdiff_t)hi << (8*sizeof(int))) \
+			 	+ (ptrdiff_t)lo;			\
+			rtype_ *ret = (rtype_ *)int2ptr(hilo);		\
+			BUILD_ASSERT(sizeof(struct generator_ *)	\
+				     <= 2*sizeof(int));)		\
+		gen = generator_state_(ret);				\
+		name_##_generator_(ret);				\
+		gen->complete = true;					\
+		setcontext(&gen->caller);				\
+		assert(0);						\
+	}								\
+	storage_ generator_t(rtype_) name_(void)			\
+	{								\
+		return generator_new_(name_##_generator__,		\
+				      sizeof(rtype_));			\
+	}								\
+	static void name_##_generator_(rtype_ *ret_)
+#define generator_def(name_, rtype_)		\
+	generator_def_(name_, rtype_, )
+
+/**
+ * generator_def_static - define a private / local generator function
+ * @name: name for the generator
+ * @rtype: return type for the generator
+ *
+ * As generator_def, but the resulting generator function will be
+ * local to this module.
+ */
+#define generator_def_static(name_, rtype_)	\
+	generator_def_(name_, rtype_, static)
+
+/**
+ * generator_yield - yield (return) a value from a generator
+ * @val: value to yield
+ *
+ * Invoke only from within a generator.  Yield the given value to the
+ * caller.  This will stop execution of the generator code until the
+ * caller next invokes generator_next(), at which point it will
+ * continue from the generator_yield statement.
+ */
+#define generator_yield(val_)						\
+	do {								\
+		struct generator_ *gen_ = generator_state_(ret_);	\
+		int rc;							\
+		*(ret_) = (val_);					\
+		rc = swapcontext(&gen_->gen, &gen_->caller);		\
+		assert(rc == 0);					\
+	} while (0)
+
+/**
+ * generator_next - get next value from a generator
+ * @gen: a generator state variable
+ *
+ * Returns a pointer to a (correctly typed) buffer containing the next
+ * value yielded by @gen, or NULL if @gen is finished.  The buffer
+ * contents is only valid until the next time @gen is called or
+ * manipulated.
+ */
+static inline void *generator_next_(void *ret_)
+{
+	struct generator_ *gen = generator_state_(ret_);
+	int rc;
+
+	if (gen->complete)
+		return NULL;
+
+	rc = swapcontext(&gen->caller, &gen->gen);
+	assert(rc == 0);
+
+	return gen->complete ? NULL : ret_;
+}
+#define generator_next(gen_)				\
+	((generator_rtype_(gen_) *)generator_next_(gen_))
+
+/**
+ * generator_next_val - store next value from a generator
+ * @val: a variable of type suitable to store the generator's return
+ *       type (lvalue)
+ * @gen: a generator state variable
+ *
+ * Returns 'true' if @gen yielded a new value, false if @gen is
+ * complete.  If a new value was yielded, it is stored in @val.
+ */
+#define generator_next_val(val_, gen_)			\
+	({						\
+		generator_rtype_(gen_) *ret;		\
+		ret = generator_next(gen_);		\
+		if (ret)				\
+			(val_) = *ret;			\
+		!!ret;					\
+	})
+
+#define generator_free(gen_)					\
+	generator_free_((generator_rtype_(gen_) *)(gen_))
+
+#endif /* CCAN_GENERATOR_H */
diff --git a/ccan/generator/test/api.c b/ccan/generator/test/api.c
new file mode 100644
index 0000000..a5770fd
--- /dev/null
+++ b/ccan/generator/test/api.c
@@ -0,0 +1,62 @@
+#include <ccan/generator/generator.h>
+#include <ccan/tap/tap.h>
+#include <ccan/str/str.h>
+
+#include "example-gens.h"
+
+generator_def_static(genx, const char *)
+{
+	generator_yield("one");
+	generator_yield("two");
+	generator_yield("three");
+	generator_yield("four");
+}
+
+static void test1(void)
+{
+	generator_t(int) state1 = gen1();
+	int *ret;
+
+	ok1((ret = generator_next(state1)) != NULL);
+	ok1(*ret == 1);
+	ok1((ret = generator_next(state1)) != NULL);
+	ok1(*ret == 3);
+	ok1((ret = generator_next(state1)) != NULL);
+	ok1(*ret == 17);
+	ok1((ret = generator_next(state1)) == NULL);
+
+	/* Test that things don't go bad if we try to invoke an
+	 * already completed generator */
+	ok1((ret = generator_next(state1)) == NULL);
+
+	generator_free(state1);
+}
+
+static void testx(void)
+{
+	generator_t(const char *) statex = genx();
+	const char *val;
+
+	ok1(generator_next_val(val, statex));
+	ok1(streq(val, "one"));
+	ok1(generator_next_val(val, statex));
+	ok1(streq(val, "two"));
+	ok1(generator_next_val(val, statex));
+	ok1(streq(val, "three"));
+	ok1(generator_next_val(val, statex));
+	ok1(streq(val, "four"));
+	ok1(!generator_next_val(val, statex));
+	generator_free(statex);
+}
+
+int main(void)
+{
+	/* This is how many tests you plan to run */
+	plan_tests(8 + 9);
+
+	test1();
+	testx();
+
+	/* This exits depending on whether all tests passed */
+	return exit_status();
+}
diff --git a/ccan/generator/test/compile_fail-1.c b/ccan/generator/test/compile_fail-1.c
new file mode 100644
index 0000000..38e623a
--- /dev/null
+++ b/ccan/generator/test/compile_fail-1.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ccan/generator/generator.h>
+
+#include <ccan/generator/generator.c>
+
+#include "example-gens.h"
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	int *g = gen1();
+#else
+	generator_t(int) g = gen1();
+#endif
+
+	printf("%d", *generator_next(g));
+
+	exit(0);
+}
diff --git a/ccan/generator/test/compile_fail-2.c b/ccan/generator/test/compile_fail-2.c
new file mode 100644
index 0000000..7f24131
--- /dev/null
+++ b/ccan/generator/test/compile_fail-2.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ccan/generator/generator.h>
+
+#include <ccan/generator/generator.c>
+
+#include "example-gens.h"
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	generator_t(char) g = gen1();
+#else
+	generator_t(int) g = gen1();
+#endif
+
+	printf("%d", *generator_next(g));
+
+	exit(0);
+}
diff --git a/ccan/generator/test/compile_fail-3.c b/ccan/generator/test/compile_fail-3.c
new file mode 100644
index 0000000..9347efc
--- /dev/null
+++ b/ccan/generator/test/compile_fail-3.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ccan/generator/generator.h>
+
+#include <ccan/generator/generator.c>
+
+generator_def_static(intgen, int)
+{
+#ifdef FAIL
+	generator_yield("a");
+#else
+	generator_yield(1);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+	generator_t(int) g = intgen();
+
+	printf("%d", *generator_next(g));
+
+	exit(0);
+}
diff --git a/ccan/generator/test/compile_fail-4.c b/ccan/generator/test/compile_fail-4.c
new file mode 100644
index 0000000..f7297cf
--- /dev/null
+++ b/ccan/generator/test/compile_fail-4.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ccan/generator/generator.h>
+
+#include <ccan/generator/generator.c>
+
+#include "example-gens.h"
+
+int main(int argc, char *argv[])
+{
+	generator_t(int) g = gen1();
+#ifdef FAIL
+	char *val;
+#else
+	int val;
+#endif
+
+	generator_next_val(val, g);
+	printf("%d", val);
+
+	exit(0);
+}
diff --git a/ccan/generator/test/compile_fail-5.c b/ccan/generator/test/compile_fail-5.c
new file mode 100644
index 0000000..ba8ced9
--- /dev/null
+++ b/ccan/generator/test/compile_fail-5.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ccan/generator/generator.h>
+
+#include <ccan/generator/generator.c>
+
+#include "example-gens.h"
+
+int main(int argc, char *argv[])
+{
+#ifdef FAIL
+	int *g = NULL;
+#else
+	generator_t(int) g = gen1();
+#endif
+
+	generator_free(g);
+
+	exit(0);
+}
diff --git a/ccan/generator/test/example-gens.c b/ccan/generator/test/example-gens.c
new file mode 100644
index 0000000..c554011
--- /dev/null
+++ b/ccan/generator/test/example-gens.c
@@ -0,0 +1,10 @@
+#include <ccan/generator/generator.h>
+
+#include "example-gens.h"
+
+generator_def(gen1, int)
+{
+	generator_yield(1);
+	generator_yield(3);
+	generator_yield(17);
+}
diff --git a/ccan/generator/test/example-gens.h b/ccan/generator/test/example-gens.h
new file mode 100644
index 0000000..cf4ce3b
--- /dev/null
+++ b/ccan/generator/test/example-gens.h
@@ -0,0 +1,8 @@
+#ifndef _EXAMPLE_GENS_H
+#define _EXAMPLE_GENS_H
+
+#include <ccan/generator/generator.h>
+
+generator_declare(gen1, int);
+
+#endif /* _EXAMPLE_GENS_H */
-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/3] generator: Allow generators to take arguments
  2016-02-15 12:13 [PATCH 0/3] generators in C David Gibson
  2016-02-15 12:13 ` [PATCH 1/3] configurator: Add test for ucontext.h David Gibson
  2016-02-15 12:13 ` [PATCH 2/3] generator: Generators for C David Gibson
@ 2016-02-15 12:13 ` David Gibson
  2016-02-25 11:26 ` [PATCH 0/3] generators in C David Gibson
  3 siblings, 0 replies; 5+ messages in thread
From: David Gibson @ 2016-02-15 12:13 UTC (permalink / raw)
  To: ccan, rusty

Using some serious macro magic, this patch extends generators to allow
them to take arbitrary arguments.  The arguments are marshalled into a
structure placed at the far end of the generator's stack when it is
created.  Then, they're unmarshalled back into C parameters when we first
context switch into the generator.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 ccan/generator/_info               |  1 +
 ccan/generator/generator.h         | 63 ++++++++++++++++++++++++++++++--------
 ccan/generator/test/api.c          | 38 ++++++++++++++++++++++-
 ccan/generator/test/example-gens.c | 18 +++++++++++
 ccan/generator/test/example-gens.h |  2 ++
 5 files changed, 108 insertions(+), 14 deletions(-)

diff --git a/ccan/generator/_info b/ccan/generator/_info
index a6570e3..b0b7343 100644
--- a/ccan/generator/_info
+++ b/ccan/generator/_info
@@ -59,6 +59,7 @@ int main(int argc, char *argv[])
 		printf("ccan/ptrint\n");
 		printf("ccan/alignof\n");
 		printf("ccan/cppmagic\n");
+		printf("ccan/compiler\n");
 		return 0;
 	}
 
diff --git a/ccan/generator/generator.h b/ccan/generator/generator.h
index 6b2bd92..121f14a 100644
--- a/ccan/generator/generator.h
+++ b/ccan/generator/generator.h
@@ -23,6 +23,7 @@
 #include <ccan/ptrint/ptrint.h>
 #include <ccan/build_assert/build_assert.h>
 #include <ccan/cppmagic/cppmagic.h>
+#include <ccan/compiler/compiler.h>
 
 /*
  * Internals - included just for the use of inlines and macros
@@ -40,6 +41,11 @@ static inline struct generator_ *generator_state_(const void *ret)
 	return (struct generator_ *)ret - 1;
 }
 
+static inline void *generator_argp_(const void *ret)
+{
+	return generator_state_(ret)->base;
+}
+
 struct generator_incomplete_;
 
 #define generator_rtype_(gen_)			\
@@ -77,8 +83,8 @@ void generator_free_(void *ret);
  * Example:
  *	generator_declare(count_to_3, int);
  */
-#define generator_declare(name_, rtype_)	\
-	generator_t(rtype_) name_(void)
+#define generator_declare(name_, rtype_, ...)	\
+	generator_t(rtype_) name_(generator_parms_outer_(__VA_ARGS__))
 
 /**
  * generator_def - define a generator function
@@ -97,11 +103,35 @@ void generator_free_(void *ret);
  *		generator_yield(3);
  *	}
  */
-#define generator_def_(name_, rtype_, storage_)				\
-	static void name_##_generator_(rtype_ *ret_);			\
+#define generator_parm_(t_, n_)			t_ n_
+#define generator_parms_(...)						\
+	CPPMAGIC_2MAP(generator_parm_, __VA_ARGS__)
+#define generator_parms_inner_(...)					\
+	CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__))			\
+		(, generator_parms_(__VA_ARGS__))()
+#define generator_parms_outer_(...)					\
+	CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__))	\
+		(generator_parms_(__VA_ARGS__))(void)
+#define generator_argfield_(t_, n_)		t_ n_;
+#define generator_argstruct_(...)					\
+	struct {							\
+		CPPMAGIC_JOIN(, CPPMAGIC_2MAP(generator_argfield_,	\
+					      __VA_ARGS__))		\
+	}
+#define generator_arg_unpack_(t_, n_)		args->n_
+#define generator_args_unpack_(...)		\
+	CPPMAGIC_IFELSE(CPPMAGIC_NONEMPTY(__VA_ARGS__))			\
+		(, CPPMAGIC_2MAP(generator_arg_unpack_, __VA_ARGS__))()
+#define generator_arg_pack_(t_, n_)		args->n_ = n_
+#define generator_args_pack_(...)					\
+	CPPMAGIC_JOIN(;, CPPMAGIC_2MAP(generator_arg_pack_, __VA_ARGS__))
+#define generator_def_(name_, rtype_, storage_, ...)			\
+	static void name_##_generator_(rtype_ *ret_			\
+				       generator_parms_inner_(__VA_ARGS__)); \
 	static void name_##_generator__(generator_wrapper_args_())	\
 	{								\
 		struct generator_ *gen;					\
+		UNNEEDED generator_argstruct_(__VA_ARGS__) *args;	\
 		CPPMAGIC_IFELSE(HAVE_POINTER_SAFE_MAKECONTEXT)		\
 			()						\
 			(ptrdiff_t hilo = ((ptrdiff_t)hi << (8*sizeof(int))) \
@@ -110,19 +140,26 @@ void generator_free_(void *ret);
 			BUILD_ASSERT(sizeof(struct generator_ *)	\
 				     <= 2*sizeof(int));)		\
 		gen = generator_state_(ret);				\
-		name_##_generator_(ret);				\
+		args = generator_argp_(ret);				\
+		name_##_generator_(ret generator_args_unpack_(__VA_ARGS__)); \
 		gen->complete = true;					\
 		setcontext(&gen->caller);				\
 		assert(0);						\
 	}								\
-	storage_ generator_t(rtype_) name_(void)			\
+	storage_ generator_t(rtype_)					\
+	name_(generator_parms_outer_(__VA_ARGS__))			\
 	{								\
-		return generator_new_(name_##_generator__,		\
-				      sizeof(rtype_));			\
+		generator_t(rtype_) gen = generator_new_(name_##_generator__, \
+							 sizeof(rtype_)); \
+		UNNEEDED generator_argstruct_(__VA_ARGS__) *args =	\
+			generator_argp_(gen);				\
+		generator_args_pack_(__VA_ARGS__);			\
+		return gen;						\
 	}								\
-	static void name_##_generator_(rtype_ *ret_)
-#define generator_def(name_, rtype_)		\
-	generator_def_(name_, rtype_, )
+	static void name_##_generator_(rtype_ *ret_			\
+				       generator_parms_inner_(__VA_ARGS__))
+#define generator_def(name_, rtype_, ...)	\
+	generator_def_(name_, rtype_, , __VA_ARGS__)
 
 /**
  * generator_def_static - define a private / local generator function
@@ -132,8 +169,8 @@ void generator_free_(void *ret);
  * As generator_def, but the resulting generator function will be
  * local to this module.
  */
-#define generator_def_static(name_, rtype_)	\
-	generator_def_(name_, rtype_, static)
+#define generator_def_static(name_, rtype_, ...)	\
+	generator_def_(name_, rtype_, static, __VA_ARGS__)
 
 /**
  * generator_yield - yield (return) a value from a generator
diff --git a/ccan/generator/test/api.c b/ccan/generator/test/api.c
index a5770fd..56e98cc 100644
--- a/ccan/generator/test/api.c
+++ b/ccan/generator/test/api.c
@@ -32,6 +32,40 @@ static void test1(void)
 	generator_free(state1);
 }
 
+static void test2(void)
+{
+	generator_t(int) state2 = gen2(100);
+	int *ret;
+
+	ok1((ret = generator_next(state2)) != NULL);
+	ok1(*ret == 101);
+	ok1((ret = generator_next(state2)) != NULL);
+	ok1(*ret == 103);
+	ok1((ret = generator_next(state2)) != NULL);
+	ok1(*ret == 117);
+	ok1((ret = generator_next(state2)) == NULL);
+
+	generator_free(state2);
+}
+
+static void test3(void)
+{
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		generator_t(const char *) state3 = gen3("test", i);
+		const char *s;
+		int j;
+
+		for (j = 0; j < i; j++) {
+			ok1(generator_next_val(s, state3));
+			ok1(streq(s, "test"));
+		}
+		ok1(!generator_next_val(s, state3));
+		generator_free(state3);
+	}
+}
+
 static void testx(void)
 {
 	generator_t(const char *) statex = genx();
@@ -52,9 +86,11 @@ static void testx(void)
 int main(void)
 {
 	/* This is how many tests you plan to run */
-	plan_tests(8 + 9);
+	plan_tests(8 + 7 + 16 + 9);
 
 	test1();
+	test2();
+	test3();
 	testx();
 
 	/* This exits depending on whether all tests passed */
diff --git a/ccan/generator/test/example-gens.c b/ccan/generator/test/example-gens.c
index c554011..ec3157d 100644
--- a/ccan/generator/test/example-gens.c
+++ b/ccan/generator/test/example-gens.c
@@ -1,3 +1,5 @@
+#include <stdio.h>
+
 #include <ccan/generator/generator.h>
 
 #include "example-gens.h"
@@ -8,3 +10,19 @@ generator_def(gen1, int)
 	generator_yield(3);
 	generator_yield(17);
 }
+
+generator_def(gen2, int, int, base)
+{
+	generator_yield(base + 1);
+	generator_yield(base + 3);
+	generator_yield(base + 17);
+}
+
+generator_def(gen3, const char *, const char *, str, int, count)
+{
+	int i;
+
+	for (i = 0; i < count; i++)
+		generator_yield(str);
+}
+
diff --git a/ccan/generator/test/example-gens.h b/ccan/generator/test/example-gens.h
index cf4ce3b..2b9076f 100644
--- a/ccan/generator/test/example-gens.h
+++ b/ccan/generator/test/example-gens.h
@@ -4,5 +4,7 @@
 #include <ccan/generator/generator.h>
 
 generator_declare(gen1, int);
+generator_declare(gen2, int, int, base);
+generator_declare(gen3, const char *, const char *, str, int, count);
 
 #endif /* _EXAMPLE_GENS_H */
-- 
2.5.0

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/3] generators in C
  2016-02-15 12:13 [PATCH 0/3] generators in C David Gibson
                   ` (2 preceding siblings ...)
  2016-02-15 12:13 ` [PATCH 3/3] generator: Allow generators to take arguments David Gibson
@ 2016-02-25 11:26 ` David Gibson
  3 siblings, 0 replies; 5+ messages in thread
From: David Gibson @ 2016-02-25 11:26 UTC (permalink / raw)
  To: ccan, rusty


[-- Attachment #1.1: Type: text/plain, Size: 560 bytes --]

On Mon, Feb 15, 2016 at 11:13:21PM +1100, David Gibson wrote:
> This series add's the 'generator' module which implements Python-like
> generators in C.  By use of some extremely hairy macros, it's able to
> have a surprisingly natural interface, even with arbitrary parameters
> and return types.

I'm taking silence as assent and have merged this module.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 127 bytes --]

_______________________________________________
ccan mailing list
ccan@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/ccan

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2016-02-25 11:45 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-02-15 12:13 [PATCH 0/3] generators in C David Gibson
2016-02-15 12:13 ` [PATCH 1/3] configurator: Add test for ucontext.h David Gibson
2016-02-15 12:13 ` [PATCH 2/3] generator: Generators for C David Gibson
2016-02-15 12:13 ` [PATCH 3/3] generator: Allow generators to take arguments David Gibson
2016-02-25 11:26 ` [PATCH 0/3] generators in C David Gibson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox