代码之家  ›  专栏  ›  技术社区  ›  Cyan

在正确位置获取编译警告(`gcc`)

  •  1
  • Cyan  · 技术社区  · 6 年前

    this example powered by Godbolt ,转载如下, 可以创建一个函数,它需要它的参数来满足某些条件 (i >= 0) , ptr != NULL 等等。

    // EXPECT() : will trigger a warning if the condition is not guaranteed
    #define EXPECT(c)  (void)((c) ? (void)0 : never_reach())
    
    // assume() : tell the compiler that `cond` is true
    // note : only works for simple conditions, such as `i>=0`.
    // Avoid complex conditions invoking functions.
    #define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
    
    
    // *********************************************************
    // Function declaration
    // *********************************************************
    
    int positive_plus1_body(int v); 
    #define positive_plus1_preconditions(v)   ((v)>=0)     // Let's first define the preconditions. 
                                                           // Name is long, because conditions must be unique to the function
    
    // The inlined function express the contract, with both pre and post conditions.
    // `inline` is essential, so that actual input values get verified,
    // and result gets enriched with `assume` conditions.
    // Problem is, the error is now located INTO the inlined function, not at the place where it's called.
    // This makes it very hard to identify where the problem happens.
    static inline int positive_plus1(int v)                // Ideally, this function should also have additional attribute to not trigger warnings when it's not invoked
    {
        int r;
        EXPECT(positive_plus1_preconditions(v));           // preconditions
        r = positive_plus1_body(v);
        assume(r > 0);                                     // postconditions
        return r;
    }
    

    这个 Godbolt 通过实例,可以用正确和错误的参数调用函数,并观察对编译诊断的影响。

    如果调用者不遵守这些条件,编译器将生成一个警告。这对于立即检测条件冲突非常方便。通常的替代方法是 assert() 在函数体内部,但依赖运行时测试来检测潜在的条件冲突,这不太直接,也不太确定。

    问题是,如果 inline 使用错误的参数调用函数时,编译器将查找条件冲突 里面 这个 内联 功能。这种诊断方法用处不大。相反,我们要读 内联 函数的参数不正确。

    解决方法是改用宏。但是,宏会触发著名的多重求值问题,因为它的参数必须多次使用。这是危险的足以避免它。此外,封装 内联 函数可以用宏自动生成,但宏不能由另一个宏生成。

    另一个解决办法是使用 statement expression 解决了多评价问题。但是,它是不可移植的,而且它仍然是一个宏,因此不能由另一个宏生成。

    最好还是保持原样 内联 如果可能的话。但是在违反条件的地方生成正确的行号是非常重要的。 有没有一种方法可以在保持 内联 函数方法?

    0 回复  |  直到 6 年前