Commit b0fa019a authored by Rusty Russell's avatar Rusty Russell

typesafe_cb: simplify, preserve namespace.

Get rid of many variants, which were just confusing for most people.
Keep typesafe_cb(), typesafe_cb_preargs() and typesafe_cb_postarts(),
and rework cast_if_type() into typesafe_cb_cast() so we stay in our
namespace.

I should have done this as soon as I discovered the limitation that
the types have to be defined if I want const-taking callbacks.
parent 076877c2
...@@ -16,7 +16,7 @@ const void *at_pool_ctx(struct at_pool *atp); ...@@ -16,7 +16,7 @@ const void *at_pool_ctx(struct at_pool *atp);
/* Creating an antithread via fork(). Returned athread is child of pool. */ /* Creating an antithread via fork(). Returned athread is child of pool. */
#define at_run(pool, fn, arg) \ #define at_run(pool, fn, arg) \
_at_run(pool, \ _at_run(pool, \
typesafe_cb_preargs(void *, (fn), (arg), struct at_pool *), \ typesafe_cb_preargs(void *, void *, (fn), (arg), struct at_pool *), \
(arg)) (arg))
/* Fork and execvp, with added arguments for child to grab. /* Fork and execvp, with added arguments for child to grab.
......
...@@ -23,10 +23,11 @@ ...@@ -23,10 +23,11 @@
#if HAVE_TYPEOF #if HAVE_TYPEOF
#define asearch(key, base, num, cmp) \ #define asearch(key, base, num, cmp) \
((__typeof__(*(base))*)(bsearch((key), (base), (num), sizeof(*(base)), \ ((__typeof__(*(base))*)(bsearch((key), (base), (num), sizeof(*(base)), \
cast_if_type(int (*)(const void *, const void *), \ typesafe_cb_cast(int (*)(const void *, const void *), \
(cmp), &*(cmp), \ int (*)(const __typeof__(*(key)) *, \
int (*)(const __typeof__(*(key)) *, \ const __typeof__(*(base)) *), \
const __typeof__(*(base)) *))))) (cmp)))))
#else #else
#define asearch(key, base, num, cmp) \ #define asearch(key, base, num, cmp) \
(bsearch((key), (base), (num), sizeof(*(base)), \ (bsearch((key), (base), (num), sizeof(*(base)), \
......
...@@ -19,11 +19,12 @@ ...@@ -19,11 +19,12 @@
*/ */
#define asort(base, num, cmp, ctx) \ #define asort(base, num, cmp, ctx) \
_asort((base), (num), sizeof(*(base)), \ _asort((base), (num), sizeof(*(base)), \
cast_if_type(int (*)(const void *, const void *, void *), \ typesafe_cb_cast(int (*)(const void *, const void *, void *), \
(cmp), &*(cmp), \ int (*)(const __typeof__(*(base)) *, \
int (*)(const __typeof__(*(base)) *, \ const __typeof__(*(base)) *, \
const __typeof__(*(base)) *, \ __typeof__(ctx)), \
__typeof__(ctx))), (ctx)) (cmp)), \
(ctx))
#if HAVE_QSORT_R_PRIVATE_LAST #if HAVE_QSORT_R_PRIVATE_LAST
#define _asort(b, n, s, cmp, ctx) qsort_r(b, n, s, cmp, ctx) #define _asort(b, n, s, cmp, ctx) qsort_r(b, n, s, cmp, ctx)
......
...@@ -315,22 +315,22 @@ struct opt_table { ...@@ -315,22 +315,22 @@ struct opt_table {
/* Resolves to the four parameters for non-arg callbacks. */ /* Resolves to the four parameters for non-arg callbacks. */
#define OPT_CB_NOARG(cb, arg) \ #define OPT_CB_NOARG(cb, arg) \
OPT_NOARG, \ OPT_NOARG, \
cast_if_any(char *(*)(void *), (cb), 0?(cb):(cb),\ typesafe_cb_cast3(char *(*)(void *), \
char *(*)(typeof(*(arg))*), \ char *(*)(typeof(*(arg))*), \
char *(*)(const typeof(*(arg))*), \ char *(*)(const typeof(*(arg))*), \
char *(*)(const void *)), \ char *(*)(const void *), (cb)), \
NULL, NULL NULL, NULL
/* Resolves to the four parameters for arg callbacks. */ /* Resolves to the four parameters for arg callbacks. */
#define OPT_CB_ARG(cb, show, arg) \ #define OPT_CB_ARG(cb, show, arg) \
OPT_HASARG, NULL, \ OPT_HASARG, NULL, \
cast_if_any(char *(*)(const char *,void *), (cb), 0?(cb):(cb), \ typesafe_cb_cast3(char *(*)(const char *,void *), \
char *(*)(const char *, typeof(*(arg))*), \ char *(*)(const char *, typeof(*(arg))*), \
char *(*)(const char *, const typeof(*(arg))*), \ char *(*)(const char *, const typeof(*(arg))*), \
char *(*)(const char *, const void *)), \ char *(*)(const char *, const void *), \
cast_if_type(void (*)(char buf[], const void *), (show), \ (cb)), \
0?(show):(show), \ typesafe_cb_cast(void (*)(char buf[], const void *), \
void (*)(char buf[], const typeof(*(arg))*)) void (*)(char buf[], const typeof(*(arg))*), (show))
/* Non-typesafe register function. */ /* Non-typesafe register function. */
void _opt_register(const char *names, enum opt_type type, void _opt_register(const char *names, enum opt_type type,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Example: * Example:
* #include <ccan/sparse_bsearch/sparse_bsearch.h> * #include <ccan/sparse_bsearch/sparse_bsearch.h>
* *
* static bool val_valid(unsigned int *val) * static bool val_valid(const unsigned int *val)
* { * {
* return *val != 0; * return *val != 0;
* } * }
......
...@@ -14,9 +14,10 @@ ...@@ -14,9 +14,10 @@
* @validfn: whether this element is valid. * @validfn: whether this element is valid.
* *
* Binary search of a sorted array, which may have some invalid entries. * Binary search of a sorted array, which may have some invalid entries.
* Note that cmpfn and validfn take const pointers.
* *
* Example: * Example:
* static bool val_valid(unsigned int *val) * static bool val_valid(const unsigned int *val)
* { * {
* return *val != 0; * return *val != 0;
* } * }
...@@ -40,8 +41,13 @@ ...@@ -40,8 +41,13 @@
#define sparse_bsearch(key, base, nmemb, cmpfn, validfn) \ #define sparse_bsearch(key, base, nmemb, cmpfn, validfn) \
_sparse_bsearch((key)+check_types_match((key), &(base)[0]), \ _sparse_bsearch((key)+check_types_match((key), &(base)[0]), \
(base), (nmemb), sizeof((base)[0]), \ (base), (nmemb), sizeof((base)[0]), \
typesafe_cb_cmp(int, (cmpfn), (base)), \ typesafe_cb_cast(int (*)(const void *, const void *), \
typesafe_cb_const(bool, (validfn), (base))) int (*)(const __typeof__(*(base)) *, \
const __typeof__(*(base)) *), \
(cmpfn)), \
typesafe_cb_cast(bool (*)(const void *), \
bool (*)(const __typeof__(*(base)) *), \
(validfn)))
void *_sparse_bsearch(const void *key, const void *base, void *_sparse_bsearch(const void *key, const void *base,
size_t nmemb, size_t size, size_t nmemb, size_t size,
......
...@@ -210,7 +210,7 @@ int talloc_free(const void *ptr); ...@@ -210,7 +210,7 @@ int talloc_free(const void *ptr);
* talloc, talloc_free * talloc, talloc_free
*/ */
#define talloc_set_destructor(ptr, function) \ #define talloc_set_destructor(ptr, function) \
_talloc_set_destructor((ptr), typesafe_cb_def(int, (function), (ptr))) _talloc_set_destructor((ptr), typesafe_cb(int, void *, (function), (ptr)))
/** /**
* talloc_zero - allocate zeroed dynamic memory for a type * talloc_zero - allocate zeroed dynamic memory for a type
......
...@@ -311,7 +311,7 @@ enum TDB_ERROR tdb_transaction_prepare_commit(struct tdb_context *tdb); ...@@ -311,7 +311,7 @@ enum TDB_ERROR tdb_transaction_prepare_commit(struct tdb_context *tdb);
* a negative enum TDB_ERROR value. * a negative enum TDB_ERROR value.
*/ */
#define tdb_traverse(tdb, fn, p) \ #define tdb_traverse(tdb, fn, p) \
tdb_traverse_(tdb, typesafe_cb_preargs(int, (fn), (p), \ tdb_traverse_(tdb, typesafe_cb_preargs(int, void *, (fn), (p), \
struct tdb_context *, \ struct tdb_context *, \
TDB_DATA, TDB_DATA), (p)) TDB_DATA, TDB_DATA), (p))
...@@ -334,7 +334,8 @@ int64_t tdb_traverse_(struct tdb_context *tdb, ...@@ -334,7 +334,8 @@ int64_t tdb_traverse_(struct tdb_context *tdb,
*/ */
#define tdb_parse_record(tdb, key, parse, p) \ #define tdb_parse_record(tdb, key, parse, p) \
tdb_parse_record_((tdb), (key), \ tdb_parse_record_((tdb), (key), \
typesafe_cb_preargs(enum TDB_ERROR, (parse), (p), \ typesafe_cb_preargs(enum TDB_ERROR, void *, \
(parse), (p), \
TDB_DATA, TDB_DATA), (p)) TDB_DATA, TDB_DATA), (p))
enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb, enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb,
...@@ -472,7 +473,7 @@ enum TDB_ERROR tdb_wipe_all(struct tdb_context *tdb); ...@@ -472,7 +473,7 @@ enum TDB_ERROR tdb_wipe_all(struct tdb_context *tdb);
* Returns TDB_SUCCESS or an error. * Returns TDB_SUCCESS or an error.
*/ */
#define tdb_check(tdb, check, private_data) \ #define tdb_check(tdb, check, private_data) \
tdb_check_((tdb), typesafe_cb_preargs(enum TDB_ERROR, \ tdb_check_((tdb), typesafe_cb_preargs(enum TDB_ERROR, void *, \
(check), (private_data), \ (check), (private_data), \
struct tdb_data, \ struct tdb_data, \
struct tdb_data), \ struct tdb_data), \
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
/** /**
* typesafe_cb - macros for safe callbacks. * typesafe_cb - macros for safe callbacks.
* *
* The basis of the typesafe_cb header is cast_if_type(): a * The basis of the typesafe_cb header is typesafe_cb_cast(): a
* conditional cast macro. If an expression exactly matches a given * conditional cast macro. If an expression exactly matches a given
* type, it is cast to the target type, otherwise it is left alone. * type, it is cast to the target type, otherwise it is left alone.
* *
...@@ -35,15 +35,16 @@ ...@@ -35,15 +35,16 @@
* the exactly correct function type to match the argument, or a * the exactly correct function type to match the argument, or a
* function which takes a void *. * function which takes a void *.
* *
* This is where typesafe_cb() comes in: it uses cast_if_type() to * This is where typesafe_cb() comes in: it uses typesafe_cb_cast() to
* cast the callback function if it matches the argument type: * cast the callback function if it matches the argument type:
* *
* void _register_callback(void (*cb)(void *arg), void *arg); * void _register_callback(void (*cb)(void *arg), void *arg);
* #define register_callback(cb, arg) \ * #define register_callback(cb, arg) \
* _register_callback(typesafe_cb(void, (cb), (arg)), (arg)) * _register_callback(typesafe_cb(void, void *, (cb), (arg)), \
* (arg))
* *
* On compilers which don't support the extensions required * On compilers which don't support the extensions required
* cast_if_type() and friend become an unconditional cast, so your * typesafe_cb_cast() and friend become an unconditional cast, so your
* code will compile but you won't get type checking. * code will compile but you won't get type checking.
* *
* Example: * Example:
...@@ -72,7 +73,8 @@ ...@@ -72,7 +73,8 @@
* } * }
* #define register_callback(value, cb, arg) \ * #define register_callback(value, cb, arg) \
* _register_callback(value, \ * _register_callback(value, \
* typesafe_cb_preargs(int, (cb), (arg), int),\ * typesafe_cb_preargs(int, void *, \
* (cb), (arg), int),\
* (arg)) * (arg))
* *
* static struct callback *find_callback(int value) * static struct callback *find_callback(int value)
......
...@@ -6,14 +6,14 @@ static void _set_some_value(void *val) ...@@ -6,14 +6,14 @@ static void _set_some_value(void *val)
} }
#define set_some_value(expr) \ #define set_some_value(expr) \
_set_some_value(cast_if_type(void *, (expr), (expr), long)) _set_some_value(typesafe_cb_cast(void *, long, (expr)))
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#ifdef FAIL #ifdef FAIL
bool x = 0; bool x = 0;
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P #if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_if_type is a noop." #error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
#endif #endif
#else #else
long x = 0; long x = 0;
......
...@@ -6,7 +6,7 @@ static void _register_callback(void (*cb)(void *arg), void *arg) ...@@ -6,7 +6,7 @@ static void _register_callback(void (*cb)(void *arg), void *arg)
} }
#define register_callback(cb, arg) \ #define register_callback(cb, arg) \
_register_callback(typesafe_cb(void, (cb), (arg)), (arg)) _register_callback(typesafe_cb(void, void *, (cb), (arg)), (arg))
static void my_callback(char *p) static void my_callback(char *p)
{ {
...@@ -18,7 +18,7 @@ int main(int argc, char *argv[]) ...@@ -18,7 +18,7 @@ int main(int argc, char *argv[])
#ifdef FAIL #ifdef FAIL
int *p; int *p;
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P #if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_if_type is a noop." #error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
#endif #endif
#else #else
char *p; char *p;
......
...@@ -29,14 +29,15 @@ int main(int argc, char *argv[]) ...@@ -29,14 +29,15 @@ int main(int argc, char *argv[])
{ {
#ifdef FAIL #ifdef FAIL
struct other struct other
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P #if !HAVE_TYPEOF || !HAVE_CAST_TO_UNION
#error "Unfortunately we don't fail if cast_if_type is a noop." #error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
#endif #endif
#else #else
struct foo struct foo
#endif #endif
*arg = NULL; *arg = NULL;
take_any(cast_if_any(struct any *, arg, arg, take_any(typesafe_cb_cast3(struct any *,
struct foo *, struct bar *, struct baz *)); struct foo *, struct bar *, struct baz *,
arg));
return 0; return 0;
} }
...@@ -7,15 +7,15 @@ void _set_some_value(void *val) ...@@ -7,15 +7,15 @@ void _set_some_value(void *val)
} }
#define set_some_value(expr) \ #define set_some_value(expr) \
_set_some_value(cast_if_type(void *, (expr), (expr), unsigned long)) _set_some_value(typesafe_cb_cast(void *, unsigned long, (expr)))
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#ifdef FAIL #ifdef FAIL
int x = 0; int x = 0;
set_some_value(x); set_some_value(x);
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P #if !HAVE_TYPEOF||!HAVE_CAST_TO_UNION
#error "Unfortunately we don't fail if cast_if_type is a noop." #error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
#endif #endif
#else #else
void *p = 0; void *p = 0;
......
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h>
static void _register_callback(void (*cb)(void *arg), const void *arg)
{
}
#define register_callback(cb, arg) \
_register_callback(typesafe_cb_exact(void, (cb), (arg)), (arg))
static void my_callback(const char *p)
{
}
int main(int argc, char *argv[])
{
#ifdef FAIL
char *p;
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_if_type is a noop."
#endif
#else
const char *p;
#endif
p = NULL;
/* This should work always. */
register_callback(my_callback, (const char *)"hello world");
/* This will fail with FAIL defined */
register_callback(my_callback, p);
return 0;
}
...@@ -5,7 +5,7 @@ static void _register_callback(void (*cb)(void *arg, int x), void *arg) ...@@ -5,7 +5,7 @@ static void _register_callback(void (*cb)(void *arg, int x), void *arg)
{ {
} }
#define register_callback(cb, arg) \ #define register_callback(cb, arg) \
_register_callback(typesafe_cb_postargs(void, (cb), (arg), int), (arg)) _register_callback(typesafe_cb_postargs(void, void *, (cb), (arg), int), (arg))
static void my_callback(char *p, int x) static void my_callback(char *p, int x)
{ {
...@@ -16,7 +16,7 @@ int main(int argc, char *argv[]) ...@@ -16,7 +16,7 @@ int main(int argc, char *argv[])
#ifdef FAIL #ifdef FAIL
int *p; int *p;
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P #if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_if_type is a noop." #error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
#endif #endif
#else #else
char *p; char *p;
......
...@@ -6,7 +6,7 @@ static void _register_callback(void (*cb)(int x, void *arg), void *arg) ...@@ -6,7 +6,7 @@ static void _register_callback(void (*cb)(int x, void *arg), void *arg)
} }
#define register_callback(cb, arg) \ #define register_callback(cb, arg) \
_register_callback(typesafe_cb_preargs(void, (cb), (arg), int), (arg)) _register_callback(typesafe_cb_preargs(void, void *, (cb), (arg), int), (arg))
static void my_callback(int x, char *p) static void my_callback(int x, char *p)
{ {
...@@ -17,7 +17,7 @@ int main(int argc, char *argv[]) ...@@ -17,7 +17,7 @@ int main(int argc, char *argv[])
#ifdef FAIL #ifdef FAIL
int *p; int *p;
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P #if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if cast_if_type is a noop." #error "Unfortunately we don't fail if typesafe_cb_cast is a noop."
#endif #endif
#else #else
char *p; char *p;
......
#include <ccan/typesafe_cb/typesafe_cb.h> #include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h> #include <stdlib.h>
/* NULL args for callback function should be OK for _exact and _def. */ /* NULL args for callback function should be OK for normal and _def. */
static void _register_callback(void (*cb)(const void *arg), const void *arg) static void _register_callback(void (*cb)(const void *arg), const void *arg)
{ {
} }
#define register_callback_def(cb, arg) \ #define register_callback(cb, arg) \
_register_callback(typesafe_cb_def(void, (cb), (arg)), (arg)) _register_callback(typesafe_cb(void, const void *, (cb), (arg)), (arg))
#define register_callback_exact(cb, arg) \
_register_callback(typesafe_cb_exact(void, (cb), (arg)), (arg))
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
register_callback_def(NULL, "hello world"); register_callback(NULL, "hello world");
register_callback_exact(NULL, "hello world");
return 0; return 0;
} }
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h>
/* const args in callbacks should be OK. */
static void _register_callback(void (*cb)(void *arg), void *arg)
{
}
#define register_callback(cb, arg) \
_register_callback(typesafe_cb(void, (cb), (arg)), (arg))
#define register_callback_def(cb, arg) \
_register_callback(typesafe_cb_def(void, (cb), (arg)), (arg))
static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg)
{
}
#define register_callback_pre(cb, arg) \
_register_callback_pre(typesafe_cb_preargs(void, (cb), (arg), int), (arg))
static void _register_callback_post(void (*cb)(void *arg, int x), void *arg)
{
}
#define register_callback_post(cb, arg) \
_register_callback_post(typesafe_cb_postargs(void, (cb), (arg), int), (arg))
static void my_callback(const char *p)
{
}
static void my_callback_pre(int x, /*const*/ char *p)
{
}
static void my_callback_post(/*const*/ char *p, int x)
{
}
int main(int argc, char *argv[])
{
char p[] = "hello world";
register_callback(my_callback, p);
register_callback_def(my_callback, p);
register_callback_pre(my_callback_pre, p);
register_callback_post(my_callback_post, p);
return 0;
}
...@@ -8,25 +8,25 @@ static void _register_callback(void (*cb)(void *arg), void *arg) ...@@ -8,25 +8,25 @@ static void _register_callback(void (*cb)(void *arg), void *arg)
} }
#define register_callback(cb, arg) \ #define register_callback(cb, arg) \
_register_callback(typesafe_cb(void, (cb), (arg)), (arg)) _register_callback(typesafe_cb(void, void *, (cb), (arg)), (arg))
static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg) static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg)
{ {
} }
#define register_callback_pre(cb, arg) \ #define register_callback_pre(cb, arg) \
_register_callback_pre(typesafe_cb_preargs(void, (cb), (arg), int), (arg)) _register_callback_pre(typesafe_cb_preargs(void, void *, (cb), (arg), int), (arg))
static void _register_callback_post(void (*cb)(void *arg, int x), void *arg) static void _register_callback_post(void (*cb)(void *arg, int x), void *arg)
{ {
} }
#define register_callback_post(cb, arg) \ #define register_callback_post(cb, arg) \
_register_callback_post(typesafe_cb_postargs(void, (cb), (arg), int), (arg)) _register_callback_post(typesafe_cb_postargs(void, void *, (cb), (arg), int), (arg))
struct undefined; struct undefined;
static void my_callback(const struct undefined *undef) static void my_callback(struct undefined *undef)
{ {
} }
......
...@@ -8,25 +8,25 @@ static void _register_callback(void (*cb)(void *arg), void *arg) ...@@ -8,25 +8,25 @@ static void _register_callback(void (*cb)(void *arg), void *arg)
} }
#define register_callback(cb, arg) \ #define register_callback(cb, arg) \
_register_callback(typesafe_cb(void, (cb), (arg)), (arg)) _register_callback(typesafe_cb(void, void *, (cb), (arg)), (arg))
static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg) static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg)
{ {
} }
#define register_callback_pre(cb, arg) \ #define register_callback_pre(cb, arg) \
_register_callback_pre(typesafe_cb_preargs(void, (cb), (arg), int), (arg)) _register_callback_pre(typesafe_cb_preargs(void, void *, (cb), (arg), int), (arg))
static void _register_callback_post(void (*cb)(void *arg, int x), void *arg) static void _register_callback_post(void (*cb)(void *arg, int x), void *arg)
{ {
} }
#define register_callback_post(cb, arg) \ #define register_callback_post(cb, arg) \
_register_callback_post(typesafe_cb_postargs(void, (cb), (arg), int), (arg)) _register_callback_post(typesafe_cb_postargs(void, void *, (cb), (arg), int), (arg))
struct undefined; struct undefined;
static void my_callback(const struct undefined *undef) static void my_callback(struct undefined *undef)
{ {
} }
...@@ -41,7 +41,7 @@ static void my_callback_post(struct undefined *undef, int x) ...@@ -41,7 +41,7 @@ static void my_callback_post(struct undefined *undef, int x)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct undefined *handle = NULL; struct undefined *handle = NULL;
void (*cb)(const struct undefined *undef) = my_callback; void (*cb)(struct undefined *undef) = my_callback;
void (*pre)(int x, struct undefined *undef) = my_callback_pre; void (*pre)(int x, struct undefined *undef) = my_callback_pre;
void (*post)(struct undefined *undef, int x) = my_callback_post; void (*post)(struct undefined *undef, int x) = my_callback_post;
......
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h>
/* volatile args in callbacks should be OK. */
static void _register_callback(void (*cb)(void *arg), void *arg)
{
}
#define register_callback(cb, arg) \
_register_callback(typesafe_cb(void, (cb), (arg)), (arg))
static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg)
{
}
#define register_callback_pre(cb, arg) \
_register_callback_pre(typesafe_cb_preargs(void, (cb), (arg), int), (arg))
static void _register_callback_post(void (*cb)(void *arg, int x), void *arg)
{
}
#define register_callback_post(cb, arg) \
_register_callback_post(typesafe_cb_postargs(void, (cb), (arg), int), (arg))
static void my_callback(volatile char *p)
{
}
/* FIXME: Can't handle volatile for these */
static void my_callback_pre(int x, /* volatile */ char *p)
{
}
static void my_callback_post(/* volatile */ char *p, int x)
{
}
int main(int argc, char *argv[])
{
char p[] = "hello world";
register_callback(my_callback, p);
register_callback_pre(my_callback_pre, p);
register_callback_post(my_callback_post, p);
return 0;
}
...@@ -23,19 +23,19 @@ static void take_any(struct any *any) ...@@ -23,19 +23,19 @@ static void take_any(struct any *any)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#if HAVE_TYPEOF
/* Otherwise we get unused warnings for these. */ /* Otherwise we get unused warnings for these. */
struct foo *foo = NULL; struct foo *foo = NULL;
struct bar *bar = NULL; struct bar *bar = NULL;
struct baz *baz = NULL; struct baz *baz = NULL;
#endif
struct other *arg = NULL;
take_any(cast_if_any(struct any *, arg, foo, take_any(typesafe_cb_cast3(struct any *,
struct foo *, struct bar *, struct baz *)); struct foo *, struct bar *, struct baz *,
take_any(cast_if_any(struct any *, arg, bar, foo));
struct foo *, struct bar *, struct baz *)); take_any(typesafe_cb_cast3(struct any *,
take_any(cast_if_any(struct any *, arg, baz, struct foo *, struct bar *, struct baz *,
struct foo *, struct bar *, struct baz *)); bar));
take_any(typesafe_cb_cast3(struct any *,
struct foo *, struct bar *, struct baz *,
baz));
return 0; return 0;
} }
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h>
/* const args in callbacks should be OK. */
static void _register_callback(void (*cb)(void *arg), void *arg)
{
}
#define register_callback(cb, arg) \
_register_callback(typesafe_cb(void, (cb), (arg)), (arg))
static void _register_callback_pre(void (*cb)(int x, void *arg), void *arg)
{
}
#define register_callback_pre(cb, arg) \
_register_callback_pre(typesafe_cb_preargs(void, (cb), (arg), int), (arg))
static void _register_callback_post(void (*cb)(void *arg, int x), void *arg)
{
}
#define register_callback_post(cb, arg) \
_register_callback_post(typesafe_cb_postargs(void, (cb), (arg), int), (arg))
static void my_callback(const char *p)
{
}
static void my_callback_pre(int x, /*const*/ char *p)
{
}
static void my_callback_post(/*const*/ char *p, int x)
{
}
int main(int argc, char *argv[])
{
char p[] = "hello world";
register_callback(my_callback, p);
register_callback_pre(my_callback_pre, p);
register_callback_post(my_callback_post, p);
return 0;
}
...@@ -12,7 +12,7 @@ static void _set_some_value(void *val) ...@@ -12,7 +12,7 @@ static void _set_some_value(void *val)
} }
#define set_some_value(expr) \ #define set_some_value(expr) \
_set_some_value(cast_if_type(void *, (expr), (expr), unsigned long)) _set_some_value(typesafe_cb_cast(void *, unsigned long, (expr)))
static void _callback_onearg(void (*fn)(void *arg), void *arg) static void _callback_onearg(void (*fn)(void *arg), void *arg)
{ {
...@@ -30,30 +30,19 @@ static void _callback_postargs(void (*fn)(void *arg, int a, int b), void *arg) ...@@ -30,30 +30,19 @@ static void _callback_postargs(void (*fn)(void *arg, int a, int b), void *arg)
} }
#define callback_onearg(cb, arg) \ #define callback_onearg(cb, arg) \
_callback_onearg(typesafe_cb(void, (cb), (arg)), (arg)) _callback_onearg(typesafe_cb(void, void *, (cb), (arg)), (arg))
#define callback_preargs(cb, arg) \ #define callback_preargs(cb, arg) \
_callback_preargs(typesafe_cb_preargs(void, (cb), (arg), int, int), (arg)) _callback_preargs(typesafe_cb_preargs(void, void *, (cb), (arg), int, int), (arg))
#define callback_postargs(cb, arg) \ #define callback_postargs(cb, arg) \
_callback_postargs(typesafe_cb_postargs(void, (cb), (arg), int, int), (arg)) _callback_postargs(typesafe_cb_postargs(void, void *, (cb), (arg), int, int), (arg))
static void my_callback_onearg(char *p) static void my_callback_onearg(char *p)
{ {
ok1(strcmp(p, "hello world") == 0); ok1(strcmp(p, "hello world") == 0);
} }
static void my_callback_onearg_const(const char *p)
{
ok1(strcmp(p, "hello world") == 0);
}
static void my_callback_onearg_volatile(volatile char *p)
{
/* Double cast avoids warning on gcc's -Wcast-qual */
ok1(strcmp((char *)(intptr_t)p, "hello world") == 0);
}
static void my_callback_preargs(int a, int b, char *p) static void my_callback_preargs(int a, int b, char *p)
{ {
ok1(a == 1); ok1(a == 1);
...@@ -61,22 +50,6 @@ static void my_callback_preargs(int a, int b, char *p) ...@@ -61,22 +50,6 @@ static void my_callback_preargs(int a, int b, char *p)
ok1(strcmp(p, "hello world") == 0); ok1(strcmp(p, "hello world") == 0);
} }
#if 0 /* FIXME */
static void my_callback_preargs_const(int a, int b, const char *p)
{
ok1(a == 1);
ok1(b == 2);
ok1(strcmp(p, "hello world") == 0);
}
static void my_callback_preargs_volatile(int a, int b, volatile char *p)
{
ok1(a == 1);
ok1(b == 2);
ok1(strcmp((char *)p, "hello world") == 0);
}
#endif
static void my_callback_postargs(char *p, int a, int b) static void my_callback_postargs(char *p, int a, int b)
{ {
ok1(a == 1); ok1(a == 1);
...@@ -84,23 +57,7 @@ static void my_callback_postargs(char *p, int a, int b) ...@@ -84,23 +57,7 @@ static void my_callback_postargs(char *p, int a, int b)
ok1(strcmp(p, "hello world") == 0); ok1(strcmp(p, "hello world") == 0);
} }
#if 0 /* FIXME */ /* This is simply a compile test; we promised typesafe_cb_cast can be in a
static void my_callback_postargs_const(const char *p, int a, int b)
{
ok1(a == 1);
ok1(b == 2);
ok1(strcmp(p, "hello world") == 0);
}
static void my_callback_postargs_volatile(volatile char *p, int a, int b)
{
ok1(a == 1);
ok1(b == 2);
ok1(strcmp((char *)p, "hello world") == 0);
}
#endif
/* This is simply a compile test; we promised cast_if_type can be in a
* static initializer. */ * static initializer. */
struct callback_onearg struct callback_onearg
{ {
...@@ -109,7 +66,7 @@ struct callback_onearg ...@@ -109,7 +66,7 @@ struct callback_onearg
}; };
struct callback_onearg cb_onearg struct callback_onearg cb_onearg
= { typesafe_cb(void, my_callback_onearg, (char *)(intptr_t)"hello world"), = { typesafe_cb(void, void *, my_callback_onearg, (char *)(intptr_t)"hello world"),
"hello world" }; "hello world" };
struct callback_preargs struct callback_preargs
...@@ -119,7 +76,7 @@ struct callback_preargs ...@@ -119,7 +76,7 @@ struct callback_preargs
}; };
struct callback_preargs cb_preargs struct callback_preargs cb_preargs
= { typesafe_cb_preargs(void, my_callback_preargs, = { typesafe_cb_preargs(void, void *, my_callback_preargs,
(char *)(intptr_t)"hi", int, int), "hi" }; (char *)(intptr_t)"hi", int, int), "hi" };
struct callback_postargs struct callback_postargs
...@@ -129,7 +86,7 @@ struct callback_postargs ...@@ -129,7 +86,7 @@ struct callback_postargs
}; };
struct callback_postargs cb_postargs struct callback_postargs cb_postargs
= { typesafe_cb_postargs(void, my_callback_postargs, = { typesafe_cb_postargs(void, void *, my_callback_postargs,
(char *)(intptr_t)"hi", int, int), "hi" }; (char *)(intptr_t)"hi", int, int), "hi" };
int main(int argc, char *argv[]) int main(int argc, char *argv[])
...@@ -138,25 +95,15 @@ int main(int argc, char *argv[]) ...@@ -138,25 +95,15 @@ int main(int argc, char *argv[])
unsigned long l = (unsigned long)p; unsigned long l = (unsigned long)p;
char str[] = "hello world"; char str[] = "hello world";
plan_tests(2 + 3 + 3 + 3); plan_tests(2 + 1 + 3 + 3);
set_some_value(p); set_some_value(p);
set_some_value(l); set_some_value(l);
callback_onearg(my_callback_onearg, str); callback_onearg(my_callback_onearg, str);
callback_onearg(my_callback_onearg_const, str);
callback_onearg(my_callback_onearg_volatile, str);
callback_preargs(my_callback_preargs, str); callback_preargs(my_callback_preargs, str);
#if 0 /* FIXME */
callback_preargs(my_callback_preargs_const, str);
callback_preargs(my_callback_preargs_volatile, str);
#endif
callback_postargs(my_callback_postargs, str); callback_postargs(my_callback_postargs, str);
#if 0 /* FIXME */
callback_postargs(my_callback_postargs_const, str);
callback_postargs(my_callback_postargs_volatile, str);
#endif
return exit_status(); return exit_status();
} }
#ifndef CCAN_CAST_IF_TYPE_H #ifndef CCAN_TYPESAFE_CB_H
#define CCAN_CAST_IF_TYPE_H #define CCAN_TYPESAFE_CB_H
#include "config.h" #include "config.h"
#if HAVE_TYPEOF && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P #if HAVE_TYPEOF && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P
/** /**
* cast_if_type - only cast an expression if test matches a given type * typesafe_cb_cast - only cast an expression if it matches a given type
* @desttype: the type to cast to * @desttype: the type to cast to
* @expr: the expression to cast
* @test: the expression to test
* @oktype: the type we allow * @oktype: the type we allow
* @expr: the expression to cast
* *
* This macro is used to create functions which allow multiple types. * This macro is used to create functions which allow multiple types.
* The result of this macro is used somewhere that a @desttype type is * The result of this macro is used somewhere that a @desttype type is
* expected: if @test is exactly of type @oktype, then @expr will be * expected: if @expr is exactly of type @oktype, then it will be
* cast to @desttype type, otherwise left alone. * cast to @desttype type, otherwise left alone.
* *
* This macro can be used in static initializers. * This macro can be used in static initializers.
* *
* This is merely useful for warnings: if the compiler does not * This is merely useful for warnings: if the compiler does not
* support the primitives required for cast_if_type(), it becomes an * support the primitives required for typesafe_cb_cast(), it becomes an
* unconditional cast, and the @test and @oktype argument is not used. In * unconditional cast, and the @oktype argument is not used. In
* particular, this means that @oktype can be a type which uses * particular, this means that @oktype can be a type which uses the
* the "typeof": it will not be evaluated if typeof is not supported. * "typeof": it will not be evaluated if typeof is not supported.
* *
* Example: * Example:
* // We can take either an unsigned long or a void *. * // We can take either an unsigned long or a void *.
* void _set_some_value(void *val); * void _set_some_value(void *val);
* #define set_some_value(e) \ * #define set_some_value(e) \
* _set_some_value(cast_if_type(void *, (e), (e), unsigned long)) * _set_some_value(typesafe_cb_cast(void *, (e), unsigned long))
*/ */
#define cast_if_type(desttype, expr, test, oktype) \ #define typesafe_cb_cast(desttype, oktype, expr) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(test), oktype), \ __builtin_choose_expr( \
(desttype)(expr), (expr)) __builtin_types_compatible_p(__typeof__(0?(expr):(expr)), \
oktype), \
(desttype)(expr), (expr))
#else #else
#define cast_if_type(desttype, expr, test, oktype) ((desttype)(expr)) #define typesafe_cb_cast(desttype, oktype, expr) ((desttype)(expr))
#endif #endif
/** /**
* cast_if_any - only cast an expression if it is one of the three given types * typesafe_cb_cast3 - only cast an expression if it matches given types
* @desttype: the type to cast to * @desttype: the type to cast to
* @expr: the expression to cast
* @test: the expression to test
* @ok1: the first type we allow * @ok1: the first type we allow
* @ok2: the second type we allow * @ok2: the second type we allow
* @ok3: the third type we allow * @ok3: the third type we allow
* @expr: the expression to cast
* *
* This is a convenient wrapper for multiple cast_if_type() calls. You can * This is a convenient wrapper for multiple typesafe_cb_cast() calls.
* chain them inside each other (ie. use cast_if_any() for expr) if you need * You can chain them inside each other (ie. use typesafe_cb_cast()
* more than 3 arguments. * for expr) if you need more than 3 arguments.
* *
* Example: * Example:
* // We can take either a long, unsigned long, void * or a const void *. * // We can take either a long, unsigned long, void * or a const void *.
* void _set_some_value(void *val); * void _set_some_value(void *val);
* #define set_some_value(expr) \ * #define set_some_value(expr) \
* _set_some_value(cast_if_any(void *, (expr), (expr), \ * _set_some_value(typesafe_cb_cast3(void *,, \
* long, unsigned long, const void *)) * long, unsigned long, const void *,\
* (expr)))
*/ */
#define cast_if_any(desttype, expr, test, ok1, ok2, ok3) \ #define typesafe_cb_cast3(desttype, ok1, ok2, ok3, expr) \
cast_if_type(desttype, \ typesafe_cb_cast(desttype, ok1, \
cast_if_type(desttype, \ typesafe_cb_cast(desttype, ok2, \
cast_if_type(desttype, (expr), (test), ok1), \ typesafe_cb_cast(desttype, ok3, \
(test), \ (expr))))
ok2), \
(test), \
ok3)
/** /**
* typesafe_cb - cast a callback function if it matches the arg * typesafe_cb - cast a callback function if it matches the arg
* @rtype: the return type of the callback function * @rtype: the return type of the callback function
* @atype: the (pointer) type which the callback function expects.
* @fn: the callback function to cast * @fn: the callback function to cast
* @arg: the (pointer) argument to hand to the callback function. * @arg: the (pointer) argument to hand to the callback function.
* *
* If a callback function takes a single argument, this macro does * If a callback function takes a single argument, this macro does
* appropriate casts to a function which takes a single void * argument if the * appropriate casts to a function which takes a single atype argument if the
* callback provided matches the @arg (or a const or volatile version).
*
* It is assumed that @arg is of pointer type: usually @arg is passed
* or assigned to a void * elsewhere anyway.
*
* This will not work with a NULL @fn argument: see typesafe_cb_def or
* typesafe_cb_exact.
*
* Example:
* void _register_callback(void (*fn)(void *arg), void *arg);
* #define register_callback(fn, arg) \
* _register_callback(typesafe_cb(void, (fn), (arg)), (arg))
*/
#define typesafe_cb(rtype, fn, arg) \
cast_if_type(rtype (*)(void *), (fn), (fn)(arg), rtype)
/**
* typesafe_cb_def - cast a callback fn if it matches arg (of defined type)
* @rtype: the return type of the callback function
* @fn: the callback function to cast
* @arg: the (pointer) argument to hand to the callback function.
*
* This is typesafe_cb(), except the type must be defined (eg. if it's
* struct foo *, the definition of struct foo must be visible). For many
* applications, this is reasonable.
*
* This variant can accept @fn equal to NULL.
*
* Example:
* void _register_callback(void (*fn)(void *arg), void *arg);
* #define register_callback(fn, arg) \
* _register_callback(typesafe_cb_def(void, (fn), (arg)), (arg))
*/
#define typesafe_cb_def(rtype, fn, arg) \
cast_if_any(rtype (*)(void *), (fn), 0?(fn):(fn), \
rtype (*)(typeof(*arg)*), \
rtype (*)(const typeof(*arg)*), \
rtype (*)(volatile typeof(*arg)*))
/**
* typesafe_cb_exact - cast a callback fn if it exactly matches arg
* @rtype: the return type of the callback function
* @fn: the callback function to cast
* @arg: the (pointer) argument to hand to the callback function.
*
* This is typesafe_cb(), except the @fn can be NULL, or must exactly match
* the @arg type (no const or volatile).
*
* Example:
* void _register_callback(void (*fn)(void *arg), void *arg);
* #define register_callback(fn, arg) \
* _register_callback(typesafe_cb_exact(void, (fn), (arg)), (arg))
*/
#define typesafe_cb_exact(rtype, fn, arg) \
cast_if_type(rtype (*)(void *), (fn), 0?(fn):(fn), \
rtype (*)(typeof(arg)))
/**
* typesafe_cb_const - cast a const callback function if it matches the arg
* @rtype: the return type of the callback function
* @fn: the callback function to cast
* @arg: the (pointer) argument to hand to the callback function.
*
* If a callback function takes a single argument, this macro does appropriate
* casts to a function which takes a single const void * argument if the
* callback provided matches the @arg. * callback provided matches the @arg.
* *
* It is assumed that @arg is of pointer type: usually @arg is passed * It is assumed that @arg is of pointer type: usually @arg is passed
* or assigned to a void * elsewhere anyway. * or assigned to a void * elsewhere anyway.
* *
* Example: * Example:
* void _register_callback(void (*fn)(const void *arg), const void *arg); * void _register_callback(void (*fn)(void *arg), void *arg);
* #define register_callback(fn, arg) \ * #define register_callback(fn, arg) \
* _register_callback(typesafe_cb_const(void, (fn), (arg)), (arg)) * _register_callback(typesafe_cb(void, (fn), void*, (arg)), (arg))
*/ */
#define typesafe_cb_const(rtype, fn, arg) \ #define typesafe_cb(rtype, atype, fn, arg) \
cast_if_type(rtype (*)(const void *), (fn), (fn)(arg), rtype) typesafe_cb_cast(rtype (*)(atype), \
rtype (*)(__typeof__(arg)), \
(fn))
/** /**
* typesafe_cb_preargs - cast a callback function if it matches the arg * typesafe_cb_preargs - cast a callback function if it matches the arg
* @rtype: the return type of the callback function * @rtype: the return type of the callback function
* @atype: the (pointer) type which the callback function expects.
* @fn: the callback function to cast * @fn: the callback function to cast
* @arg: the (pointer) argument to hand to the callback function. * @arg: the (pointer) argument to hand to the callback function.
* *
...@@ -162,17 +99,20 @@ ...@@ -162,17 +99,20 @@
* *
* Example: * Example:
* void _register_callback(void (*fn)(int, void *arg), void *arg); * void _register_callback(void (*fn)(int, void *arg), void *arg);
* #define register_callback(fn, arg) \ * #define register_callback(fn, arg) \
* _register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\ * _register_callback(typesafe_cb_preargs(void, (fn), void *, \
* (arg), int), \
* (arg)) * (arg))
*/ */
#define typesafe_cb_preargs(rtype, fn, arg, ...) \ #define typesafe_cb_preargs(rtype, atype, fn, arg, ...) \
cast_if_type(rtype (*)(__VA_ARGS__, void *), (fn), 0?(fn):(fn), \ typesafe_cb_cast(rtype (*)(__VA_ARGS__, atype), \
rtype (*)(__VA_ARGS__, typeof(arg))) rtype (*)(__VA_ARGS__, __typeof__(arg)), \
(fn))
/** /**
* typesafe_cb_postargs - cast a callback function if it matches the arg * typesafe_cb_postargs - cast a callback function if it matches the arg
* @rtype: the return type of the callback function * @rtype: the return type of the callback function
* @atype: the (pointer) type which the callback function expects.
* @fn: the callback function to cast * @fn: the callback function to cast
* @arg: the (pointer) argument to hand to the callback function. * @arg: the (pointer) argument to hand to the callback function.
* *
...@@ -182,37 +122,12 @@ ...@@ -182,37 +122,12 @@
* Example: * Example:
* void _register_callback(void (*fn)(void *arg, int), void *arg); * void _register_callback(void (*fn)(void *arg, int), void *arg);
* #define register_callback(fn, arg) \ * #define register_callback(fn, arg) \
* _register_callback(typesafe_cb_postargs(void, (fn), (arg), int),\ * _register_callback(typesafe_cb_postargs(void, (fn), void *, \
* (arg), int), \
* (arg)) * (arg))
*/ */
#define typesafe_cb_postargs(rtype, fn, arg, ...) \ #define typesafe_cb_postargs(rtype, atype, fn, arg, ...) \
cast_if_type(rtype (*)(void *, __VA_ARGS__), (fn), 0?(fn):(fn), \ typesafe_cb_cast(rtype (*)(atype, __VA_ARGS__), \
rtype (*)(typeof(arg), __VA_ARGS__)) rtype (*)(__typeof__(arg), __VA_ARGS__), \
(fn))
/**
* typesafe_cb_cmp - cast a compare function if it matches the arg
* @rtype: the return type of the callback function
* @fn: the callback function to cast
* @arg: the (pointer) argument(s) to hand to the compare function.
*
* If a callback function takes two matching-type arguments, this macro does
* appropriate casts to a function which takes two const void * arguments if
* the callback provided takes two a const pointers to @arg.
*
* It is assumed that @arg is of pointer type: usually @arg is passed
* or assigned to a void * elsewhere anyway. Note also that the type
* arg points to must be defined.
*
* Example:
* void _my_qsort(void *base, size_t nmemb, size_t size,
* int (*cmp)(const void *, const void *));
* #define my_qsort(base, nmemb, cmpfn) \
* _my_qsort((base), (nmemb), sizeof(*(base)), \
* typesafe_cb_cmp(int, (cmpfn), (base)), (arg))
*/
#define typesafe_cb_cmp(rtype, cmpfn, arg) \
cast_if_type(rtype (*)(const void *, const void *), \
(cmpfn), 0?(cmpfn):(cmpfn), \
rtype (*)(const typeof(*arg)*, const typeof(*arg)*))
#endif /* CCAN_CAST_IF_TYPE_H */ #endif /* CCAN_CAST_IF_TYPE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment