尽可能在编译时执行功能协定 [英] Enforcing function contract at compile time when possible

查看:111
本文介绍了尽可能在编译时执行功能协定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(此问题是受比方说,我们有一个单参数 foo ,其语义定义为

Let's say, we have a single-argument foo, semantically defined as

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

    return *parg;
}

上面的整个代码用于说明一个简单的想法-函数将返回它自己的参数,除非该参数等于5,在这种情况下,行为是不确定的.

The whole code above is used to illustrate a simple idea - function returns it's own argument unless the argument is equal to 5, in which case behavior is undefined.

现在,挑战在于-以某种方式修改函数,如果在编译时知道其参数,则应生成编译器诊断(警告或错误),否则,行为在运行时仍未定义.解决方案可能取决于编译器,只要四大编译器之一都可用.

Now, the challenge - modify the function in such a way, that if it's argument is known at compile time, a compiler diagnostic (warning or error) should be generated, and if not, behavior remains undefined in runtime. Solution could be compiler-dependent, as long as it is available in either one of the big 4 compilers.

以下是一些无法解决问题的潜在路线:

Here are some potential routes which do not solve the problem:

  • 使函数成为一个以参数作为模板参数的模板-这不能解决问题,因为它使函数不适合运行时参数
  • 使函数成为 constexpr -这不能解决问题,因为即使编译器看到未定义的行为,它们也不会在我的测试中产生诊断-而是gcc插入 ud2 指令,这不是我想要的.
  • Making function a template which takes it's argument as a template parameter - this doesn't solve the problem because it makes function ineligible for run-time arguments
  • Making function a constexpr - this doesn't solve the problem, because even when compilers see undefined behavior, they do not produce diagnostics in my tests - instead, gcc inserts ud2 instruction, which is not what I want.

推荐答案

常量表达式中用于

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

演示

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

We cannot know that argument value is known at compile type, but we can use type representing value with std::integral_constant

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

可能使用 UDL 运算符" _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 在标准中似乎不可能来允许分派,但是某些编译器为此提供了内置功能( __ builtin_constant_p ),因此使用MACRO,我们可以执行分派:

As I said, arguments are not known to be constant inside the function, and is_constexpr seems not possible in standard to allow dispatch, but some compiler provide built-in for that (__builtin_constant_p), so with MACRO, we can do the dispatch:

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

演示

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

Note: Cannot use foo(int_c<X>{}) directly, even in if constexpr, as there is still some syntax check.

这篇关于尽可能在编译时执行功能协定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆