I'm trying to follow this guide to implement the cbrt function in a type-generic way that works for float and double.
My C code:
#include <math.h>
/* based on https://web.archive.org/web/20131205042841/http://carolina.mff.cuni.cz/~trmac/blog/2005/the-ugliest-c-feature-tgmathh/ */
#define __has_integer_type(x) ((__typeof__(x))1.25 == 1)
#define __e1(x) (1 ? (__typeof__(x) *)0 : (void *)__has_integer_type(x))
#define __e2_old(x) (1 ? (int *)0 : (void *)(!__has_integer_type(x)))*/
#define __e2(x) (1 ? (double *)0 : (void *)(!__has_integer_type(x)))
#define __result_type(x) __typeof__(*(1 ? (__typeof__(__e1(x)))0 : (__typeof__(__e2(x)))0))
#define cbrt(x) ({ __result_type(x) __result; if (sizeof(x) == sizeof(float) && !__has_integer_type(x)) { __result = cbrtf(x); } else { __result = cbrt(x); }; __result; })
double my_cbrt1(double x) { return cbrt(x); }
double my_cbrt2(int x) { return cbrt(x); }
float my_cbrt3(float x) { return cbrt(x); }
As indicated by https://godbolt.org/z/MW84rvca7 , GCC (gcc -std=c99
) fails to compile it. I've tried several GCC versions ranging from 4.8 to 10.4. GCC 10.4 reports the following errors:
C source #1x86-64 gcc 10.4 (Editor #1)Output of x86-64 gcc 10.4 (Compiler #1)
<source>: In function 'my_cbrt1':
<source>:9:37: error: variable or field '__result' declared void
9 | #define cbrt(x) ({ __result_type(x) __result; if (sizeof(x) == sizeof(float) && !__has_integer_type(x)) { __result = cbrtf(x); } else { __result = cbrt(x); }; __result; })
| ^~~~~~~~
<source>:11:36: note: in expansion of macro 'cbrt'
11 | double my_cbrt1(double x) { return cbrt(x); }
| ^~~~
<source>: In function 'my_cbrt3':
<source>:9:37: error: variable or field '__result' declared void
9 | #define cbrt(x) ({ __result_type(x) __result; if (sizeof(x) == sizeof(float) && !__has_integer_type(x)) { __result = cbrtf(x); } else { __result = cbrt(x); }; __result; })
| ^~~~~~~~
<source>:13:34: note: in expansion of macro 'cbrt'
13 | float my_cbrt3(float x) { return cbrt(x); }
| ^~~~
What am I doing wrong? Is the guide wrong, is my solution wrong, or does GCC implement the C99 standard incorrectly? How can it be fixed so that gcc -std=c99
compiles it?
Please note I'd like to fix the (void*)
approach described in the guide above, and thus in this question I'm not interested in the following workarounds:
_Generic(...)
withgcc -std=c11
.- GCC
__builtin_tgmath
. - GCC
__builtin_choose_expr
. - GCC
__builtin_classify_type
. It's indeed possible to solve it with a combination of__builtin_choose_expr
,__typeof__
and__builtin_classify_type
. - GCC
__builtin_types_compatible_p
(see here). FYI It's indeed possible to solve it with a combination of__builtin_choose_expr
,__typeof__
and__builtin_types_compatible_p
.
FYI See How is <tgmath.h> implemented? for a similar question, about <tgmath.h>
, but it doesn't answer my questions about this specific implementation and GCC.