在 C++ 编译时以编程方式创建静态数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2978259/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:39:46  来源:igfitidea点击:

Programmatically create static arrays at compile time in C++

c++metaprogrammingstatic-array

提问by Hippicoder

One can define a static array at compile time as follows:

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

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

Question 1- Is it possible by using various kinds of metaprogramming techniques to assign these values "programmatically" at compile time?

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

Question 2- Assuming all the values in the array are to be the same barr a few, is it possible to selectively assign values at compile time in a programmatic manner?

问题 2- 假设数组中的所有值都相同,是否可以在编译时以编程方式有选择地分配值?

eg:

例如:

const std::size_t size = 7;        
unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 };
  1. Solutions using C++0x are welcome
  2. The array may be quite large, few hundred elements long
  3. The array for now will only consist of POD types
  4. It can also be assumed the size of the array will be known beforehand, in a static compile-time compliant manner.
  5. Solutions must be in C++ (no script, no macros, no pp or code generator based solutions pls)
  1. 欢迎使用 C++0x 的解决方案
  2. 数组可能很大,有几百个元素长
  3. 现在的数组将只包含 POD 类型
  4. 还可以假设数组的大小以静态编译时兼容的方式预先知道。
  5. 解决方案必须是 C++ (没有脚本,没有宏,没有基于 pp 或代码生成器的解决方案)

UPDATE:Georg Fritzsche's solution is amazing, needs a little work to get it compiling on msvc and intel compilers, but nonetheless a very interesting approach to the problem.

更新:Georg Fritzsche 的解决方案很棒,需要做一些工作才能在 msvc 和 intel 编译器上进行编译,但仍然是解决该问题的非常有趣的方法。

回答by Georg Fritzsche

The closest you can get is using C++0x features to initialize local or member arrays of templates from a variadic template argument list.
This is of course limited by the maximum template instantiation depth and wether that actually makes a notable difference in your case would have to be measured.

您可以获得的最接近的是使用 C++0x 功能从可变参数模板参数列表初始化模板的本地或成员数组。
这当然受到最大模板实例化深度的限制,并且必须测量实际上在您的情况下产生显着差异的情况。

Example:

例子:

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

Usage for your 1..5case:

用于您的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";
}

回答by Matthieu M.

Well your requirements are so vague it's difficult to do anything about them... The main issue is of course: where do those value come from ?

嗯,你的要求太模糊了,很难对它们做任何事情......当然主要的问题是:这些价值从何而来?

Anyway a build in C++ can be thought of as 4 steps:

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

  • Pre-build steps: script generation of header/source from other formats
  • Preprocessing
  • Template instantiations
  • Compilation proper
  • 预构建步骤:从其他格式生成标题/源的脚本
  • 预处理
  • 模板实例化
  • 编译正确

If you wish to rule out the script generation, then you're left with 2 alternatives: Preprocessing and Meta-template programming.

如果您希望排除脚本生成,那么您只有两种选择:预处理和元模板编程。

There is just no way I know of for meta-template programming to do the trick here, because as far as I know it's not possible to concatenate two arrays at compile time. Thus we are left with the savior of the day: Preprocessor Programming

我不知道元模板编程可以在这里做这个伎俩,因为据我所知,在编译时连接两个数组是不可能的。因此,我们只剩下今天的救星了:预处理器编程

I would suggest using a full-fledged library to help us out: Boost.Preprocessor.

我建议使用一个成熟的库来帮助我们:Boost.Preprocessor

Of particular interest here:

这里特别有趣:

Now if only we knew where to pick the values from, we could give more meaningful examples.

现在,如果我们知道从哪里选择值,我们就可以给出更有意义的例子。

回答by Michael Anderson

How about building a nested struct using templates, and casting that as an array of the right type. The example below works for me, but I have a feeling I'm either treading in or walking very close to undefined behaviour.

如何使用模板构建嵌套结构,并将其转换为正确类型的数组。下面的例子对我有用,但我有一种感觉,我要么踏入其中,要么非常接近于未定义的行为。

#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;
  }
}

And of course you could argue that the array is not initialised at compile time (which I think is impossible) but the values that will go into the array are calculated at compile time, and you can access them as you would a normal array... I think that's as close as you can get.

当然,您可能会争辩说数组未在编译时初始化(我认为这是不可能的),但将在编译时计算将进入数组的值,您可以像访问普通数组一样访问它们。 . 我认为这是尽可能接近的。

回答by CB Bailey

Do you really need to do it at compiler time? It would be much easier to do at static initialization time. You could do something like this.

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

#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';
    }
}

You could push the lists outside of the structs if you wanted but I thought it was a bit cleaner like this.

如果您愿意,您可以将列表推送到结构之外,但我认为这样更简洁一些。

回答by danielkza

Something like Boost.Assignmentcould work for standard containers. If you really need to use arrays, you can use it along Boost.Array.

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

回答by kwesolowski

Sometime (not always) such array is generated from array of types. For example if you already have variadic class list (like template) and want to store encapsulated uint32_t value you can use:

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

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

回答by Max

the 1't question. You can do it like that.

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

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

回答by Michael Dorgan

from boost,

从提升,

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

Will generate a list of sorted numbers from 1 to 5 at compile time. For the second, you mention no criteria for which values would be changed. I'm pretty sure you can't undef then redef a new var once a list is created.

将在编译时生成从 1 到 5 的排序数字列表。对于第二个,您没有提到更改哪些值的标准。我很确定你不能取消定义然后在创建列表后重新定义一个新的 var。

回答by valdo

There's a lot of things you can do with meta-programming. But first I'd like to ask: why would you want to do this in your case? I could understand if you needed to declare such an array in different places, so that it'd demand rewriting the same things multiple times. Is this your case?

元编程可以做很多事情。但首先我想问:你为什么要在你的情况下这样做?我可以理解您是否需要在不同的地方声明这样一个数组,以便它需要多次重写相同的内容。这是你的情况吗?

By saying "define programmatically" I suggest the following:

通过说“以编程方式定义”,我建议如下:

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

By now we've defined all the values you wanted in the most abstract way. BTW if those values actually mean something for you - you could add it to the declaration:

到目前为止,我们已经以最抽象的方式定义了您想要的所有值。顺便说一句,如果这些值实际上对您有意义 - 您可以将其添加到声明中:

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

Now let's breath life into the above declaration.

现在让我们为上述声明注入活力。

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

You can also handle the situation where most of your array entries are the same, with some perverted creativity :)

您还可以处理大多数数组条目相同的情况,但具有一些变态的创造力:)

But you should always ask yourself: is this really worth it? Because, as you can see, you turn the code into a puzzle.

但你应该经常问自己:这真的值得吗?因为,如您所见,您将代码变成了一个谜题。

回答by Rui Curado

Just use a code generator. Build one or more templates that can generate the code you want, using a table or even math functions. Then include the file you generated in your app.

只需使用代码生成器。使用表格甚至数学函数构建一个或多个可以生成您想要的代码的模板。然后包含您在应用程序中生成的文件。

Seriously, a code generator would make your life much easier.

说真的,代码生成器会让你的生活更轻松。