C++:创建使用数学函数计算元素的编译时计算查找表

问题描述 投票:0回答:1

我正在尝试用 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 结构,并简单地从中读取值。

c++ c++17 constexpr lookup-tables
1个回答
0
投票

这引出了我的问题:constexpr 不是 C++ 机制吗?还是我用我的代码做了一些语法上愚蠢的事情?

不,这都是正确的,而且您原则上想要使用的数学函数的部分功能也适用于常量表达式,这也是正确的。

问题很简单,这些函数最初并不是为了在编译时使用而编写的,需要做一些工作来指定它们在编译时的行为方式(例如如何处理

errno
,如何处理浮点异常) )然后库实现者需要实现这些规范。

对于所有库功能都是如此,而不仅仅是数学函数。

constexpr
已在 C++11 中引入,从那时起,越来越多的核心语言和库已在编译时可用。对于数学函数,这项工作也从 C++23 开始,更多函数将在 C++26 中
constexpr

您始终可以自己将这些函数(具有您想要的行为)编写为

constexpr
并使用它们。一旦您决定了
errno
和浮点异常行为,就没有什么根本问题了。

这里一个潜在的问题是我从不同的 C 模块而不是 C++ 调用 foo()。

这不是问题。当你这样做时,不会有任何问题。

© www.soinside.com 2019 - 2024. All rights reserved.