2013-11-01 27 views
6

鑑於我想對某些數據執行過濾,我如何避免在運行時生成這些數據,但要保持更改這些過濾器的大小和數據分佈的靈活性,同時還要保留乾淨的可重用代碼。我知道我可以使用模板來完成類似以下內容:如何在編譯時靜態生成浮點數據?

template <int x> class Filter 
{ 
    static const float f; 
    static const Filter<x-1> next; 
    inline float* begin(const Filter<x>& el){ return &f;  } 
    inline float* end(const Filter<x>& el) { return (&f)+x+1; } 
}; 
template <> class Filter<0> 
{ 
    static const float f; 
    inline float* begin(const Filter<0>& el){ return &f; } 
    inline float* end(const Filter<0>& el) { return (&f)+1; } 
}; 

template <int x> const float Filter<x>::f = someDistribution(x); 
template <>  const float Filter<0>::f = someDistribution(0); 

這將在我的過濾器根據根據someDistribution(...)過濾器對象指數X的確產生的數據。然而,我的用途有一些缺點...

1)我認爲我是對的,雖然這些數據不是在對象構造上生成的,但它在程序啓動時會生成一次。 - 這是我可以容忍的,雖然寧願過濾器計算在comiletime,然後在那裏烘焙(這是甚至可能爲浮點數據?)

2)過濾器將不會實例化「下一個」成員,除非一個成員函數(即所謂的地方!)橫穿結構的長度,即

// inside template<int x> class Filter 
inline void instantiate() const { next.instantiate(); }; 
// then inside template<> class Filter<0> 
inline void instantiate() const { }; 

我必須做是錯誤的,要求暴跌實例功能,這個失敗易於維護的條款。

編輯:我在這裏關心的原因是我想確保next成員被實例化,所以我可以使用begin和end函數遍歷靜態'數組'。

所以,首先我該如何解決問題2,並用實例化功能做掉,其次是有可能解決的問題1,使這個數據是在編譯時動態生成和支持。

(NB我在類似的問題上,我使用了python預編譯腳本來生成包含過濾器數據的源文件,但我不想在這裏使用它,因爲這是它自己的水壺!)

+2

我不認爲你的通用'end'函數是合法的。向非數組變量的地址添加多於1應該是非法的。 –

+0

所以這應該是可以的,因爲'float f'應該在內存中都是連續的,這樣你就可以將指針指向第一個並迭代到最後。重要的是,最終迭代器需要可解引用,並且保持指向未初始化內存的指針是合法的。 我同意我可以通過編寫'((float *)(&f))+ x + 1'來更加清楚,但是我覺得使用上面使用的簡短格式更清楚其餘代碼段的含義。感謝您檢查。 – geoff3jones

回答

2

既然你不能使用contexpr ...關於你的問題:

  1. 除非你正在尋找下一個最大的素數,你不應該打擾簡單的計算在啓動時發生一次。嘗試測量它,並且很可能您會發現初始化在不到一毫秒的時間內完成。

    也就是說,啓動時計算的值表現爲變量(每次使用時都必須爲asked),而編譯時常量的值始終是已知的。因此前者可能會慢一點,但可能沒有任何意義。

    在引入不便之前,請務必先測量。

  2. 再次,你爲什麼在意?如果Filter類型爲特定x未在代碼中的任何位置使用,爲什麼值應該在某處?

    如果模板靜態相互依賴,則會產生問題 - 在您的情況下,它們不會,每個f都是自治的。

說了這麼多,鼓搗圍繞一個偉大的工具是http://gcc.godbolt.org/ - 你看裝配爲你鍵入。它不支持MS編譯器,但它可以讓你很好地猜測編譯器如何優化東西。

如果您disrtibution是很簡單的做個宏這將是一個編譯時間常數:

#define someDistribution(x) x * x 

template <int x> struct Filter 
{ 
    static const float f; 
}; 

template <int x> const float Filter<x>::f = someDistribution(x); 

int main() 
{ 
    return Filter<200>::f + Filter<100>::f; 
} 

的組件(鏘):

main:         # @main 
    movl $50000, %eax   # imm = 0xC350 
    ret 

如果更改someDistribution是一個函數,即使是內聯函數,你也會看到計算必須發生。

編輯:記住,你可以用宏來做maaany事情,包括對某些值「專門化」它們。簡單的分佈應該是預處理友好的。

+0

我更新了我的答案。 – gwiazdorrr

+0

謝謝,這回答我的問題,並指出我在正確的方向。還有關於「爲什麼?」在第二部分中,我認爲我是模棱兩可的(並且會在上面修正),但我希望能夠使用begin()end()來訪問數據,但是如果所有'next'成員都沒有被實例化,這當然會創建垃圾。 哦,你修補工具鏈接 - 真棒! :) – geoff3jones

-1

您可以獲得使用可變模板的難題。一旦將integer_sequence支持添加到標準庫中,您可以使用它而不是seq/gen_seq。

​​

爲了讓這個編譯時間,而不是在啓動時初始化,雖然你真的想要VS 2013沒有的constexpr支持。這裏的constexpr version

#include <array> 
#include <iostream> 

using namespace std; 

template<size_t... Is> struct seq {}; 
template<size_t N, size_t... Is> struct gen_seq : gen_seq<N - 1, N - 1, Is...> {}; 
template <size_t... Is> struct gen_seq<0, Is...> : seq<Is...>{}; 

template<typename Func, size_t... Is> 
constexpr array<float, sizeof...(Is)> make_coeffs(Func f, seq<Is...>) { 
    return array<float, sizeof...(Is)>{ f(Is)... }; 
} 

constexpr float square(float x) { return x * x; } 

int main() { 
    constexpr auto coeffs = make_coeffs(square, gen_seq<10>{}); 
    static_assert(coeffs[3] == 9, ""); 
    for (float x : coeffs) { 
     cout << x << " "; 
    } 
    cout << endl; 
}