summaryrefslogtreecommitdiff
path: root/ctdb/include/ctdb_typesafe_cb.h
blob: b1f2c5f5b5e95f11f91a080da485f726575ecf93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#ifndef CCAN_CAST_IF_TYPE_H
#define CCAN_CAST_IF_TYPE_H

#if (__GNUC__ >= 3)
#define HAVE_TYPEOF 1
#define HAVE_BUILTIN_CHOOSE_EXPR 1
#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
#endif

#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
 * @desttype: the type to cast to
 * @expr: the expression to cast
 * @test: the expression to test
 * @oktype: the type we allow
 *
 * This macro is used to create functions which allow multiple types.
 * The result of this macro is used somewhere that a @desttype type is
 * expected: if @expr was of type @oktype, it will be cast to
 * @desttype type.  As a result, if @expr is any type other than
 * @oktype or @desttype, a compiler warning will be issued.
 *
 * This macro can be used in static initializers.
 *
 * This is merely useful for warnings: if the compiler does not
 * support the primitives required for cast_if_type(), it becomes an
 * unconditional cast, and the @test and @oktype argument is not used.  In
 * particular, this means that @oktype can be a type which uses
 * the "typeof": it will not be evaluated if typeof is not supported.
 *
 * Example:
 *	// We can take either an unsigned long or a void *.
 *	void _set_some_value(void *val);
 *	#define set_some_value(e)			\
 *		_set_some_value(cast_if_type(void *, (e), (e), unsigned long))
 */
#define cast_if_type(desttype, expr, test, oktype)			\
__builtin_choose_expr(__builtin_types_compatible_p(typeof(1?(test):0), oktype), \
			(desttype)(expr), (expr))
#else
#define cast_if_type(desttype, expr, test, oktype) ((desttype)(expr))
#endif

/**
 * cast_if_any - only cast an expression if it is one of the three given types
 * @desttype: the type to cast to
 * @expr: the expression to cast
 * @test: the expression to test
 * @ok1: the first type we allow
 * @ok2: the second type we allow
 * @ok3: the third type we allow
 *
 * This is a convenient wrapper for multiple cast_if_type() calls.  You can
 * chain them inside each other (ie. use cast_if_any() for expr) if you need
 * more than 3 arguments.
 *
 * Example:
 *	// We can take either a long, unsigned long, void * or a const void *.
 *	void _set_some_value(void *val);
 *	#define set_some_value(expr)					\
 *		_set_some_value(cast_if_any(void *, (expr), (expr),	\
 *					    long, unsigned long, const void *))
 */
#define cast_if_any(desttype, expr, test, ok1, ok2, ok3)		\
	cast_if_type(desttype,						\
		     cast_if_type(desttype,				\
				  cast_if_type(desttype, (expr), (test), ok1), \
				  ok2),					\
		     ok3)

/**
 * typesafe_cb - cast a 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 void * 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.
 *
 * 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_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.
 *
 * It is assumed that @arg is of pointer type: usually @arg is passed
 * or assigned to a void * elsewhere anyway.
 *
 * Example:
 *	void _register_callback(void (*fn)(const void *arg), const void *arg);
 *	#define register_callback(fn, arg) \
 *		_register_callback(typesafe_cb_const(void, (fn), (arg)), (arg))
 */
#define typesafe_cb_const(rtype, fn, arg)				\
	sizeof((fn)((const void *)0)),					\
		cast_if_type(rtype (*)(const void *),			\
			     (fn), (fn)(arg), rtype (*)(typeof(arg)))

/**
 * typesafe_cb_preargs - cast a 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.
 *
 * This is a version of typesafe_cb() for callbacks that take other arguments
 * before the @arg.
 *
 * Example:
 *	void _register_callback(void (*fn)(int, void *arg), void *arg);
 *	#define register_callback(fn, arg) \
 *		_register_callback(typesafe_cb_preargs(void, (fn), (arg), int),\
 *				   (arg))
 */
#define typesafe_cb_preargs(rtype, fn, arg, ...)			\
	cast_if_type(rtype (*)(__VA_ARGS__, void *), (fn), (fn),	\
		     rtype (*)(__VA_ARGS__, typeof(arg)))
/**
 * typesafe_cb_postargs - cast a 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.
 *
 * This is a version of typesafe_cb() for callbacks that take other arguments
 * after the @arg.
 *
 * Example:
 *	void _register_callback(void (*fn)(void *arg, int), void *arg);
 *	#define register_callback(fn, arg) \
 *		_register_callback(typesafe_cb_postargs(void, (fn), (arg), int),\
 *				   (arg))
 */
#define typesafe_cb_postargs(rtype, fn, arg, ...)			\
	cast_if_type(rtype (*)(void *, __VA_ARGS__), (fn), (fn),	\
		     rtype (*)(typeof(arg), __VA_ARGS__))
/**
 * 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),	\
		     rtype (*)(const typeof(*arg)*, const typeof(*arg)*))
		     
#endif /* CCAN_CAST_IF_TYPE_H */