[英]Programmatic initialization of a long array at compile or initialization time
這是一個代碼片段,說明了我的問題:
const float D = 0.1F;
const float A[4] = {sin(0*D), sin(1*D), sin(2*D), sin(3*D)};
想象一下,全局數組A
要長得多,而且你不想做所有這種重復輸入。 在編譯或初始化時是否有更短的方法來初始化數組A,即無需編寫初始化函數並在程序中的某處調用它?
您可以在動態初始化時間內初始化A,如下所示:
const float *init_a(float x_)
{
static float data[4];
for(unsigned i=0; i<4; ++i)
data[i]=sin(i*x_);
return data;
}
const float D=0.1f;
const float *A=init_a(D);
您可以使用代碼生成器生成初始化代碼。 也就是說,編寫將為您編寫初始化代碼的程序。 實際上,您可以在生成時計算值。
請記住,C ++允許放置,
最后一個元素之后。 也沒有必要指定數組大小。 這兩件事應該可以輕松編寫發電機。
這個簡單的python代碼應該運行良好:
from math import sin
print('const float A[', N, '] = {')
for i in range(N):
print('\t', sin(i*D), ',', sep='')
print('};')
好吧我剛剛意識到這實際上沒有回答這個問題,因為它指定“無需編寫初始化函數並在程序中的某個地方調用它?” 但我想不出一個方便的選擇。
template<size_t N>
std::array<float, N> GetLookupTable(float d)
{
std::array<float, N> table;
// .. populate table here
return table;
}
// a global somewhere
(static?) const auto Table_10x5 = GetLookupTable<10>(5.0f);
第一部分在C ++ 14中已經過時,但不長:
template<unsigned...>struct indexes { using type=indexes; };
template<unsigned Max, unsigned...Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...>{};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Max>using make_indexes_t=typename make_indexes<Max>::type;
這是一些模板元編程,它允許我們創建和傳遞索引包。
然后一些代碼生成數組:
namespace details {
template<std::size_t N, unsigned...Is>
std::array<float, N> poly_sin(float src, indexes<Is...>) {
return { (Is*sin(src))... };
}
}
template<std::size_t N>
std::array<float, N> poly_sin(float src) {
return details::poly_sin<N>( src, make_indexes_t<N>{} );
}
第一種方法采用indexes<Is...>
,我們計划Is...
是0, 1, 2, ..., N-1
。 然后它將參數包擴展為正確大小的std::array
。
make_indexes_t<N>{}
擴展(在編譯時) indexes<0, 1, 2, ..., N-1>{}
,然后傳遞給details::poly_sin
,然后可以推導出Is...
和自身內部使用它們。
和使用點:
const float D = 0.1F;
const auto A = poly_sin<4>(D);
如果你有一個constexpr
sin
函數,你甚至可以使poly_sin
成為constexpr
函數,並且基本保證在編譯時進行評估。
如果是這種情況,請使用兩個poly_sin
函數制作D
constexpr
並使其相同。
如上所述,這發生在動態初始化時。
雖然看起來數組被復制了兩次,但RVO elision意味着任何體面的編譯器都會直接在A
構造它。
如果您希望能夠一般地執行此操作,請首先從上面的indexes
代碼開始。 然后添加:
template<class Sig>using result_of_t=typename std::result_of<Sig>::type;
namespace details {
template<std::size_t N, class F, unsigned... Is>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f, indexes<Is...> ) {
return { f( Is )... };
}
}
template<std::size_t N, class F>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f ) {
return details::make_array( std::forward<F>(f), make_indexes_t<N>{} );
}
const auto A = make_array<4>( [](unsigned i){ return float(i*sin(D)); } );
它使用lambda傳遞重復的代碼來構建數組。 遺憾的是,lambdas默認不是constexpr
所以你不能在編譯時這樣做。
您可以使用boost.preprocessor ,特別是BOOST_PP_ENUM
宏,如下例所示:
#include <iostream>
#include <cmath>
#include <boost/preprocessor/repetition/enum.hpp>
#define SIZE 4
#define D 0.1
#define ORDER(z, n, text) std::sin(n * D)
double const A[SIZE] = { BOOST_PP_ENUM(SIZE, ORDER, ~) };
int main() {
for(auto i : A) std::cout << i << std::endl;
}
或者,您可以使用std::array
而不是原始數組,並通過使用模板元編程在編譯時生成std::array
。 像下面的例子:
template<typename T, typename F, int SIZE, int... N>
constexpr std::array<T, SIZE>
genarray(F f) {
return std::array<T, SIZE>{{ f(N)... }};
}
template<typename T, typename F, int SIZE, int...> struct recursive_gen;
template<typename T, typename F, int SIZE, int... Args>
struct recursive_gen<T, F, SIZE, 0, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return genarray<T, F, SIZE, 0, Args...>(f);
}
};
template<typename T, typename F, int SIZE, int N, int... Args>
struct recursive_gen<T, F, SIZE, N, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, N - 1, N, Args...>::generate(f);
}
};
template<typename T, int SIZE>
struct array_generator {
template<typename F>
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, SIZE - 1>::generate(f);
}
};
std::array<double, 4> const A = array_generator<double, 4>::generate([](int i) { return std::sin(0.1 * i);});
std::array<double, 4> const B = array_generator<double, 4>::generate([](int i) { return std::cos(0.1 * i);});
constexpr int fun(int i) { return 2 * i; }
constexpr std::array<int, 4> const C = array_generator<int, 4>::generate(fun); // generation during compile time
但請注意,為了在編譯時生成, array_generator
輸入函數必須是constexpr
。 這不是三角函數的情況(即,它們不是constexpr
)。 因此, array
s A
和B
初始化將在初始化時發生,而array C
生成將在編譯時發生。
想象一下,全局數組A要長得多,而且你不想做所有這種重復輸入。 在編譯或初始化時是否有更短的方法來初始化數組A.
創建一個生成器並通過std::generate_n()
(或plain std::generate()
)傳遞它。
#include <algorithm>
#include <array>
#include <cmath>
template <typename Value_t>
struct SinGenerator{
SinGenerator(std::size_t start = 0, Value_t counter_scalar = 1)
: index{start},
scalar{counter_scalar} {
}
Value_t operator()() {
return sin(index++ * scalar);
}
std::size_t index;
Value_t scalar;
};
template <typename Value_t, std::size_t Size>
std::array<Value_t, Size> init_table(const std::size_t start,
const Value_t counter_scalar) {
std::array<Value_t, Size> arr;
SinGenerator<Value_t> gen(start, counter_scalar);
std::generate(arr.begin(), arr.end(), gen);
return arr;
}
const auto kSinTable(init_table<float, 10>(0, 0.1f));
如果您A
數組總是保持不變並且非常大,您總是可以編寫一個簡短的腳本,它可以計算數組中的每個值,並且可以在源代碼中使用輸出來進行靜態初始化。
如果公式很簡單,甚至可以使用MS Excel 生成那種靜態初始化數據。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.