如何确保constexpr函数在运行时永远不会被调用
How to ensure constexpr function never called at runtime?
假设您有一个函数为您的应用程序生成一些安全令牌,例如一些散列盐,或者可能是一个对称或非对称密钥。
现在让我们假设你在c++中有这个函数作为constexpr,并且你根据一些信息(比如,构建号,时间戳,其他东西)为你的构建生成键。
作为一个勤奋的程序员,你要确保以适当的方式调用它,以确保它只在编译时被调用,因此dead stripper会从最终的可执行文件中删除代码。
然而,你永远不能确定其他人不会以不安全的方式调用它,或者编译器不会剥离函数,然后你的安全令牌算法将成为公共知识,使潜在攻击者更容易猜测未来的令牌。
或者,撇开安全不谈,假设这个函数需要很长时间来执行,而你想确保它不会在运行时发生,从而给最终用户带来糟糕的用户体验。
是否有任何方法可以确保constexpr函数永远不会在运行时被调用?或者,在运行时抛出断言或类似的错误也可以,但不像编译错误那样明显。我听说有一些方法涉及抛出一个不存在的异常类型,所以如果constexpr函数没有死剥离,你会得到一个链接器错误,但听说这只适用于某些编译器。
远相关问题:强制constexpr在编译时求值
在c++ 20中你可以用consteval
代替constexpr
来强制一个函数总是在编译时求值。
的例子:
int rt_function(int v){ return v; }
constexpr int rt_ct_function(int v){ return v; }
consteval int ct_function(int v){ return v; }
int main(){
constexpr int ct_value = 1; // compile value
int rt_value = 2; // runtime value
int a = rt_function(ct_value);
int b = rt_ct_function(ct_value);
int c = ct_function(ct_value);
int d = rt_function(rt_value);
int e = rt_ct_function(rt_value);
int f = ct_function(rt_value); // ERROR: runtime value
constexpr int g = rt_function(ct_value); // ERROR: runtime function
constexpr int h = rt_ct_function(ct_value);
constexpr int i = ct_function(ct_value);
}
prec ++20解决方案
你可以在常量表达式中强制使用它:
#include<utility>
template<typename T, T V>
constexpr auto ct() { return V; }
template<typename T>
constexpr auto func() {
return ct<decltype(std::declval<T>().value()), T{}.value()>();
}
template<typename T>
struct S {
constexpr S() {}
constexpr T value() { return T{}; }
};
template<typename T>
struct U {
U() {}
T value() { return T{}; }
};
int main() {
func<S<int>>();
// won't work
//func<U<int>>();
}
通过使用函数的结果作为模板参数,如果在编译时无法解决,则会得到错误。
一个理论解决方案(作为模板应该是图灵完备的)-不要使用constexpr函数,而只使用struct template with values
返回到好的旧std=c++0x
风格的计算。例如,不要做
constexpr uintmax_t fact(uint n) {
return n>1 ? n*fact(n-1) : (n==1 ? 1 : 0);
}
,
template <uint N> struct fact {
uintmax_t value=N*fact<N-1>::value;
}
template <> struct fact<1>
uintmax_t value=1;
}
template <> struct fact<0>
uintmax_t value=0;
}
struct
方法保证在编译时独占地求值。
boost的家伙设法做一个编译时解析器的事实是一个强烈的信号,尽管繁琐,这种方法应该是可行的-这是一次性的成本,也许可以认为它是一种投资。
例如:
to power structure:
// ***Warning: note the unusual order of (power, base) for the parameters
// *** due to the default val for the base
template <unsigned long exponent, std::uintmax_t base=10>
struct pow_struct
{
private:
static constexpr uintmax_t at_half_pow=pow_struct<exponent / 2, base>::value;
public:
static constexpr uintmax_t value=
at_half_pow*at_half_pow*(exponent % 2 ? base : 1)
;
};
// not necessary, but will cut the recursion one step
template <std::uintmax_t base>
struct pow_struct<1, base>
{
static constexpr uintmax_t value=base;
};
template <std::uintmax_t base>
struct pow_struct<0,base>
{
static constexpr uintmax_t value=1;
};
构建标记
template <uint vmajor, uint vminor, uint build>
struct build_token {
constexpr uintmax_t value=
vmajor*pow_struct<9>::value
+ vminor*pow_struct<6>::value
+ build_number
;
}
在即将到来的c++ 20中,将有consteval
说明符。
consteval——指定函数是直接函数,也就是说,每次调用该函数都必须产生一个编译时常数
现在我们有c++ 17了,有一个更简单的解决方案:
template <auto V>
struct constant {
constexpr static decltype(V) value = V;
};
关键是非类型参数可以声明为auto
。如果你使用c++ 17之前的标准,你可能不得不使用std::integral_constant
。还有一个关于constant
助手类的建议。
一个例子:
template <auto V>
struct constant {
constexpr static decltype(V) value = V;
};
constexpr uint64_t factorial(int n) {
if (n <= 0) {
return 1;
}
return n * factorial(n - 1);
}
int main() {
std::cout << "20! = " << constant<factorial(20)>::value << std::endl;
return 0;
}
让你的函数接受模板形参而不是实参,并在lambda中实现你的逻辑。
#include <iostream>
template< uint64_t N >
constexpr uint64_t factorial() {
// note that we need to pass the lambda to itself to make the recursive call
auto f = []( uint64_t n, auto& f ) -> uint64_t {
if ( n < 2 ) return 1;
return n * f( n - 1, f );
};
return f( N, f );
}
using namespace std;
int main() {
cout << factorial<5>() << std::endl;
}
- CMake-按正确顺序将项目与C运行时对象文件链接
- 我在c++代码中生成了一个运行时#3异常
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 删除指向指针的指针是运行时错误吗
- 如何用参数值调用函数(仅在运行时已知)
- 为什么即使使用-cudart-static进行编译,库用户仍然需要链接到cuda运行时
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- c++中的指针和运行时错误
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 建议在运行时将带有类实例的列表从c++导入qml
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- 如何在GTK程序运行时禁用屏幕保护程序/电源管理/屏幕消隐
- 为什么是谷神星协方差.计算()似乎永远运行而不返回?
- 在同一模拟中使用静脉和静脉_ inet内容时出现运行时错误
- 读取文件时运行时的未知行为
- 函数在Windows或Linux上运行时表现不同
- 在声明中合并两个常量"std::set"(不是在运行时)
- 有没有一种方法可以在编译时加载到omp.h中,因此机器永远不需要在运行时获取它
- 如何确保constexpr函数在运行时永远不会被调用