2016-12-30 98 views
0

我想產生這樣的功能:使用表達式模板編譯時間數組索引 - constexpr?

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

但我想,以確保該widthoffset秒,甚至可能weight s爲編譯/時間常數。

也就是說可以通過定義一個類來實現:

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

這看起來不錯,但現在,該陣列不一定編譯時間已知。如果我像上面用表達式中的文字那樣編寫它,我已經驗證了編譯器可以進行正確的優化。但是我想通過保證找到一種方法,它們總是編譯時間常量。

我相信我能編碼offsetAccessorWeightedTerm模板參數,但我不能看我怎麼能做到這一點,並保持所需的表達式語法,因爲運營商()主罰偏移作爲常規參數。

所以,問題是,如果有一種方法來實現這一目標?我有一種感覺,constexpr可能在這裏使用,我有點不熟悉。

回答

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

類似的(但更復雜的)技術可被用於傳遞偏移的包。重量是一團糟,因爲雙重支持的編譯時間很少。

+0

有趣。我認爲這將隨後用於像這樣:'const的模版<3> S(1 * U [M1] + 2 * U [Z] + 1 * U [P1]);'。正確?此外,「用戶定義的文字」 - 你指的是什麼? – kalj

+0

@kalj請在谷歌之前askimg關於它。你可以將'_intk'作爲參數傳遞給'apply_stencil',但是它們的值會保證編譯時間。 – Yakk