【问题标题】:Compile time check AND runtime check 'at the same time'编译时间检查和运行时检查'同时'
【发布时间】:2020-01-07 04:49:14
【问题描述】:

假设我有以下简化程序:

Link to godbolt.org:

#include <cassert>

struct Dimensions {

    Dimensions& operator=(int i) {
      assert(i != 0);
      return *this;
    }

};

int getDim();

int main() {
    Dimensions dims;
    dims = getDim();//ok, just use runtime assert
    dims = 0;//compile error wanted here
    return 0;
}

在第一种情况 (getDim) 中,无法检查编译时,因此我们很乐意仅在运行时进行检查。

但是,当理论上看起来有可能时,是否也可以在编译时检测到(对于第二种情况,dims = 0;)? (甚至可能带有某种重载或包装器?)

【问题讨论】:

  • @george_ptr 总是会给出编译时错误,因为i 在该上下文中的常量表达式中不可用。
  • 草率地说,在dims = 0; 中,0 是一个运行时值。只有当您可以接受将其转换为编译时值时,您才能对其进行静态断言(例如,作为模板参数,如 StackDanny 建议的那样)
  • @Bathsheba ty,这是个好主意,但是我不能在实际情况下使用它甚至不是尺寸(我只是认为这样可以更好地传达含义)
  • @darune:不确定我应该 - 我认为这太特殊了,我不会自己做,我认为运行时断言就足够了。
  • 想让它与std::is_constant_evaulated() 一起工作,但它的行为很奇怪,也许其他人能够修复它godbolt

标签: c++ c++17 compile-time static-assert


【解决方案1】:

在 C 中使用 gcc 编译器执行的一种典型方法,在 C++ 中也可以使用:您使用 __builtin_constant_p 内置函数来检查表达式是否是不断求值的,然后检查表达式,然后调用用 @ 声明的函数987654323@ 或__attribute__((__error__))。像这样:

#include <cassert>
#include <type_traits>

#define CONCAT(a, b) a ## b
#define XCONCAT(a, b) CONCAT(a, b)
#define maybe_static_maybe_not_assert(expr) do { \
    if (__builtin_constant_p(expr)) { \
            if (!(expr)) { \
                extern __attribute__((__warning__( \
                "static_assert: expression: " #expr " will fail on runtime!" \
                ))) void XCONCAT(maybe_static_maybe_not_assert_warn, __LINE__)(); \
              XCONCAT(maybe_static_maybe_not_assert_warn, __LINE__)(); \
         } \
    } \
    assert(expr); \
} while(0)

struct Dimensions {

    Dimensions& operator=(int i) {
        maybe_static_maybe_not_assert(i != 0);
        return *this;
    }

};

int getDim();

int main() {
    Dimensions dims;
    dims = getDim();
    dims = 0;
    dims = 1;
    return 0;
}

使用优化编译时应该发出警告:

In member function 'Dimensions& Dimensions::operator=(int)',
    inlined from 'int main()' at <source>:32:12:
<source>:12:70: warning: call to 'maybe_static_maybe_not_assert_warn21' declared with attribute warning: static_assert: expression: i != 0 will fail on runtime! [-Wattribute-warning]
   12 |                 XCONCAT(maybe_static_maybe_not_assert_warn, __LINE__)(); \
      |                                                                      ^
<source>:21:9: note: in expansion of macro 'maybe_static_maybe_not_assert'
   21 |         maybe_static_maybe_not_assert(i != 0);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 0

这是_FORTIFY_SOURCEglibc 中实现的方式。

【讨论】:

  • msvc 也可以这样吗? (或铿锵声?)
  • clang 支持 gnu 扩展,因此它应该适用于 clang,但是您必须将 __attribute__((__warning__)) 更改为 __attribute__((__deprecated__))。 clang 无法识别warningerror 属性,但有一个deprecated 属性,通常使用。
  • 我想这对 MSVC 来说是不可能的吗?并且 MS 方面没有计划支持它(?)
  • 我对 MSVC 没有经验,所以不打算回答。我从未见过在 MSVC 中执行 __builtin_constant_p 的方法。
  • @KamilCuk :在clang__attribute__((__deprecated__)) 上如何工作?似乎在任何时候在源代码中调用函数时都会触发警告,而不管它是否会被调用,因此忽略了 DCE 优化。
猜你喜欢
  • 2021-11-05
  • 2011-09-30
  • 1970-01-01
  • 2020-02-07
  • 2015-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多