2
\$\begingroup\$

I have to register different callback functions to a scheduler. The callback signature defines a void * parameter. Some callbacks don't use a parameter. This works, but is it clean?

I expected at least an incompatible pointer type warning for the missing cast on the parameterless call which I actually get when using a function to register the callback but the compiler doesn't complain about this example.

#include <stdio.h>
typedef void (* cb)(void *);
void noparams(void) {
 printf("noparams\n");
}
void params(void * param) {
 char * s = param;
 printf(s);
}
void call(cb cbk, void * ctx) {
 cbk(ctx);
}
int main(void) {
 void * context = 0;
 call(noparams, context); // warning expected
 
 char str[] = "params\n";
 call(params, str);
 return 0;
}

ideone

asked Sep 23, 2020 at 7:52
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

When I compile your code with gcc (9.1.1 ) and use the command line option -Wall, I get a warning:

cb.c:20:10: warning: passing argument 1 of ‘call’ from incompatible pointer type [-Wincompatible-pointer-types]
 20 | call(noparams, context); // warning expected
 | ^~~~~~~~
 | |
 | void (*)(void)

So you might want to up the warning level on the compilation. You could suppress this warning with a cast -- it is ok to cast function pointers.

However the real problem is that it is, I believe, undefined behaviour to call a function (either directly or through a function pointer) with a different number of arguments from those given in its definition. (You might want to check this by asking a stack overflow question with tags C and language lawyer).

If it is indeed UB then you have to avoid it. It might appear to work with a particular compiler, but fail arbitrarily with a different compiler, or even with the same compiler on a different platform.

What you have to do, I think, is to provide a callback with the demanded arguments. You could do this, for example, by adding

void noparams_wrap(void* p) {
 noparams();
}

and passing noparams_wrap to call() rather than noparams() itself.

answered Sep 23, 2020 at 12:11
\$\endgroup\$
1
  • \$\begingroup\$ Thank you for the answer. I'll ask on SO. The problem with the wrapper function is that the compiler complains about the unused parameter. Still better than UB though. I wonder if there's an elegant solution. I use my example on ARM and it probably works because the unused parameter ends up in the scratch register r0 which can be safely ignored/overwritten by the callee. I have no clues on why it works on x86_64 (ideone). I don't know the calling conventions for that arch. \$\endgroup\$ Commented Sep 25, 2020 at 9:51

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.