像 C++ constexpr 一样的 ANSI-C 常量表达式函数?
ANSI-C constant-expression function like C++ constexpr?
简单地说,有没有一种 ANSI-C 方法可以使函数成为常量表达式?
- 纯 ANSI-C,但 GNU 扩展是可以接受的 - 但不是 C++。
- 最好不要依赖宏。
- 某些行为肯定类似于 C++
constexpr
并且不会在 运行 时解决。
背景:
我需要在没有浮点的嵌入式处理器中实现大量数学运算,因此我在我的应用程序中使用了定点。
不过,我不喜欢在我的头文件中看到神秘的常量。
我的硬件需要几个浮点常量(例如 130.7 microseconds
、0.2503 mJ
),我真的希望能够读取(和更改)我的常量,因为列出了零件数据表值。
在给定的时刻,我的硬件需要使用这个常量,例如,填充一个定时器重载值,而且,由于这些值是常量,我想要这样的东西:
// Header file.
static const int values_table[] =
{
_Time( 123.45 ), // 123.45 microseconds.
// ...
};
然后:
// Application source file.
int conv_to_timer( x ) { /* my calculations - all const. */ }
// ...
void my_code( void )
{
// ...
timer_reload = conv_to_timer( values_table[ index ] );
一种方法是让我的 _Time( x )
宏来完成计时器值所需的所有计算,但它不灵活(即无法与外面的东西相比较)既不便携(不同的硬件需要不同的计算).
有什么优雅的方法建议吗?
TL;DR: 使用宏。
标准 C 没有 C++ constexpr
的直接模拟。最接近的是宏和内联函数,在这两者中,宏可以以某些内联函数不能使用的方式使用——特别是,内联函数不能用于 C 需要常量表达式的地方(嗯……听起来很相似到你知道的任何 C++ 关键字?),但另一方面,宏可以扩展为合适的表达式。
constexpr
被引入到 C++ 中主要是为了提供一种替代宏的方法,其中需要 non-trivial 但 compile-time-computable 表达式。我相信它们也有一些优点,例如它们可能导致在编译时计算的值类型,但这些似乎与您的特定情况无关。在从未引入 constexpr
的 C 中,宏仍然是标准方法。
您反对使用宏的主要理由似乎主要是代码风格。您观察到 C++ constexpr
函数的 body 可以在与它的 "calls" 不同的翻译单元中,这显然对您很有吸引力。但是请注意,如果这是您的选择,那么您仍然需要在使用它们的翻译单元中至少 声明 此类函数,因此您实际上不会节省太多很多方面的清洁度。此外,虽然每个翻译单元必须包含它使用的每个宏的 body,您仍然可以将宏定义隔离到单独的 header.
总的来说,我不认为避免使用宏——如果可以的话——真的会给你带来任何好处。我倾向于认为 C++ 社区对宏的普遍厌恶是 constexpr
与任何功能优势一样重要的动机。如果碰巧你被这种特殊的厌恶所困扰,那么你真的需要克服它才能在标准 C 中有效地编程。
相信你的优化器,然后验证。
C++ constexpr
是基于编译器已经进行的优化而设计的。如果你写:
int fac( int n ) {
int r = 1;
for (int i = 2; i < n; ++i)
r*=i;
return r;
}
然后做
printf("%d", fac(10));
适度优化设置下的编译器将评估对 fac
的调用,并将调用替换为 fac(10)
.
的常量 return 值
constexpr
的存在主要是为了允许 fac(10)
在语义不变的地方使用,比如 C++ 中数组的大小。所以在 C++ 中你可以这样做:
char buffer[fac(10)];
当且仅当 fac(10)
是 constexpr
。 (C 具有可变大小的数组)。
您可以将 _Time
编写为宏,或者您可以将其编写为 inline
函数并告诉您的编译器进行优化。然后检查生成的二进制文件,看它是否存储了具有最终值的数组。
宏也可以使用,但宏有常见的调试问题(我不知道有哪个编译器可以在宏搞砸时为您提供体面的步进),这使得它们不太适合复杂的代码。
了解 C++ 为您提供 constexpr
函数在编译时实际计算的零保证。它只是允许您在其他上下文中使用它,并且大多数编译器会为您预先计算它。
简单地说,有没有一种 ANSI-C 方法可以使函数成为常量表达式?
- 纯 ANSI-C,但 GNU 扩展是可以接受的 - 但不是 C++。
- 最好不要依赖宏。
- 某些行为肯定类似于 C++
constexpr
并且不会在 运行 时解决。
背景:
我需要在没有浮点的嵌入式处理器中实现大量数学运算,因此我在我的应用程序中使用了定点。
不过,我不喜欢在我的头文件中看到神秘的常量。
我的硬件需要几个浮点常量(例如 130.7 microseconds
、0.2503 mJ
),我真的希望能够读取(和更改)我的常量,因为列出了零件数据表值。
在给定的时刻,我的硬件需要使用这个常量,例如,填充一个定时器重载值,而且,由于这些值是常量,我想要这样的东西:
// Header file.
static const int values_table[] =
{
_Time( 123.45 ), // 123.45 microseconds.
// ...
};
然后:
// Application source file.
int conv_to_timer( x ) { /* my calculations - all const. */ }
// ...
void my_code( void )
{
// ...
timer_reload = conv_to_timer( values_table[ index ] );
一种方法是让我的 _Time( x )
宏来完成计时器值所需的所有计算,但它不灵活(即无法与外面的东西相比较)既不便携(不同的硬件需要不同的计算).
有什么优雅的方法建议吗?
TL;DR: 使用宏。
标准 C 没有 C++ constexpr
的直接模拟。最接近的是宏和内联函数,在这两者中,宏可以以某些内联函数不能使用的方式使用——特别是,内联函数不能用于 C 需要常量表达式的地方(嗯……听起来很相似到你知道的任何 C++ 关键字?),但另一方面,宏可以扩展为合适的表达式。
constexpr
被引入到 C++ 中主要是为了提供一种替代宏的方法,其中需要 non-trivial 但 compile-time-computable 表达式。我相信它们也有一些优点,例如它们可能导致在编译时计算的值类型,但这些似乎与您的特定情况无关。在从未引入 constexpr
的 C 中,宏仍然是标准方法。
您反对使用宏的主要理由似乎主要是代码风格。您观察到 C++ constexpr
函数的 body 可以在与它的 "calls" 不同的翻译单元中,这显然对您很有吸引力。但是请注意,如果这是您的选择,那么您仍然需要在使用它们的翻译单元中至少 声明 此类函数,因此您实际上不会节省太多很多方面的清洁度。此外,虽然每个翻译单元必须包含它使用的每个宏的 body,您仍然可以将宏定义隔离到单独的 header.
总的来说,我不认为避免使用宏——如果可以的话——真的会给你带来任何好处。我倾向于认为 C++ 社区对宏的普遍厌恶是 constexpr
与任何功能优势一样重要的动机。如果碰巧你被这种特殊的厌恶所困扰,那么你真的需要克服它才能在标准 C 中有效地编程。
相信你的优化器,然后验证。
C++ constexpr
是基于编译器已经进行的优化而设计的。如果你写:
int fac( int n ) {
int r = 1;
for (int i = 2; i < n; ++i)
r*=i;
return r;
}
然后做
printf("%d", fac(10));
适度优化设置下的编译器将评估对 fac
的调用,并将调用替换为 fac(10)
.
constexpr
的存在主要是为了允许 fac(10)
在语义不变的地方使用,比如 C++ 中数组的大小。所以在 C++ 中你可以这样做:
char buffer[fac(10)];
当且仅当 fac(10)
是 constexpr
。 (C 具有可变大小的数组)。
您可以将 _Time
编写为宏,或者您可以将其编写为 inline
函数并告诉您的编译器进行优化。然后检查生成的二进制文件,看它是否存储了具有最终值的数组。
宏也可以使用,但宏有常见的调试问题(我不知道有哪个编译器可以在宏搞砸时为您提供体面的步进),这使得它们不太适合复杂的代码。
了解 C++ 为您提供 constexpr
函数在编译时实际计算的零保证。它只是允许您在其他上下文中使用它,并且大多数编译器会为您预先计算它。