реализовать функцию в C99 <tgmath.h> и GCC

Я пытаюсь следовать этому руководству , чтобы реализовать функцию cbrt универсальным способом, который работает для float и double.

Мой код C:

      #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); } 

Как указано https://godbolt.org/z/MW84rvca7 , GCC() не может его скомпилировать. Я пробовал несколько версий GCC от 4.8 до 10.4. GCC 10.4 сообщает о следующих ошибках:

      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); }
      |                                  ^~~~

Что я делаю не так? Является ли руководство неправильным, мое решение неверно или GCC неправильно реализует стандарт C99? Как это можно исправить, чтобыgcc -std=c99компилирует это?

Обратите внимание, что я хотел бы исправить(void*)подход, описанный в руководстве выше, и поэтому в этом вопросе меня не интересуют следующие обходные пути:

  • _Generic(...)сgcc -std=c11.
  • GCC__builtin_tgmath.
  • ССЗ.
  • ССЗ. Действительно возможно решить эту проблему с помощью комбинации , и__builtin_classify_type.
  • GCC (см. здесь ). К вашему сведению, это действительно возможно решить с помощью комбинации__builtin_choose_expr,__typeof__и__builtin_types_compatible_p.

К вашему сведению: по аналогичному вопросу, о<tgmath.h>, но это не отвечает на мои вопросы об этой конкретной реализации и GCC.

как реализован &amp;lt;tgmath.h&amp;gt;?Как реализован &amp;lt;tgmath.h&amp;gt;?

1 ответ

Причина сбоя заключается в том, что GCC и Clang не рассматривают выражения, содержащие значения с плавающей запятой, как целочисленное константное выражение, как показано здесь:

      prog.c: In function 'x':
prog.c:1:22: error: first argument to '__builtin_choose_expr' not a constant
 int x(void) { return __builtin_choose_expr(0.25 == 0.25, 5, 6); }
      prog.c: In function 'x':
prog.c:1:22: error: first argument to '__builtin_choose_expr' not a constant
 int x(void) { return __builtin_choose_expr((int)(double)1, 5, 6); }

Я недостаточно хорошо знаю C99, чтобы решить, прав ли здесь GCC.

Чтобы это исправить,__has_integer_type(x)необходимо изменить на что-то, что не содержит чисел с плавающей запятой, даже в качестве временных. К сожалению, это невозможно без и__builtin_types_compatible_p.

Однако с GCC__builtin_classify_type, это легко (см. также https://godbolt.org/z/43aYer9hE):

      #include <math.h>

#define __has_integer_type(x) (__builtin_classify_type(x) != 8)
#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); } 
Другие вопросы по тегам