This is the mail archive of the
libffi-discuss@sourceware.org
mailing list for the libffi project.
RFC: Delegates (or something)
- From: William Ahern <william at 25thandClement dot com>
- To: libffi-discuss at sourceware dot org
- Date: Sat, 18 Apr 2009 15:04:33 -0700
- Subject: 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 */