我正在尝试用 C++ 创建一个 LUT,该 LUT 在编译时计算并简单地放置在内存中以供其他模块使用。这个 LUT 中的每一项都可以在编译时计算,它只是一堆常数和运算,例如和、指数和球面贝塞尔函数。
构成其中一个元素的计算示例。 再次注意,方程的每一部分在编译时都是已知的。我只是希望编译器为我完成工作并生成 LUT。
完全来自嵌入式 C 背景,我决定与 C++ 模块交互以利用
constexpr
功能,我的研究表明,这将是生成相当大的 2D 查找表的正确机制,而无需求助于数百个丑陋的数组初始值设定项声明。我的想法是:我可以编写一些函数,将它们标记为 constexpr
,并将它们接触的所有结构、变量和函数标记为 constexpr
,然后一切都会成功。
我遇到的问题是,此 LUT 中元素的计算是使用 C++ stl 数学函数(如
std::powf()
或 std::cyl_bessel_jf()
)计算的,而这些函数不是 constexpr
。它们以非线程安全的方式使用 errno
全局状态变量,因此是不可重入的,因此不能是 constexpr
。酷。
但这对我的用例来说应该不重要。目标应用程序在运行时是否具有
std::powf()
的线程安全实现并不重要,我希望编译器只在编译时计算这些并将其放入内存中!
这引出了我的问题:
constexpr
不是C++机制吗?还是我用我的代码做了一些语法上愚蠢的事情?
这里潜在的问题是我从不同的 C 模块而不是 C++ 调用
foo()
。
说明示例的代码,无需涉及太多数学内容。 注意:我使用 C++17 和 gcc
constexpr int t_cnt = 100;
constexpr int r_cnt = 100;
constexpr float _v_param = 1.0f;
constexpr float _R_param = 1.0f;
constexpr float _mu_param = 1.0f;
constexpr float exp_func (float t, float r)
{
return expf(-1.0f * powf(r, 2) * _v_param * t / powf(_R_param, 2));
}
struct LUT
{
constexpr LUT() : values()
{
for (auto t = 0; t < t_cnt; t++)
{
for (auto r = 0; r < r_cnt; r++)
{
values[t][r] = exp_func(t, r);
}
}
}
float values[t_cnt][r_cnt];
};
void foo ()
{
constexpr auto footable = LUT();
/*I would like to use footable in other parts of the code,
to get values like footable[0][1] without calculating it in runtime*/
}
此代码会生成以下错误:
src/precompute.cpp: In function 'void foo()':
src/precompute.cpp:143:30: in 'constexpr' expansion of 'LUT()'
src/precompute.cpp:133:40: in 'constexpr' expansion of 'exp_func((float)LUT::__ct ::t, (float)LUT::__ct ::r)'
src/precompute.cpp:122:16: error: 'expf(-1.0e+2f)' is not a constant expression
122 | return expf(-1.0f * powf(r, 2) * _v_param * t / powf(_R_param, 2));
我希望我应该能够在编译时创建 LUT 结构,并简单地从中读取值。
这引出了我的问题:constexpr 不是 C++ 机制吗?还是我用我的代码做了一些语法上愚蠢的事情?
不,这都是正确的,而且您原则上想要使用的数学函数的部分功能也适用于常量表达式,这也是正确的。
问题很简单,这些函数最初并不是为了在编译时使用而编写的,需要做一些工作来指定它们在编译时的行为方式(例如如何处理
errno
,如何处理浮点异常) )然后库实现者需要实现这些规范。
对于所有库功能都是如此,而不仅仅是数学函数。
constexpr
已在 C++11 中引入,从那时起,越来越多的核心语言和库已在编译时可用。对于数学函数,这项工作也从 C++23 开始,更多函数将在 C++26 中constexpr
。
您始终可以自己将这些函数(具有您想要的行为)编写为
constexpr
并使用它们。一旦您决定了 errno
和浮点异常行为,就没有什么根本问题了。
这里一个潜在的问题是我从不同的 C 模块而不是 C++ 调用 foo()。
这不是问题。当你这样做时,不会有任何问题。