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

尽可能在编译时执行函数契约

c++
  •  13
  • SergeyA  · 技术社区  · 6 年前

    (这个问题的灵感来自 How can I generate a compilation error to prevent certain VALUE (not type) to go into the function? )

    比如说,我们只有一个论点 foo ,语义上定义为

    int foo(int arg) {
        int* parg;
        if (arg != 5) {
            parg = &arg;
        }
    
        return *parg;
    }
    

    上面的整个代码用于说明一个简单的IDEA-函数返回它自己的参数,除非参数等于5,在这种情况下,行为是未定义的。

    现在,挑战是修改函数的方式,如果在编译时知道它的参数,那么应该生成编译器诊断(警告或错误),如果不知道,那么在运行时行为仍然是未定义的。解决方案可能依赖于编译器,只要它在四大编译器中的任何一个中都可用。

    以下是一些无法解决问题的潜在途径:

    • 使函数成为一个将其参数作为模板参数的模板-这并不能解决问题,因为它使函数不适合运行时参数
    • 使功能A constexpr -这并不能解决问题,因为即使编译器看到未定义的行为,它们也不会在我的测试中生成诊断——相反,gcc插入 ud2 指令,这不是我想要的。
    3 回复  |  直到 6 年前
        1
  •  4
  •   Jarod42    6 年前

    我有错误 constexpr 当用于 常量表达式 为:

    constexpr int foo(int arg) {
        int* parg = nullptr;
        if (arg != 5) {
            parg = &arg;
        }
        return *parg;
    }
    

    Demo

    我们不知道参数值在编译类型中是已知的,但是我们可以使用表示值的类型 std::integral_constant

    // alias to shorten name. 
    template <int N>
    using int_c = std::integral_constant<int, N>;
    

    可能与 UDL 具有 operator "" _c 拥有 5_c , 42_c .

    然后,添加过载:

    template <int N>
    constexpr auto foo(int_c<N>) {
        return int_c<foo(N)>{};
    }
    

    所以:

    foo(int_c<42>{}); // OK
    foo(int_c<5>{}); // Fail to compile
    
    // and with previous constexpr:
    foo(5); // Runtime error, No compile time diagnostic
    constexpr auto r = foo(5); // Fail to compile
    

    正如我所说,参数在函数内部不是常量,并且 is_constexpr seems not possible in standard 允许调度,但某些编译器提供了内置的( __builtin_constant_p )因此,使用宏,我们可以执行分派:

    #define FOO(X) [&](){ \
        if constexpr (__builtin_constant_p(X)) {\
            return foo(int_c<__builtin_constant_p (X) ? X : 0>{});\
        } else {\
            return foo(X); \
        } \
    }()
    

    Demo

    注意:不能使用 foo(int_c<X>{}) 直接,即使在constexpr中,因为仍然有一些语法检查。

        2
  •  2
  •   Iłya Bursov    6 年前

    gcc/clang/intel编译器支持 __builtin_constant_p ,这样您就可以使用:

    template <int D>
    int foo_ub(int arg) {
        static_assert(D != 5, "error");
        int* parg = nullptr;
        if (arg != 5) {
            parg = &arg;
        }
    
        return *parg;
    }
    
    #define foo(e) foo_ub< __builtin_constant_p(e) ? e : 0 >(e)
    

    这些语句产生编译时错误:

    • foo(5)
    • foo(2+3)
    • constexpr int i = 5; foo(i);

    而所有其他-运行时segfault(或ub,如果没有 nullptr 使用)

        3
  •  0
  •   Fureeish    6 年前

    这不完美 它要求我们在两个不同的地方使用论点 但是 “作品” :

    template<int N = 0>
    int foo(int arg = 0) {
        static_assert(N != 5, "N cannot be 5!");
        int* parg;
        if (arg != 5) {
            parg = &arg;
        }
    
        return *parg;
    }
    

    我们可以这样称呼它:

    foo<5>();   // does not compile
    foo(5);     // UB
    foo<5>(5);  // does not compile
    foo<5>(10); // does not compile
    foo<10>(5); // UB
    foo();      // fine
    foo<10>();  // fine
    foo(10);    // fine