尽可能在编译时执行功能协定

时间:2018-12-26 14:53:25

标签: c++

(此问题的灵感来自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;
}

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

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

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

  • 使函数成为一个以参数作为模板参数的模板-这不能解决问题,因为它使函数不适合运行时参数
  • 使函数constexpr不能解决问题,因为即使编译器看到未定义的行为,它们也不会在我的测试中生成诊断信息,而是gcc插入了ud2指令,不是我想要的。

3 个答案:

答案 0 :(得分:4)

常量表达式中用于以下情况时,我在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>;

可能与UDLoperator "" _c一起拥有5_c42_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),因此使用MACRO,我们可以执行调度:

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

Demo

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

答案 1 :(得分:2)

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);

所有其他字符-运行时段错误(如果未使用nullptr,则为ub)

答案 2 :(得分:0)

这不是完美的,它要求我们在两个不同的地方使用参数,但它'有效'

server {
        listen 80 default_server;
        listen [::]:80;

        root /var/www/html;

        server_name example.com;

        location /static {
            alias /var/www/html/static/;
        }

        location / {
            try_files $uri @wsgi;
        }

        location @wsgi {
            proxy_pass http://unix:/tmp/gunicorn.sock;
            include proxy_params;
        }

        location ~* .(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
            access_log off;
            log_not_found off;
            expires max;
        }
}

我们可以这样称呼它:

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;
}