ANSI-C常量表达式函数如C ++ constexpr?

时间:2017-08-01 16:18:03

标签: c c++11 const ansi-c

简单来说,有一种ANSI-C方法可以使函数成为常量表达式吗?

  1. 纯ANSI-C但GNU扩展是可以接受的 - 但是没有C ++。
  2. 最好不依赖宏。
  3. 肯定会像C ++ constexpr那样表现的东西,并且不会在运行时解决。
  4. 背景

    我需要在没有浮点的嵌入式处理器中实现大量的数学运算,所以我在我的应用程序中使用了固定点。

    我不喜欢在我的头文件中看到神秘的常量。 我的硬件需要几个浮点常量(例如130.7 microseconds0.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 )宏来完成计时器值所需的所有计算,但它不灵活(即不能与某些外部相比),既不是便携式(不同的硬件也需要不同的计算)。

    对优雅方法有任何建议吗?

2 个答案:

答案 0 :(得分:5)

TL; DR:使用宏。

标准C没有C ++ constexpr的直接模拟。最接近的是宏和内联函数,在这两者中,宏可以在某些方面使用内联函数不能使用 - 特别是,在C需要常量表达式的情况下不能使用内联函数(嗯...听起来类似对于你知道的任何C ++关键字?),但另一方面,宏可以扩展为合适的表达式。

constexpr被引入C ++主要是为了提供宏的替代方法,其中需要一个非平凡但可编译时可计算的表达式。我相信它们也有一些优点,例如它们可以导致在编译时计算的值的类型,但那些似乎与您的特定情况无关。在C中,从未引入constexpr,宏仍然是标准的方法。

您对使用宏的主要反对意见似乎主要与代码风格有关。您观察到C ++ constexpr函数的主体可以在与“调用”不同的转换单元中,这显然对您有吸引力。但请注意,如果这是您的选项,那么在使用它们的翻译单元中,您仍然需要至少声明这些函数,因此您实际上并没有节省太多清洁。此外,尽管每个翻译单元必须包含它使用的每个宏的主体,但您仍然可以将宏定义分隔为单独的标题。

总的来说,我没有看到避免使用宏 - 如果可以的话 - 会真正获得任何东西。我倾向于认为C ++社区对宏的普遍厌恶与constexpr的任何功能性好处一样大。如果碰巧你受到特定厌恶的折磨,那么你真的需要克服它以便在标准C中有效地编程。

答案 1 :(得分:2)

相信你的优化器,然后验证。

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函数。它只是允许您在其他上下文中使用它,大多数编译器都会为您预先计算它。