在编译或初始化时编程初始化长数组

时间:2014-08-10 20:14:04

标签: c++ c++11 initialization

以下是说明我的问题的代码段:

const float D    = 0.1F;
const float A[4] = {sin(0*D), sin(1*D), sin(2*D), sin(3*D)};

想象一下,全局数组A要长得多,而且你不想做所有这些重复输入。在编译或初始化时是否有更短的方法来初始化数组A,即无需编写初始化函数并在程序中的某处调用它?

7 个答案:

答案 0 :(得分:6)

您可以在动态初始化时间内初始化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);

答案 1 :(得分:5)

您可以使用代码生成器生成初始化代码。也就是说,编写将为您编写初始化代码的程序。实际上,您可以在生成时计算值。

请记住,C ++允许在最后一个元素之后放置,。也没有必要指定数组大小。这两件事应该可以轻松编写发电机。

这个简单的python代码应该运行良好:

from math import sin

print('const float A[', N, '] = {')
for i in range(N):
    print('\t', sin(i*D), ',', sep='')
print('};')

答案 2 :(得分:4)

第一部分在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成为D函数,并且基本保证在编译时对其进行评估。

如果是这种情况,请使用constexpr poly_sin和两个A功能相同。

如上所述,这发生在动态初始化时。

虽然看起来数组被复制了两次,但RVO elision意味着任何体面的编译器都会在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)); } ); 代码开始。然后添加:

constexpr

使用lambda传递重复的代码来构建数组。遗憾的是,lambdas默认情况下不是{{1}}所以你不能在编译时这样做。

答案 3 :(得分:3)

您可以使用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

LIVE DEMO

但请注意,为了在编译时生成,array_generator中的输入函数必须为constexpr。对于三角函数不是这种情况(即,它们不是constexpr)。因此,array s AB的初始化将在初始化时进行,而array C的生成将在编译时进行。

答案 4 :(得分:3)

好的我刚刚意识到这实际上没有回答这个问题,因为它指定“无需编写初始化函数并在程序中的某个地方调用它?”但我想不出一个方便的选择。

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

答案 5 :(得分:2)

  

想象一下,全局数组A要长得多,而且你不想做所有这些重复输入。是否有更短的方法在编译或初始化时初始化数组A

创建一个生成器并将其传递给std::generate_n()(或普通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));

答案 6 :(得分:2)

如果你A数组总是保持不变并且非常大,你总是可以编写一个简短的脚本来计算数组中的每个值,并且可以在源代码中使用输出来进行静态初始化。 / p>

如果公式很简单,甚至可以使用MS Excel 生成那种静态初始化数据。