Re: ensure function pointer / argument compatibility for apply-like functions
Andrew C Aitchison via Gcc-help <[email protected]> Tue, 9 Dec 2025 12:02:52 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.help |
|---|---|
| Message-ID | <[email protected]> |
I am assuming C rather than C++, but what standard level we can require ?
On Tue, 9 Dec 2025, Matthias Pfaller wrote:
> and now for something completely different...
>
> In our RTOS I have a function called task_create. Originally I declared it as
> follows:
>
> struct task *task_create(const char *name, void (*f)(void *), void *farg,
> unsigned int stacksize, unsigned int priority);
>
> When the task is created, it is started by calling f(farg). But actually I
> wanted to be able to declare the task function taking an arbitrary typed
> argument and I wanted to make sure that the "farg" argument (which also
> should be of an arbitrary type) passed to task_create matches the prototype
> of f.
>
> I came up with the following solution. I modified the original prototype as
> follows:
>
> union __attribute__((__transparent_union__)) __task_arg {
> void *__parg;
> const void *__cparg;
> volatile void *__vparg;
> const volatile void *__cvparg;
> int __iarg;
> unsigned int __uiarg;
> short __siarg;
> unsigned short __usiarg;
> };
>
> struct task *_task_create(const char *name, void *f, union __task_arg arg,
> unsigned int stacksize, unsigned int priority);
>
> The task_create function is then replaced by the following macro:
>
> static void __attribute__((error("incompatible arguments"), __unused__,
> __noinline__)) __task_create_error(void) { while (1); }
>
> /*
> * We need to cast arg to unsigned int in the first if-clause
> * because otherwise gcc will emit a
> * warning when arg is something like &variable (object addresses never
> * can be NULL).
> */
> #define task_create(name, func, arg, stacksize, priority) ({ \
> if (__builtin_constant_p(arg) && ((unsigned int)(arg)) == 0) { \
> /* null pointer arg is allowed */ \
> } else if ((__builtin_types_compatible_p(__typeof__(&func), void
> (*)(void *)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(volatile void *)))
> && \
> !__builtin_types_compatible_p(__typeof__(arg), signed char)
> && \
> !__builtin_types_compatible_p(__typeof__(arg), char) && \
> !__builtin_types_compatible_p(__typeof__(arg), short) && \
> !__builtin_types_compatible_p(__typeof__(arg), int) && \
> !__builtin_types_compatible_p(__typeof__(arg), long) && \
> !__builtin_types_compatible_p(__typeof__(arg), unsigned char)
> && \
> !__builtin_types_compatible_p(__typeof__(arg), unsigned
> short) && \
> !__builtin_types_compatible_p(__typeof__(arg), unsigned int)
> && \
> !__builtin_types_compatible_p(__typeof__(arg), unsigned
> long)) { \
> /* every pointer is accepted when the function expects void * */
> \
> } else if ((__builtin_types_compatible_p(__typeof__(func), void
> (*)(signed char)) || \
> __builtin_types_compatible_p(__typeof__(func), void
> (*)(char)) || \
> __builtin_types_compatible_p(__typeof__(func), void
> (*)(short)) || \
> __builtin_types_compatible_p(__typeof__(func), void (*)(int))
> || \
> __builtin_types_compatible_p(__typeof__(func), void
> (*)(long)) || \
> __builtin_types_compatible_p(__typeof__(func), void
> (*)(unsigned char)) || \
> __builtin_types_compatible_p(__typeof__(func), void
> (*)(unsigned int)) || \
> __builtin_types_compatible_p(__typeof__(func), void
> (*)(unsigned long)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(signed char)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(char)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(short)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(int)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(long)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(unsigned char)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(unsigned int)) || \
> __builtin_types_compatible_p(__typeof__(&func), void (*)(unsigned long))) &&
> \
> (__builtin_types_compatible_p(__typeof__(arg), signed char)
> || \
> __builtin_types_compatible_p(__typeof__(arg), char) || \
> __builtin_types_compatible_p(__typeof__(arg), short) || \
> __builtin_types_compatible_p(__typeof__(arg), int) || \
> __builtin_types_compatible_p(__typeof__(arg), long) || \
> __builtin_types_compatible_p(__typeof__(arg), unsigned char)
> || \
> __builtin_types_compatible_p(__typeof__(arg), unsigned short)
> || \
> __builtin_types_compatible_p(__typeof__(arg), unsigned int)
> || \
> __builtin_types_compatible_p(__typeof__(arg), unsigned
> long))) { \
> /* all integer arguments are considered compatible */ \
> } else if (__builtin_types_compatible_p(__typeof__(func), void
> (*)(__typeof__(arg)))) { \
> /* matching type */ \
> } else if (__builtin_types_compatible_p(__typeof__(&(func)), void
> (*)(__typeof__(arg)))) { \
> /* matching type */ \
> } else { \
> __task_create_error(); \
> } \
> _task_create(name, func, arg, stacksize, priority); \
> })
>
> That way I can e.g. do
>
> void task1(int wd);
> task_create("wd", task1, 20, 1024, 10);
>
> But if I would do:
>
> task_create("wd", task1, "hello", 1024, 10);
>
> I would get a compile time error.Is there a simpler way to achieve my goal (I
> did this about 14 years ago...)?
>
> regards, Matthias
>
--
Andrew C. Aitchison Kendal, UK
[email protected]