我想生成一个这样的函数:
double apply_stencil(const double *u, const int i, const width,
const int *offset, const double *weight)
{
double t=0;
for(int j=0; j<width; j++)
t += u[i+offset[j]] * weight[j];
return t;
}
但我想确保width
,offset
,甚至weight
是编译/时间常量。
通过定义类型可以实现:
template <typename S>
double apply_stencil(const double *u, const int i)
{
double t=0;
for(int j=0; j<S::width; j++)
t += u[i+S::offset[j]] * S::weight[j];
return t;
}
// then:
struct Stencil {
const static double weight[];
const static int offset[];
const static unsigned int width = 3;
};
const double Stencil::weight[] = {1.0, 2.0, 1.0};
const int Stencil::offset[] = {-1, 0, 1};
但是,这不是很漂亮。我希望用户能够在他们的应用程序代码中指定Stencil
,然后从我的头文件中调用我的apply_stencil
函数(这实际上是对更复杂的东西的简化)。
理想情况下,我希望使用表达式模板指定事物,如下所示:
const Symbol u;
const Stencil<3> s (1*u[-1] + 2*u[0] + 1*u[1]);
使用此基础架构:
struct Accessor {
int offset;
};
struct Symbol
{
Accessor operator[](int i) const {
return Accessor{i};
}
};
struct WeightedTerm
{
double weight;
int offset;
WeightedTerm()
: weight(1), offset(0) {}
WeightedTerm(double a, Accessor u)
: weight(a), offset(u.offset) {}
};
WeightedTerm operator*(double a, Accessor u) {
return WeightedTerm(a,u);
}
template <int n>
struct Sum
{
WeightedTerm terms[n];
Sum(WeightedTerm x, WeightedTerm y) {
terms[0] = x;
terms[1] = y;
}
Sum(Sum<n-1> x, WeightedTerm y) {
for(int i = 0; i < n-1; ++i)
terms[i] = x.terms[i];
terms[n-1] = y;
}
};
Sum<2> operator+(WeightedTerm x, WeightedTerm y) {
return Sum<2>(x,y);
}
template <int n>
Sum<n+1> operator+(Sum<n> x, WeightedTerm y) {
return Sum<n+1>(x,y);
}
template <int width>
struct Stencil
{
double weights[width];
int offsets[width];
Stencil(const Sum<width> s) {
for(int j = 0; j < width; ++j) {
weights[j] = s.terms[j].weight;
offsets[j] = s.terms[j].offset;
}
};
};
这看起来不错,但现在,数组不一定是已知的编译时间。如果我像上面那样用表达式中的文字来编写它,我已经验证了编译器可以进行正确的优化。但我想找到一种方法来保证它们总是编译时间常量。
我认为我可以将offset
编码为Accessor
和WeightedTerm
中的模板参数,但我无法看到我如何做到这一点并保留所需的表达式语法因为operator()将偏移量作为常规参数。
所以,问题是,是否有办法实现这一目标?我觉得constexpr
可以在这里使用,我有点不熟悉。
答案 0 :(得分:1)
使用std::integral_constant
别名。如果您想要易用,请使用用户定义的文字。
constexpr int int_from_chars(){return 0;}
constexpr int k_pow(unsigned int x, unsigned int b=10){
return (x==0)?1:(k_pow(x-1,b)*b);
}
template<class...Chars>
constexpr int int_from_chars(char x, Chars...chars){
return k_pow(sizeof...(Chars))*(x-'0') + int_from_chars(chars...);
}
template<char...Cs>
constexpr auto operator""_kint(){
return std::integral_constant<int, int_from_chars(Cs...)>{};
}
或某些人。需要抛光。
现在7_kint
是编译时编码为7的类型。
您可以将integral_constant<int, K>
作为函数参数,其中K
是模板非类型参数。
延伸到八进制/十六进制/二进制作为练习。
template<int width>
double apply_stencil(const double *u, const int i, std::integral_constant<int,width>,
const int *offset, const double *weight)
{
double t=0;
for(int j=0; j<width; j++)
t += u[i+offset[j]] * weight[j];
return t;
}
通过
调用apply_stencil( ptr, i, 7_kint, poffsets, pweights);
可以使用类似(但更复杂)的技术来传递偏移量。重量是一团糟,因为很少有编译时双重支持。