This is the mail archive of the libffi-discuss@sourceware.org mailing list for the libffi project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: Delegates (or something)


I'm curious of people's thoughts on this. Especially if somebody can point
out a bug, including the "you just got lucky that it works" kind.

Not exactly sure what the proper terminology is called. It's very similar to
what C# calls delegates, except you can't pass new parameters to the
invocation. Similar to a lambda expression, perhaps. Practically speaking,
it's a neat hack to abstract away (with what _should_ be almost zero
overhead) manually initializing and invoking the ffi_cif object by using
GCC's type introspection capabilities.

This file can be compiled directly. It includes a small regression test, and
can be compiled directly into an executable with -DDELEGATE_MAIN. It "works
for me" on OS X x86-32 and OpenBSD 4.4 amd64. This is a slightly modified
rehash of something I came up w/ the other night for using with libevent.

It depends on the GCC builtins __builtin_choose_expr(),
__builtin_types_compatible_p(), and typeof(); GCC compound expressions; and
C99 compound literals and __VA_ARGS__.

Other than libffi, is there any other dependency or behavior I'm leaving
out? While not instrinsic to the concept, is it acceptable to recursively
(or serially, even) invoke ffi_call() on the same cif object?

Anybody with an Intel compiler, or any other compiler w/ such GCC support,
that can give it a test drive?

Note that the textual pre-processor output is enormous (on the order of a
megabyte+ per macro invocation), but GCC seems to handle the code just fine,
w/o any perceptible compilation slow-down. GCC is clever enough.

The small regression test at the bottom shows usage.


/* ==========================================================================
 * delegate.h - Not quite closures.
 * --------------------------------------------------------------------------
 * Copyright (c) 2009  William Ahern
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ==========================================================================
 */
#ifndef DELEGATE_H
#define DELEGATE_H

#include <stdint.h>

#include <string.h>

#include <ffi/ffi.h>


#define ffi_typeof(p) __typeof__(					\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), uint8_t),		\
	(uint8_t){ 0 },							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), int8_t),		\
	(int8_t){ 0 },							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), uint16_t),		\
	(uint16_t){ 0 },						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), int16_t),		\
	(int16_t){ 0 },							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), uint32_t),		\
	(uint32_t){ 0 },						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), int32_t),		\
	(int32_t){ 0 },							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), uint64_t),		\
	(uint64_t){ 0 },						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), int64_t),		\
	(int64_t){ 0 },							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), float),		\
	(float){ 0 },							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), double),		\
	(double){ 0 },							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), long double),	\
	(long double){ 0 },						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(__typeof__(p), void *),		\
	(void *){ 0 }, (void *){ 0 }					\
	)))))))))))))


#define ffi_type(p)							\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), uint8_t),		\
	&ffi_type_uint8,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), int8_t),		\
	&ffi_type_sint8,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), uint16_t),		\
	&ffi_type_uint16,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), int16_t),		\
	&ffi_type_sint16,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), uint32_t),		\
	&ffi_type_uint32,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), int32_t),		\
	&ffi_type_sint32,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), uint64_t),		\
	&ffi_type_uint64,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), int64_t),		\
	&ffi_type_sint64,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), float),		\
	&ffi_type_float,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), double),		\
	&ffi_type_double,						\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(ffi_typeof(p), void *),		\
	&ffi_type_pointer, &ffi_type_void				\
	)))))))))))


#define DELEGATE_NARG_(a, b, c, d, e, f, g, N,...) N
#define DELEGATE_NARG(...) DELEGATE_NARG_(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)

#define DELEGATE_INITIALIZER { -1, 0 }

struct delegate {
	void (*fn)();

	int argc;

	union {
		long long ll;
		unsigned long long llu;
		void *p;
		long double g;
	} args[7];

	ffi_type *types[7];

	ffi_cif cif;
}; /* struct delegate */


#define delegate_copyarg(del, argn, arg) \
	({ memcpy(&(del)->args[(argn)], &(arg), sizeof (ffi_typeof(arg))); \
	   (del)->types[(argn)] = ffi_type(arg); })

#define delegate_setarg(del, argn, arg) \
	delegate_copyarg((del), (argn), (ffi_typeof((arg))){(arg)})

#define delegate7(del, fn, argc, arg0, arg1, arg2, arg3, arg4, arg5, arg6, ...) \
	({ __builtin_choose_expr(((argc) > 0), delegate_setarg((del), 0, (arg0)), (void)0); \
	   __builtin_choose_expr(((argc) > 1), delegate_setarg((del), 1, (arg1)), (void)0); \
	   __builtin_choose_expr(((argc) > 2), delegate_setarg((del), 2, (arg2)), (void)0); \
	   __builtin_choose_expr(((argc) > 3), delegate_setarg((del), 3, (arg3)), (void)0); \
	   __builtin_choose_expr(((argc) > 4), delegate_setarg((del), 4, (arg4)), (void)0); \
	   __builtin_choose_expr(((argc) > 5), delegate_setarg((del), 5, (arg5)), (void)0); \
	   __builtin_choose_expr(((argc) > 6), delegate_setarg((del), 6, (arg6)), (void)0); \
	   delegate_init((del), (fn), (argc)); })

#define delegateN1(del, fn, ...) \
	delegate7((del), (fn), (DELEGATE_NARG(__VA_ARGS__) - 1), __VA_ARGS__, 0, 0, 0, 0, 0, 0, 0)

#define delegate(...) delegateN1(__VA_ARGS__, 0) // In case of 0 parameters


static inline struct delegate *delegate_init(struct delegate *del, void (*fn)(), int argc) {
	if (FFI_OK != ffi_prep_cif(&del->cif, FFI_DEFAULT_ABI, argc, &ffi_type_void, del->types))
		return 0;

	del->fn   = fn;
	del->argc = argc;

	return del;
} /* delegate_init() */


static inline void invoke(struct delegate *del) {
	void *args[sizeof del->args / sizeof del->args[0]];
	ffi_arg retval;
	int argc;

	for (argc = 0; argc < del->argc; argc++)
		args[argc] = &del->args[argc];

	ffi_call(&del->cif, FFI_FN(del->fn), &retval, args);
} /* invoke() */


#endif /* DELEGATE_H */


#if DELEGATE_MAIN

#include <stdio.h>

static void func(long long ll, const char *str, struct delegate *del) {
	static int depth;

	depth++;

	printf("ll:%lld str:[%s] del:%p depth:%d\n", ll, str, (void *)del, depth);

	if (depth < 3)
		invoke(del);

	depth--;
} /* func() */


int main(int argc, char *argv[]) {
	char *hello = "hello world again";
	struct delegate del;

	invoke(delegate(&del, &func, 0xdeafbeefLL, "hello world", &del));

	invoke(delegate(&del, &func, 0x0ffffffffffffff0LL, hello, &del));

	return 0;
} /* main() */

#endif /* DELEGATE_MAIN */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]