在编译时使用Constexpr填充数组

Jim*_* Lu 20 c++ templates constexpr c++11

我想使用constexpr填充一个枚举数组.阵列的内容遵循某种模式.

我有一个枚举将ASCII字符集分为四类.

enum Type {
    Alphabet,
    Number,
    Symbol,
    Other,
};

constexpr Type table[128] = /* blah blah */;
Run Code Online (Sandbox Code Playgroud)

我想有一个128的数组Type.它们可以是一个结构.数组的索引将对应于ASCII字符,值将是Type每个字符的值.

所以我可以查询这个数组,找出ASCII字符属于哪个类别.就像是

char c = RandomFunction();
if (table[c] == Alphabet) 
    DoSomething();
Run Code Online (Sandbox Code Playgroud)

我想知道如果没有一些冗长的宏观黑客,这是否可行.

目前,我通过执行以下操作来初始化表.

constexpr bool IsAlphabet (char c) {
    return ((c >= 0x41 && c <= 0x5A) ||
            (c >= 0x61 && c <= 0x7A));
}

constexpr bool IsNumber (char c) { /* blah blah */ }

constexpr bool IsSymbol (char c) { /* blah blah */ }

constexpr Type whichCategory (char c) { /* blah blah */ }

constexpr Type table[128] = { INITIALIZE };
Run Code Online (Sandbox Code Playgroud)

哪里INITIALIZE是一些非常冗长的宏黑客入口点.就像是

#define INITIALIZE INIT(0)
#define INIT(N) INIT_##N
#define INIT_0 whichCategory(0), INIT_1
#define INIT_1 whichCategory(1), INIT_2
//...
#define INIT_127 whichCategory(127)
Run Code Online (Sandbox Code Playgroud)

我想要一种方法来填充这个数组或包含该数组的结构,而不需要这个宏hack ...

也许是这样的

struct Table {
    Type _[128];
};

constexpr Table table = MagicFunction();
Run Code Online (Sandbox Code Playgroud)

那么,问题是如何写这个MagicFunction

注意:我知道cctype和喜欢,这个问题更多的是一个Is this possible?而不是Is this the best way to do it?.

任何帮助,将不胜感激.

谢谢,

Xeo*_*Xeo 28

无视所有问题,救援指数:

template<unsigned... Is> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};

template<unsigned... Is>
constexpr Table MagicFunction(seq<Is...>){
  return {{ whichCategory(Is)... }};
}

constexpr Table MagicFunction(){
  return MagicFunction(gen_seq<128>{});
}
Run Code Online (Sandbox Code Playgroud)

实例.

  • @Steven:添加了一个指向Lounge <C++> wiki条目的链接.它基本上构建了一个列表`[0..127]`并扩展它,调用`whichCategory(0),whichCategory(1),...,whichCategory(127)`并将其作为列表初始化的初始化参数传递给` Table._`(注意用于初始化内部数组的双"{}". (2认同)
  • @BeyondSora:C++ 11的新手,称为*list-initialization*(也不那么正式,通常称为统一初始化). (2认同)
  • 为什么使用双大括号? (2认同)
  • 在C ++ 14中,此答案已过时,因为C ++ 14允许您在constexpr函数内部构建整个数组。 (2认同)

Omn*_*ous 11

在C++ 17 ::std::array已更新,以更constexpr友好,你可以做同样的在C++ 14,但没有一些可怕看黑客绕过缺乏constexpr在关键的地方.这是代码在那里的样子:

#include <array>

enum Type {
    Alphabet,
    Number,
    Symbol,
    Other,
};

constexpr ::std::array<Type, 128> MagicFunction()
{
   using result_t = ::std::array<Type, 128>;
   result_t result = {Other};
   result[65] = Alphabet;
   //....
   return result;
}

const ::std::array<Type, 128> table = MagicFunction();
Run Code Online (Sandbox Code Playgroud)

再次MagicFunction仍然需要遵守相当宽松的constexpr规则.主要是,它可能不会修改任何全局变量或使用new(这意味着修改全局状态,即堆)或其他此类事物.


Quu*_*one 5

恕我直言,做到这一点的最佳方法就是编写一个可以table为您生成的小型安装程序。然后您可以丢弃安装程序,或者将其与生成的源代码一起检入。

这个问题的棘手部分只是另一个问题的重复:是否可以使用模板元编程来创建和初始化一组值?

诀窍是,不可能写出类似的东西

Type table[256] = some_expression();
Run Code Online (Sandbox Code Playgroud)

在文件范围内,因为全局数组只能用文字(源级)初始化列表进行初始化。你不能用constexpr函数的结果初始化一个全局数组,即使你能以某种方式让该函数返回 a std::initializer_list,你不能这样做,因为它的构造函数没有声明constexpr

所以你需要做的是让编译器为你生成数组,让它static const成为模板类的数据成员。经过一两个级别的元编程,我太困惑而无法写出来,你会在一行看起来像

template <int... Indices>
Type DummyStruct<Indices...>::table[] = { whichCategory(Indices)... };
Run Code Online (Sandbox Code Playgroud)

哪里Indices是一个参数包,看起来像0,1,2,... 254,255. 您可以使用递归助手模板构建该参数包,或者可能只是使用 Boost 之外的东西。然后你可以写

constexpr Type (&table)[] = IndexHelperTemplate<256>::table;
Run Code Online (Sandbox Code Playgroud)

...但是当表只有 256 个条目时,除非 ASCII 本身发生变化,否则这些条目永远不会改变,为什么要这样做呢?正确的方法最简单的方法:预先计算所有 256 个条目并明确地写出表,没有模板、constexpr 或任何其他魔术。