Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
ccan
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
mirror
ccan
Commits
eded23d4
Commit
eded23d4
authored
Feb 28, 2008
by
Rusty Russell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Typesafe callback infrastructure.
parent
9927eebe
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
476 additions
and
0 deletions
+476
-0
typesafe_cb/_info.c
typesafe_cb/_info.c
+59
-0
typesafe_cb/test/compile_fail-cast_if_type-callback-int.c
typesafe_cb/test/compile_fail-cast_if_type-callback-int.c
+27
-0
typesafe_cb/test/compile_fail-cast_if_type-callback.c
typesafe_cb/test/compile_fail-cast_if_type-callback.c
+26
-0
typesafe_cb/test/compile_fail-cast_if_type.c
typesafe_cb/test/compile_fail-cast_if_type.c
+22
-0
typesafe_cb/test/compile_fail-typesafe_cb.c
typesafe_cb/test/compile_fail-typesafe_cb.c
+25
-0
typesafe_cb/test/compile_fail-typesafe_cb_postargs.c
typesafe_cb/test/compile_fail-typesafe_cb_postargs.c
+24
-0
typesafe_cb/test/compile_fail-typesafe_cb_preargs.c
typesafe_cb/test/compile_fail-typesafe_cb_preargs.c
+25
-0
typesafe_cb/test/run.c
typesafe_cb/test/run.c
+148
-0
typesafe_cb/typesafe_cb.h
typesafe_cb/typesafe_cb.h
+120
-0
No files found.
typesafe_cb/_info.c
0 → 100644
View file @
eded23d4
#include <stdio.h>
#include <string.h>
#include "config.h"
/**
* typesafe_cb - macros for safe callbacks.
*
* The basis of the typesafe_cb header is cast_if_type(): a
* conditional cast macro. If an expression exactly matches a given
* type, it is cast to the target type, otherwise it is left alone.
*
* This allows us to create functions which take a small number of
* specific types, rather than being forced to use a void *. In
* particular, it is useful for creating typesafe callbacks as the
* helpers typesafe_cb(), typesafe_cb_preargs() and
* typesafe_cb_postargs() demonstrate.
*
* The standard way of passing arguments to callback functions in C is
* to use a void pointer, which the callback then casts back to the
* expected type. This unfortunately subverts the type checking the
* compiler would perform if it were a direct call. Here's an example:
*
* static void my_callback(void *_obj)
* {
* struct obj *obj = _obj;
* ...
* }
* ...
* register_callback(my_callback, &my_obj);
*
* If we wanted to use the natural type for my_callback (ie. "void
* my_callback(struct obj *obj)"), we could make register_callback()
* take a void * as its first argument, but this would subvert all
* type checking. We really want register_callback() to accept only
* the exactly correct function type to match the argument, or a
* function which takes a void *.
*
* This is where typesafe_cb() comes in: it uses cast_if_type() to
* cast the callback function if it matches the argument type:
*
* void _register_callback(void (*cb)(void *arg), void *arg);
* #define register_callback(cb, arg) \
* _register_callback(typesafe_cb(void, (cb), (arg)), (arg))
*
* On compilers which don't support the extensions required
* cast_if_type() and friend become an unconditional cast, so your
* code will compile but you won't get type checking.
*/
int
main
(
int
argc
,
char
*
argv
[])
{
if
(
argc
!=
2
)
return
1
;
if
(
strcmp
(
argv
[
1
],
"depends"
)
==
0
)
{
return
0
;
}
return
1
;
}
typesafe_cb/test/compile_fail-cast_if_type-callback-int.c
0 → 100644
View file @
eded23d4
#include "typesafe_cb/typesafe_cb.h"
#include <stdlib.h>
void
_callback
(
void
(
*
fn
)(
void
*
arg
),
void
*
arg
);
void
_callback
(
void
(
*
fn
)(
void
*
arg
),
void
*
arg
)
{
fn
(
arg
);
}
/* Callback is set up to warn if arg isn't a pointer (since it won't
* pass cleanly to _callback's second arg. */
#define callback(fn, arg) \
_callback(cast_if_type((fn), void (*)(typeof(arg)), void (*)(void *)), \
arg)
void
my_callback
(
int
something
);
void
my_callback
(
int
something
)
{
}
int
main
(
int
argc
,
char
*
argv
[])
{
#ifdef FAIL
callback
(
my_callback
,
100
);
#endif
return
0
;
}
typesafe_cb/test/compile_fail-cast_if_type-callback.c
0 → 100644
View file @
eded23d4
#include "typesafe_cb/typesafe_cb.h"
#include <stdlib.h>
static
void
_callback
(
void
(
*
fn
)(
void
*
arg
),
void
*
arg
)
{
fn
(
arg
);
}
#define callback(fn, arg) \
_callback(cast_if_type((fn), void (*)(typeof(arg)), void (*)(void *)), \
arg)
static
void
my_callback
(
char
*
p
)
{
}
int
main
(
int
argc
,
char
*
argv
[])
{
callback
(
my_callback
,
"hello world"
);
#ifdef FAIL
/* Must be a char * */
callback
(
my_callback
,
my_callback
);
#endif
return
0
;
}
typesafe_cb/test/compile_fail-cast_if_type.c
0 → 100644
View file @
eded23d4
#include "typesafe_cb/typesafe_cb.h"
void
_set_some_value
(
void
*
val
);
void
_set_some_value
(
void
*
val
)
{
}
#define set_some_value(expr) \
_set_some_value(cast_if_type((expr), unsigned long, void *))
int
main
(
int
argc
,
char
*
argv
[])
{
#ifdef FAIL
int
x
=
0
;
set_some_value
(
x
);
#else
void
*
p
=
0
;
set_some_value
(
p
);
#endif
return
0
;
}
typesafe_cb/test/compile_fail-typesafe_cb.c
0 → 100644
View file @
eded23d4
#include "typesafe_cb/typesafe_cb.h"
#include <stdlib.h>
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
my_callback
(
char
*
p
)
{
}
int
main
(
int
argc
,
char
*
argv
[])
{
#ifdef FAIL
int
*
p
;
#else
char
*
p
;
#endif
p
=
NULL
;
register_callback
(
my_callback
,
p
);
return
0
;
}
typesafe_cb/test/compile_fail-typesafe_cb_postargs.c
0 → 100644
View file @
eded23d4
#include "typesafe_cb/typesafe_cb.h"
#include <stdlib.h>
static
void
_register_callback
(
void
(
*
cb
)(
void
*
arg
,
int
x
),
void
*
arg
)
{
}
#define register_callback(cb, arg) \
_register_callback(typesafe_cb_postargs(void, (cb), (arg), int), (arg))
static
void
my_callback
(
char
*
p
,
int
x
)
{
}
int
main
(
int
argc
,
char
*
argv
[])
{
#ifdef FAIL
int
*
p
;
#else
char
*
p
;
#endif
p
=
NULL
;
register_callback
(
my_callback
,
p
);
return
0
;
}
typesafe_cb/test/compile_fail-typesafe_cb_preargs.c
0 → 100644
View file @
eded23d4
#include "typesafe_cb/typesafe_cb.h"
#include <stdlib.h>
static
void
_register_callback
(
void
(
*
cb
)(
int
x
,
void
*
arg
),
void
*
arg
)
{
}
#define register_callback(cb, arg) \
_register_callback(typesafe_cb_preargs(void, (cb), (arg), int), (arg))
static
void
my_callback
(
int
x
,
char
*
p
)
{
}
int
main
(
int
argc
,
char
*
argv
[])
{
#ifdef FAIL
int
*
p
;
#else
char
*
p
;
#endif
p
=
NULL
;
register_callback
(
my_callback
,
p
);
return
0
;
}
typesafe_cb/test/run.c
0 → 100644
View file @
eded23d4
#include "typesafe_cb/typesafe_cb.h"
#include <string.h>
#include "tap.h"
static
char
dummy
=
0
;
/* The example usage. */
static
void
_set_some_value
(
void
*
val
)
{
ok1
(
val
==
&
dummy
);
}
#define set_some_value(expr) \
_set_some_value(cast_if_type((expr), unsigned long, void *))
static
void
_callback_onearg
(
void
(
*
fn
)(
void
*
arg
),
void
*
arg
)
{
fn
(
arg
);
}
static
void
_callback_preargs
(
void
(
*
fn
)(
int
a
,
int
b
,
void
*
arg
),
void
*
arg
)
{
fn
(
1
,
2
,
arg
);
}
static
void
_callback_postargs
(
void
(
*
fn
)(
void
*
arg
,
int
a
,
int
b
),
void
*
arg
)
{
fn
(
arg
,
1
,
2
);
}
#define callback_onearg(cb, arg) \
_callback_onearg(typesafe_cb(void, (cb), (arg)), (arg))
#define callback_preargs(cb, arg) \
_callback_preargs(typesafe_cb_preargs(void, (cb), (arg), int, int), (arg))
#define callback_postargs(cb, arg) \
_callback_postargs(typesafe_cb_postargs(void, (cb), (arg), int, int), (arg))
static
void
my_callback_onearg
(
char
*
p
)
{
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
)
{
ok1
(
strcmp
((
char
*
)
p
,
"hello world"
)
==
0
);
}
static
void
my_callback_preargs
(
int
a
,
int
b
,
char
*
p
)
{
ok1
(
a
==
1
);
ok1
(
b
==
2
);
ok1
(
strcmp
(
p
,
"hello world"
)
==
0
);
}
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
);
}
static
void
my_callback_postargs
(
char
*
p
,
int
a
,
int
b
)
{
ok1
(
a
==
1
);
ok1
(
b
==
2
);
ok1
(
strcmp
(
p
,
"hello world"
)
==
0
);
}
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
);
}
/* This is simply a compile test; we promised cast_if_type can be in a
* static initializer. */
struct
callback_onearg
{
void
(
*
fn
)(
void
*
arg
);
void
*
arg
;
};
struct
callback_onearg
cb_onearg
=
{
typesafe_cb
(
void
,
my_callback_onearg
,
"hello world"
),
"hello world"
};
struct
callback_preargs
{
void
(
*
fn
)(
int
a
,
int
b
,
void
*
arg
);
void
*
arg
;
};
struct
callback_preargs
cb_preargs
=
{
typesafe_cb_preargs
(
void
,
my_callback_preargs
,
"hi"
,
int
,
int
),
"hi"
};
struct
callback_postargs
{
void
(
*
fn
)(
void
*
arg
,
int
a
,
int
b
);
void
*
arg
;
};
struct
callback_postargs
cb_postargs
=
{
typesafe_cb_postargs
(
void
,
my_callback_postargs
,
"hi"
,
int
,
int
),
"hi"
};
int
main
(
int
argc
,
char
*
argv
[])
{
void
*
p
=
&
dummy
;
unsigned
long
l
=
(
unsigned
long
)
p
;
plan_tests
(
2
+
3
+
9
+
9
);
set_some_value
(
p
);
set_some_value
(
l
);
callback_onearg
(
my_callback_onearg
,
"hello world"
);
callback_onearg
(
my_callback_onearg_const
,
"hello world"
);
callback_onearg
(
my_callback_onearg_volatile
,
"hello world"
);
callback_preargs
(
my_callback_preargs
,
"hello world"
);
callback_preargs
(
my_callback_preargs_const
,
"hello world"
);
callback_preargs
(
my_callback_preargs_volatile
,
"hello world"
);
callback_postargs
(
my_callback_postargs
,
"hello world"
);
callback_postargs
(
my_callback_postargs_const
,
"hello world"
);
callback_postargs
(
my_callback_postargs_volatile
,
"hello world"
);
return
exit_status
();
}
typesafe_cb/typesafe_cb.h
0 → 100644
View file @
eded23d4
#ifndef CCAN_CAST_IF_TYPE_H
#define CCAN_CAST_IF_TYPE_H
#include "config.h"
#if HAVE_TYPEOF && HAVE_BUILTIN_CHOOSE_EXPR && HAVE_BUILTIN_TYPES_COMPATIBLE_P
/**
* cast_if_type - only cast an expression if it is of a given type
* @expr: the expression to cast
* @oktype: the type we allow
* @desttype: the type to cast to
*
* 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 @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(expr) \
* _set_some_value(cast_if_type((expr), unsigned long, void *))
*/
#define cast_if_type(expr, oktype, desttype) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(1?(expr):0), oktype), \
(desttype)(expr), (expr))
#else
#define cast_if_type(expr, oktype, desttype) ((desttype)(expr))
#endif
/**
* typesafe_cb - cast a callback function if it matches the arg
* @rettype: 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(rettype, fn, arg) \
cast_if_type(cast_if_type(cast_if_type((fn), \
rettype (*)(const typeof(arg)), \
rettype (*)(void *)), \
rettype (*)(volatile typeof(arg)), \
rettype (*)(void *)), \
rettype (*)(typeof(arg)), \
rettype (*)(void *))
/**
* typesafe_cb_preargs - cast a callback function if it matches the arg
* @rettype: 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(rettype, fn, arg, ...) \
cast_if_type(cast_if_type(cast_if_type((fn), \
rettype (*)(__VA_ARGS__, \
const typeof(arg)), \
rettype (*)(__VA_ARGS__, \
void *)), \
rettype (*)(__VA_ARGS__, \
volatile typeof(arg)), \
rettype (*)(__VA_ARGS__, void *)), \
rettype (*)(__VA_ARGS__, typeof(arg)), \
rettype (*)(__VA_ARGS__, void *))
/**
* typesafe_cb_postargs - cast a callback function if it matches the arg
* @rettype: 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_preargs(void, (fn), (arg), int),\
* (arg))
*/
#define typesafe_cb_postargs(rettype, fn, arg, ...) \
cast_if_type(cast_if_type(cast_if_type((fn), \
rettype (*)(const typeof(arg), \
__VA_ARGS__), \
rettype (*)(void *, \
__VA_ARGS__)), \
rettype (*)(volatile typeof(arg), \
__VA_ARGS__), \
rettype (*)(void *, __VA_ARGS__)), \
rettype (*)(typeof(arg), __VA_ARGS__), \
rettype (*)(void *, __VA_ARGS__))
#endif
/* CCAN_CAST_IF_TYPE_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment