以编程方式在C ++中以编程方式创build静态数组

我们可以在编译时定义一个静态数组,如下所示:

const std::size_t size = 5; unsigned int list[size] = { 1, 2, 3, 4, 5 }; 

问题1 – 是否可以通过使用各种元编程技术在编译时“编程”分配这些值?

问题2 – 假设数组中的所有值都是相同的,那么是否可以在编译时以编程的方式select性地赋值?

例如:

 const std::size_t size = 7; unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 }; 
  1. 欢迎使用C ++ 0x的解决scheme
  2. arrays可能相当大,几百个元素长
  3. 现在的数组将只包含PODtypes
  4. 也可以假定数组的大小将以静态编译时兼容的方式预先知道。
  5. 解决scheme必须在C ++ (没有脚本,没有macros,没有基于pp或代码生成器的解决scheme)

更新:格鲁吉亚Fritzsche的解决scheme是惊人的,需要一点工作,以编译MSVC和英特尔编译器,但仍然是一个非常有趣的方法来解决这个问题。

您可以得到最接近的是使用C ++ 0xfunction来从可变参数模板参数列表初始化模板的本地或成员数组。
这当然受到最大模板实例化深度的限制,而实际上在您的情况下会产生显着差异的模板将不得不被测量。

例:

 template<unsigned... args> struct ArrayHolder { static const unsigned data[sizeof...(args)]; }; template<unsigned... args> const unsigned ArrayHolder<args...>::data[sizeof...(args)] = { args... }; template<size_t N, template<size_t> class F, unsigned... args> struct generate_array_impl { typedef typename generate_array_impl<N-1, F, F<N>::value, args...>::result result; }; template<template<size_t> class F, unsigned... args> struct generate_array_impl<0, F, args...> { typedef ArrayHolder<F<0>::value, args...> result; }; template<size_t N, template<size_t> class F> struct generate_array { typedef typename generate_array_impl<N-1, F>::result result; }; 

用于1..5情况:

 template<size_t index> struct MetaFunc { enum { value = index + 1 }; }; void test() { const size_t count = 5; typedef generate_array<count, MetaFunc>::result A; for (size_t i=0; i<count; ++i) std::cout << A::data[i] << "\n"; } 

那么你的要求太模糊了,对他们做任何事情都很困难……主要的问题当然是:这些价值从何而来?

无论如何,C ++中的构build可以被认为是4个步骤:

  • 预构build步骤:从其他格式生成头文件/源文件
  • 预处理
  • 模板实例化
  • 编译正确

如果你想排除脚本生成,那么你剩下2个select:预处理和元模板编程。

我不知道元模板编程在这里做的窍门,因为据我所知在编译时不可能连接两个数组。 因此,我们留下了一天的救星: 预处理器编程

我会build议使用一个完整的库来帮助我们: Boost.Preprocessor 。

这里特别感兴趣的是:

  • BOOST_PP_FOR
  • BOOST_PP_REPEAT

现在,只要我们知道从哪里select价值,我们就可以给出更有意义的例子。

如何使用模板构build嵌套结构,并将其转换为正确types的数组。 下面的例子适用于我,但我有一种感觉,我要么走在非常接近未定义的行为。

 #include <iostream> template<int N> struct NestedStruct { NestedStruct<N-1> contained; int i; NestedStruct<N>() : i(N) {} }; template<> struct NestedStruct<0> { int i; NestedStruct<0>() : i(0) {} }; int main() { NestedStruct<10> f; int *array = reinterpret_cast<int*>(&f); for(unsigned int i=0;i<10;++i) { std::cout<<array[i]<<std::endl; } } 

当然,你可以争辩说数组在编译时没有被初始化(我认为这是不可能的),但是数组中的值是在编译时计算出来的,你可以像访问一个普通数组一样访问它们。我认为这样就可以了。

你真的需要在编译时做这个吗? 在静态初始化的时候做起来会容易得多。 你可以做这样的事情。

 #include <cstddef> #include <algorithm> template<std::size_t n> struct Sequence { int list[n]; Sequence() { for (std::size_t m = 0; m != n; ++m) { list[m] = m + 1; } } }; const Sequence<5> seq1; struct MostlyZero { int list[5]; MostlyZero() { std::fill_n(list, 5, 0); // Not actually necessary if our only // are static as static objects are // always zero-initialized before any // other initialization list[2] = 2; list[3] = 3; } }; const MostlyZero mz1; #include <iostream> #include <ostream> int main() { for (std::size_t n = 0; n != 5; ++n) { std::cout << seq1.list[n] << ", " << mz1.list[n] << '\n'; } } 

如果你愿意的话,你可以把列表推到结构的外面,但是我认为这样更清洁一些。

像Boost.Assignment这样的东西可以用于标准容器。 如果你真的需要使用数组,你可以沿着Boost.Array使用它。

有时(并不总是)这样的数组是从数组types生成的。 例如,如果您已经有可变类的列表(如模板)并且想要存储封装的uint32_t值,则可以使用:

 uint32_t tab[sizeof(A)]= {A::value...}; 

这个问题。 你可以这样做。

 template <int num, int cur> struct ConsequentListInternal { enum {value = cur}; ConsequentListInternal<num-1,cur+1> next_elem; }; template <int cur> struct ConsequentListInternal<0, cur> { enum {value = cur}; }; template <int v> struct ConsequentList { ConsequentListInternal<v, 0> list; }; int main() { ConsequentList<15> list; return 0; } 

元编程可以做很多事情。 但是首先我想问一下:为什么你要这样做呢? 我可以理解,如果你需要在不同的地方声明这样一个数组,所以它会要求多次重写相同的东西。 这是你的情况吗?

通过说“编程定义”我build议如下:

 #define MyArr(macro, sep) \ macro(0) sep \ macro(0) sep \ macro(2) sep \ macro(3) sep \ macro(0) sep \ macro(0) sep \ macro(0) 

现在我们已经以最抽象的方式定义了你想要的所有值。 顺便说一句,如果这些值实际上意味着你的东西 – 你可以将其添加到声明:

 #define MyArr(macro, sep) \ macro(0, Something1) sep \ macro(0, Something2) sep \ // ... 

现在,让我们呼吸到上面的声明中。

 #define NOP #define COMMA , #define Macro_Count(num, descr) 1 #define Macro_Value(num, descr) num const std::size_t size = MyArr(Macro_Count, +); unsigned int list[size] = { MyArr(Macro_Value, COMMA) }; 

你也可以处理大部分数组条目相同的情况,有一些变态的创造力:)

但是你应该总是问自己:这真的值得吗? 因为,正如你所看到的,你把代码变成一个谜题。

从提振,

 boost::mpl::range_c<int,1,5> 

将在编译时生成从1到5的有序数字列表。 对于第二,你没有提到哪些价值将被改变的标准。 我很确定你不能undef然后重新创build一个新的var一个列表创build。

只需使用一个代码生成器。 构build一个或多个可以使用表格甚至math函数生成所需代码的模板。 然后包括你在你的应用程序中生成的文件。

认真的说,一个代码生成器会让你的生活变得更容易。