簡體   English   中英

在編譯或初始化時編程初始化長數組

[英]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 AB初始化將在初始化時發生,而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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM