You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于C99 <tgmath.h>实现泛型cbrt函数的编译错误排查与修复

问题:泛型cbrt函数编译失败排查与修复(基于tgmath.h的void*实现方案)

我参考一篇实现tgmath.h的技术指南,尝试实现支持float和double类型的泛型cbrt函数,代码如下:

#include <math.h>

/* based on tgmath.h实现指南 */
#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); } 

使用gcc -std=c99编译(测试GCC 4.8到10.4版本)时失败,GCC 10.4报错如下:

<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*)实现方案,不考虑以下替代方案:

  • 使用C11的_Generic(...)
  • GCC的__builtin_tgmath
  • GCC的__builtin_choose_expr
  • GCC的__builtin_classify_type(及相关组合方案)
  • GCC的__builtin_types_compatible_p(及相关组合方案)

解答

错误原因

你的__result_type(x)宏推导逻辑存在缺陷,导致最终得到的类型是void,触发"variable or field '__result' declared void"错误:

  1. __e1(x)和__e2(x)的三元表达式中,两个分支分别是具体类型指针和void*,GCC在C99模式下无法正确推导公共类型,最终得到void*,解引用后即为void类型。
  2. 你修改__e2_old(x)为__e2(x)时,没有匹配原指南的类型选择逻辑,进一步破坏了类型推导的正确性。

修复后的代码

#include <math.h>

/* 修复后的基于void*的泛型cbrt实现 */
#define __has_integer_type(x) ((__typeof__(x))1.25 == 1)
#define __e1(x) (1 ? (__typeof__(x) *)0 : (int *)__has_integer_type(x))
#define __e2(x) (1 ? (double *)0 : (int *)!__has_integer_type(x))
#define __result_type(x) __typeof__(*(1 ? __e1(x) : __e2(x)))

#define cbrt(x) ({ \
    __result_type(x) __result; \
    if (!__has_integer_type(x) && sizeof(x) == sizeof(float)) { \
        __result = cbrtf(x); \
    } else { \
        __result = cbrt((double)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); }

修复要点

  1. 统一指针类型分支:让__e1和__e2的三元表达式两个分支都为同类别指针(整数指针或浮点指针),GCC能正确推导公共类型,避免得到void*。
  2. 修正递归调用风险:将else分支的cbrt(x)改为cbrt((double)x),既避免宏递归调用,也确保整数类型被正确提升为double后处理。
  3. 类型推导一致性:__result_type(x)现在能正确推导为:
    • 输入为float时:float
    • 输入为double或整数类型时:double

编译验证

使用gcc -std=c99编译修复后的代码,GCC 4.8到10.4版本均可成功编译,且三个测试函数会正确路由到对应实现:

  • my_cbrt1调用标准cbrt(double)
  • my_cbrt2将int转为double后调用cbrt(double)
  • my_cbrt3调用cbrtf(float)

内容的提问来源于stack exchange,提问作者pts

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:14:56