I am not sure about this function pointer type declaration syntax, but it works. The syntax is just like declaring a regular old function.
typedef struct Element GetChildCallback(void *userdata, usize parent, usize child_no);
I couldn't find any information on how standard-compliant it is or what drawbacks it could possibly have.
I thought this only worked for typedefs, so I took a step further and found out this also works for regular function parameters:
extern inline void dmpstrn(const char *t, usize n, int printer(const char *fmt, ...));
inline void dmpstrn(const char *t, usize n, int printer(const char *fmt, ...)) {
usize len = strlen(t) > n ? n : strlen(t);
printer("\"");
for (usize i = 0; i < len; i += 1) {
if (t[i] == '\n')
printer("\\n");
else
printer("%c", t[i]);
}
printer("\"\n");
}
// ...
int main() {
dmpstrn("Hello\nworld", UINT64_MAX, printf);
}
This however doesn't work for variables
int printer(const char *fmt, ...) = printf; // Invalid
It's as if the it isn't the function pointer but the actual function, but what does it mean?
Function declarations used as function parameters are implicitly adjusted by the compiler to pointers to the function types.
From the C Standard (6.7.6.3 Function declarators (including prototypes))
8 A declaration of a parameter as ‘‘function returning type’’ shall be
adjusted to ‘‘pointer to function returning type’’, as in 6.3.2.1
So for example these two function declarations
void f( void( int ) );
and
void f( void ( * )( int ) );
declare the same one function.
On the other hand (6.3.2.1 Lvalues, arrays, and function designators)
4 A function designator is an expression that has function type.
Except when it is the operand of the sizeof operator65) or the unary &
operator, a function designator with type ‘‘function returning type’’
is converted to an expression that has type ‘‘pointer to function
returning type’’
Here is a demonstrative program.
#include <stdio.h>
typedef void F( int );
F display;
void g( F );
void g( F *f )
{
int x = 10;
f( x );
}
void display( int x )
{
printf( "x = %d\n", x );
}
int main(void)
{
F *fp = display;
g( fp );
return 0;
}
The program output is
x = 10
Investigate the program.
Pay attention to that for example this typedef
typedef void F( int );
may be equivalently rewritten like
void typedef F( int );
typedef struct Element GetChildCallback(void *userdata, usize parent, usize child_no);
This defines GetChildCallback as the actual "function type", which is different from the pointer to function type. This can be used to declare a function or form the pointer to function type, but not to define a function:
GetChildCallback MyButtonCB; /* OK, declares function */
GetChildCallback MyResizeCB {} /* ERROR, bad syntax */
struct Element MyButtonCB(void *, usize, usize) {} /* OK */
GetChildCallback cb1 = MyButtonCB; /* ERROR, bad function definition */
GetChildCallback* cb2 = MyButtonCB; /* OK */
void add_callback(Button* button, GetChildCallback* cb); /* OK */
When a function type is used as a function parameter, it silently becomes the pointer to function type, just like an array parameter type becomes the element pointer type. So your example
inline void dmpstrn(const char *t, usize n, int printer(const char *fmt, ...)) {
means just the same as
inline void dmpstrn(const char *t, usize n, int (*printer)(const char *fmt, ...)) {
including the fact that printer in the function body is a pointer to function, so for example (&printer) does not act the same as (printer).
Related
I have an error when I compile my C program.
I have this code :
#include <stdio.h>
#include <stdlib.h>
#define SET__SIZE 10
#define SET__BOUND ((void*) NULL)
struct set {
void *s[SET__SIZE];
int cursor;
int (*cmp)(const void*, const void*);
void * (*copy)(const void*);
void (*del)(void *);
};
int find(const void *s[], void *c, int (*cmp)(const void*, const void*))
{
int i = 0;
while (s[i]!=SET__BOUND && cmp(s[i],c)<0)
i++;
return i;
}
int set__find(const struct set *se, void *c)
{
return (se->cmp(se->s[find(se->s,c,se->cmp)],c)==0);
}
For some reason, gcc is raising a warning for the find call in set__find saying :
note: expected ‘const void **’ but argument is of type ‘void * const*’
I can't understand why he thinks the argument is a constant pointer (if I understood the error right)
As I tried, the note goes away, when I change the code to
struct set {
void *s[SET__SIZE];
int cursor;
int (*cmp)(const void*, const void*);
void * (*copy)(const void*);
void (*del)(void *);
};
int find(void * const s[], void *c, int (*cmp)(const void*, const void*))
{
int i = 0;
while (s[i]!=SET__BOUND && cmp(s[i],c)<0)
i++;
return i;
}
int set__find(const struct set *se, void *c)
{
return (se->cmp(se->s[find(se->s,c,se->cmp)],c)==0);
}
Explanation
In the answer... No point of writting it again.
why he thinks the argument is a constant pointer
struct set {
void *s[SET__SIZE];
The structure set contains the array s.
const struct set *se
se points to a constant structure set. Because the structure is constant, the memory for the structure is constant. The elements of the array s can't be modified, they are in constant memory.
find(se->s,
Arrays decay to the pointer to the first element. So imagine it's TYPE s[SET__SIZE] where TYPE is a void*. TYPE s[SET__SIZE] decays to a pointer TYPE *. But, it's constant, so it's const TYPE s[SET__SIZE]. So it decays to a const TYPE *. You can't modify it, it's a constant array. TYPE is a void* - you can dereference the element and modify it then, but you can't modify the pointer value itself.
TL;DR you want int find(void * const s[] as in the other answer.
I want to write a wrapper for read and write unix functions, but read has a const void pointer parameter, and write a simple void pointer as a parameter.
So, a prototype like this, will fail for one of the functions:
typedef ssize_t (*genericStreamHandler)(int, const void*, size);
Do not prototype the function signature if code needs to allow incompatible functions.
The following compiles without warnings/errors.
#include <stdio.h>
ssize_t file_read(int h, const void* b, int sz) {
if (b) return 0;
return h + sz;
}
ssize_t file_write(int h, void* b, int sz) {
if (b) return 0;
return h + sz;
}
//typedef ssize_t (*genericStreamHandler)(int, void*, int);
// v--- No function prototype
typedef ssize_t (*genericStreamHandler)();
int main(void) {
genericStreamHandler gFH1 = file_read;
genericStreamHandler gFH2 = file_write;
char buf[10];
return (*gFH1)(0, buf, 10) + (*gFH2)(0, buf, 10);
}
OTOH, the better answer may lie in taking another approach that does not need a common type for variant function signatures.
I'm trying to create a system call handler, and I'm not sure how to store it.
I'm using the following typedef to store a (void *) pointer, which should receive the address of the function and an integer arg_no representing the number of arguments. Then, I create an array of this type.
typedef struct
{
void *foo;
int arg_no;
}td_sys_call_handler;
td_sys_call_handler ish[SYSCALL_HANDLER_NUM];
I'm trying to initialize the array in the following manner.
ish[0].foo = void (*halt) (void); ish[0].arg_no = 0;
ish[1].foo = void (*exit) (int status) NO_RETURN; ish[1].arg_no = 1;
ish[2].foo = pid_t (*exec) (const char *file); ish[2].arg_no = 1;
ish[3].foo = int (*wait) (pid_t); ish[3].arg_no = 1;
ish[4].foo = bool (*create) (const char *file, unsigned initial_size);
ish[4].arg_no = 2;
ish[5].foo = bool (*remove) (const char *file); ish[5].arg_no = 1;
ish[6].foo = int (*open) (const char *file); ish[6].arg_no = 1;
ish[7].foo = int (*filesize) (int fd); ish[7].arg_no = 1;
ish[8].foo = int (*read) (int fd, void *buffer, unsigned length);
ish[8].arg_no = 3;
ish[9].foo = int (*write) (int fd, const void *buffer, unsigned length);
ish[9].arg_no = 3;
ish[10].foo = void (*seek) (int fd, unsigned position);
ish[10].arg_no = 2;
ish[11].foo = unsigned (*tell) (int fd); ish[11].arg_no = 1;
But all the assignments from the function pointer to the void pointer produce the following error:
../../userprog/syscall.c: In function ‘syscall_init’:
../../userprog/syscall.c:76:17: error: expected expression before ‘void’
../../userprog/syscall.c:77:17: error: expected expression before ‘void’
../../userprog/syscall.c:78:17: error: expected expression before ‘pid_t’
../../userprog/syscall.c:79:17: error: expected expression before ‘int’
../../userprog/syscall.c:80:17: error: expected expression before ‘_Bool’
../../userprog/syscall.c:82:17: error: expected expression before ‘_Bool’
../../userprog/syscall.c:83:17: error: expected expression before ‘int’
../../userprog/syscall.c:84:17: error: expected expression before ‘int’
../../userprog/syscall.c:85:17: error: expected expression before ‘int’
../../userprog/syscall.c:87:17: error: expected expression before ‘int’
../../userprog/syscall.c:89:17: error: expected expression before ‘void’
../../userprog/syscall.c:91:17: error: expected expression before ‘unsigned’
I was under the impression that void* is the only instance of polymorphism in the language and that it can point to anything.
However, it appears that I'm wrong.
So which is the type of the pointer which can store the address of any function type?
Also, can you give me a good reference about C polymorphism? I've looked in many books but as far as I've seen the polymorphism chapter is very thin.
Thank you.
Yes, you are wrong.
void * pointers can point at any kind of data, but in C code (functions) are not data.
It's not valid to even cast between void * and function pointers: even though on most contemporary computers it will work as expected, the language does not guarantee that.
I don't understand from your code how you intended the "overloading" to be used in practice, how do you expect to call through the foo pointer? Just having the expected number of arguments is not enough, arguments have types and thus are handled differently in the function call.
The notation you need casts the system call function pointer to void *:
ish[0].foo = (void *)halt;
The C standard does not guarantee that pointers to functions will fit into pointers to data such as void *; fortunately for you, POSIX steps in and does guarantee that pointers to functions are the same size as pointers to data.
Your syntax is wrong. You should declare your function pointer first. Then you can use the address of the function pointer to assign to the pointer.
void (*halt) (void) = halt_sys_call_function;
ish[0].foo = &halt; ish[0].arg_no = 0;
C doesn't support traditional inheritance relationships in a direct way, but it does guarantee that the address of a structure is also the address of the first member of the structure. This can be used to emulate polymorphism in C. I described a similar approach in an answer I wrote about dynamic dispatch in C.
Consider a struct formatted to hold each function specifically:
typedef struct
{
void (*halt) (void);
void (*exit) (int status);
pid_t (*exec) (const char *file);
int (*wait) (pid_t);
bool (*create) (const char *file, unsigned initial_size);
bool (*remove) (const char *file);
int (*open) (const char *file);
int (*filesize) (int fd);
int (*read) (int fd, void *buffer, unsigned length);
int (*write) (int fd, const void *buffer, unsigned length);
void (*seek) (int fd, unsigned position);
unsigned (*tell) (int fd);
} myFuncs;
OR
This is messy and VERY unmaintable, but if you did cast each pointer to a void*, using void *addressOfWait = (void*)&wait;, then you could re-cast to the correct function pointer type before calling:
int (*waitFunctionPointer)(pid_t) = addressOfWait;
Then you could call that pointer:
waitFunctionPointer((pid_t) 1111); //wait for process with pid of 1111
I'll ask for #problemPotato's forgiveness for filching his structure definition:
typedef struct
{
void (*halt) (void);
void (*exit) (int status);
pid_t (*exec) (const char *file);
int (*wait) (pid_t);
bool (*create) (const char *file, unsigned initial_size);
bool (*remove) (const char *file);
int (*open) (const char *file);
int (*filesize) (int fd);
int (*read) (int fd, void *buffer, unsigned length);
int (*write) (int fd, const void *buffer, unsigned length);
void (*seek) (int fd, unsigned position);
unsigned (*tell) (int fd);
} fs_ops;
Say you have matching functions, declared like:
int ext5_open(const char * file);
unsigned ext5_tell (int fd);
then you can define and initialize a variable like (the bare name of the function is a pointer to it):
fs_ops ext5_ops = {
.open = ext5_open,
.tell = ext5_tell,
};
Fields that aren't initialized get NULL (i.e., pointer to no function). You can change the value of a field, ask if it is set (if(ext5_ops.seek == NULL) ...), and call the function:
retval = ext5_ops.(*ext5_open)("/tmp/junk");
(the parenteses around (*ext5_open) are because * (pointer indirection) binds less strongly than function call).
A function pointer can be converted into a void *, but it's a little trickier to convert it back to correct function-pointer type in order to call it. It should be possible by using a union. You'll need a separate union-member of the correct type for type of function that you want to store. And, as user4815162342 notes in a comment, you'll need to manage all the various combinations, probably with an enum.
typedef struct
{
union {
void *vp;
void (*v__v)(void);
void (*v__i)(int);
pid_t (*pid__ccp)(const char *);
int (*i__pid)(pid_t);
bool (*b__ccp_u)(const char *, unsigned);
bool (*b__ccp)(const char *);
int (*i__ccp)(const char *);
int (*i__i)(int);
int (*i__i_vp_u)(int, void *, unsigned);
int (*i__i_cvp_u)(int, const void *, unsigned);
void (*v__i_u)(int, unsigned);
unsigned (*u__i)(int);
} fp;
int arg_no;
}td_sys_call_handler;
The idea here is to try to encode the types into the identifiers, as a kind of "apps-Hungarian". This way, the meaning of any of these identifiers is directly visible.
It may be easier to generate these pointers and the associated enum at the same time. I think the easiest way to manage this part is with my favorite trick, X-Macros. Warning: it just gets more and more weird.
#define function_types(_) \
_(v__v, void, void) \
_(v__i, void, int) \
_(pid_ccp, pid_t, const char *) \
_(i__pid, int, pid_t) \
_(b__ccp_u, const char *, unsigned) \
_(b__ccp, const char *) \
_(i__ccp, const char *) \
_(i__i, int) \
_(i__i_vp_u, int, void *, unsigned) \
_(i__i_cvp_u, int, const void *, unsigned) \
_(v__i_u, int, unsigned) \
_(u__i, unsigned, int) \
/* end function_types */
This "master"-macro is a comma separated table of tokens which is passed, row by row, to the _ underscore macro, which is passed-in.
Now the struct type can be constructed by writing additional macros to use the rows, these are passed-in as _ to the table macro to instantiate the template:
#define create_function_pointer(id, ret, ...) \
ret (*id)(__VA_ARGS__);
#define create_function_type_id(id, ret, ...) \
f__ ## id
typedef struct {
union {
void *vp;
function_types(create_function_pointer)
} fp;
int arg_no;
enum {
function_types(create_function_type_id)
} type;
} td_sys_call_handler;
Now an array of these structs can be populated:
td_sys_call_handler ish[SYSCALL_HANDLER_NUM];
int i=0;
ish[i++] = (td_sys_call_handler){ halt, 0, f__v__v };
ish[i++] = (td_sys_call_handler){ exit, 1, f__v__i };
ish[i++] = (td_sys_call_handler){ exec, 1, f__pid__ccp };
ish[i++] = (td_sys_call_handler){ wait, 1, f__i__pid };
ish[i++] = (td_sys_call_handler){ create, 2, f__b__ccp_u };
ish[i++] = (td_sys_call_handler){ remove, 1, f__b__ccp };
ish[i++] = (td_sys_call_handler){ open, 1, f__i__ccp };
ish[i++] = (td_sys_call_handler){ filesize, 1, f__i__i };
ish[i++] = (td_sys_call_handler){ read, 3, f__i__i_vp_u };
ish[i++] = (td_sys_call_handler){ write, 3, f__i__i_cvp_u };
ish[i++] = (td_sys_call_handler){ seek, 2, f__v__i_u };
ish[i++] = (td_sys_call_handler){ tell, 1, f__u__i };
Now, calling a function given one of these structs will require (as you surmised) a switch, with a separate case for each signature. It needs to crack the arguments using stdarg and the call with the appropriate union member function pointer.
void make_sys_call(td_sys_call_handler ish, ...){
va_list ap;
int i;
const char *ccp;
pid_t pid;
bool b;
void *vp;
unsigned u;
const void *cvp;
va_start(ap, ish);
switch(ish.type) {
case f__v__f: ish.fp.v__v();
break;
case f__v__i: i = va_arg(int);
ish.fp.v__i(i);
break;
case f__pid__ccp: ccp = va_arg(const char *);
ish.fp.pid__ccp(ccp);
break;
// etc.
}
va_end(ap);
}
It will not be possible to return different types directly. You will either need to allocate a union type variable to hold the return value and return that, or something even more insane. An external stack data type could hold unions of the various return types. Depending on profiling results, it may be appropriate to consider this instead of returning the unions.
HTH.
I am new to the C language and pointers and I am confused by this function declaration:
void someFunction(int (*)(const void *, const void *));
Can anyone explain in layman's terms what this does and how it works?
It's the prototype of a function that takes:
a pointer to a function that takes a const void* and a const void* as arguments and returns an int
as an argument, and returns void.
It declares a function, which takes another function as its argument, and returns nothing. The other function would be declared as
int otherfunction( const void *, const void * );
and you would call somefunction() like this:
somefunction( otherfunction );
It's a function that has a single parameter. That parameter is a pointer to a function that returns an int and takes those two void pointers to constant data parameters.
This is the declaration of a function which takes a function pointer as its argument. In its most basic form, it looks like this:
void someFunction(argument_type);
Where argument_type is int (*)(const void *, const void *), which can be described as a "pointer to a function that takes two const void * arguments, and returns an int". i.e. any function that has the following declaration:
int foo(const void *, const void *);
To illustrate by example:
int foo_one(const void * x, const void * y) { ... }
int foo_two(const void * x, const void * y) { ... }
void someFunction(int (*)(const void *, const void *) function_ptr)
{
const void * x = NULL;
const void * y = NULL;
int result;
result = (*function_ptr)(x, y); // calls the function that was passed in
}
int main()
{
someFunction(foo_one);
someFunction(foo_two);
return 0;
}
Check this very helpful when dealing with complex declarations.
I'm writing a unit test framework (see SO for more details). Or view the code at GitHub.
Safer Code describes a way to pass functions of arbitrary types.
But how do I call such a function without knowing its types beforehand? Assume f needs no input, so f() should work on its own.
Let's say I want to populate an array using an arbitrary generator function.
void* gen_array(fp gen, size_t size) {
int i, len = gen_int() % 100;
void* arr = GC_MALLOC(len * size);
for (i = 0; i < len; i++) {
arr[i] = gen(NULL);
}
return arr;
}
It should look something like this, but I get compiler errors:
gcc -o example example.c qc.c qc.h -lgc
In file included from example.c:1:
qc.h:21: error: expected declaration specifiers or ‘...’ before ‘size_t’
In file included from qc.c:1:
qc.h:21: error: expected declaration specifiers or ‘...’ before ‘size_t’
qc.c:23: error: conflicting types for ‘gen_array’
qc.h:21: error: previous declaration of ‘gen_array’ was here
qc.c: In function ‘gen_array’:
qc.c:29: warning: dereferencing ‘void *’ pointer
qc.c:29: error: too many arguments to function ‘gen’
qc.c:29: error: invalid use of void expression
qc.h:21: error: expected declaration specifiers or ‘...’ before ‘size_t’
make: *** [example] Error 1
After thinking about some more I realize your problem your above code would never work.
You are first calling trying to call a void function with no parameters with the parameter NULL. Next you would need your code to be more generic. I placed an example below of what I mean. Now using a global variable
#include <stdio.h>
#include <stdlib.h>
typedef void (*fp)(void);
void * GEN_ARRAY_TEMP;
int gen_int() {
return 67;
}
void* gen_array(fp gen, size_t size) {
int i, len = gen_int() % 100;
void* arr = malloc(len * size);
void* arr_end = arr + len * size;
GEN_ARRAY_TEMP = arr;
while (GEN_ARRAY_TEMP <= arr_end) {
gen();
GEN_ARRAY_TEMP+=size;
}
return arr;
}
void make_int() {
(*(int*)GEN_ARRAY_TEMP) = 9;
}
int main() {
int i;
int * gen_int_array = (int*) gen_array(make_int, sizeof(int));
for(i=0;i<67;i++) {
printf("%d\n",gen_int_array[i]);
}
}
That page suggests you make the function pointer take a void*. So in order for your code to compile, you must pass it a void pointer:
typedef void* (*fp)(void*);
doit(fp f) {
f(NULL);
}
And just make sure that the function that you're calling simply ignores the parameter.
Generally speaking, these generic function pointers are used for starting threads. The void pointer is simply a pointer to a struct that holds the actual parameters.
What would you need to do is wrap your function in a void function like so
#include <stdio.h>
typedef void (*fp)(void);
int sum(int x,int y) {return x+y;}
void doit(fp f) {
f();
}
void func() {
printf("Hello %d\n",sum(1,2));
}
int main() {
doit(func);
}
You have two problems:
First, qc.h is missing a <stdlib.h> include. This is needed for use of size_t.
Second, in gen_array, you create a void *arr, then try to dereference it as an array (arr[i]). Since the compiler doesn't know the size of your array elements, it cannot fill the array. You must treat it as a char *, offset by arr + size * i, and pass it into gen rather than taking a return (returns also need to know the structure size):
// ...
char *arr = GC_MALLOC(len * size);
for (int i = 0; i < len; i++) {
gen(arr + i * size, NULL);
}
return arr;
This will of course require changing the fp type definition.
For the case where your pointer to a function 'fp' is of type which takes no argument and returns void, in which case you should declare it as :
typedef void (*fp)();
In the above case the call should be :
(*gen)();
If your pointer to the function 'fp' is of type which takes 'void *' as argument and returns void, in which case you should declare it as :
typedef void (*fp)(void *);
In the above case the call should be :
(*gen)(NULL);
or any other pointer variable you might want to pass.
As far as your example goes try this :
typedef void * (*fp)(void *);
void* gen_array(fp gen, size_t size) {
int i, len = gen_int() % 100;
void* arr = GC_MALLOC(len * size);
for (i = 0; i < len; i++) {
arr[i] = (*gen)(NULL);
}
return arr;
}