24

我想使用 constexpr 填充枚举数组。数组的内容遵循一定的模式。

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

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

constexpr Type table[128] = /* blah blah */;

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

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

char c = RandomFunction();
if (table[c] == Alphabet) 
    DoSomething();

我想知道如果没有一些冗长的宏黑客这是否可能。

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

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

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)

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

也许像

struct Table {
    Type _[128];
};

constexpr Table table = MagicFunction();

所以,问题是如何写这个MagicFunction

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

任何帮助,将不胜感激。

谢谢,

4

4 回答 4

30

忽略所有问题,救援指数:

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

活生生的例子。

于 2012-11-09T20:55:53.943 回答
14

在 C++17::std::array中已更新为更加constexpr友好,您可以执行与 C++14 中相同的操作,但没有一些看起来很吓人的 hack 来解决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();

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

于 2017-10-16T21:44:36.570 回答
5

恕我直言,最好的方法是简单地编写一个将为您生成的小型安装程序table。然后您可以丢弃安装程序,或者将其与生成的源代码一起签入。

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

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

Type table[256] = some_expression();

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

因此,您需要做的是让编译器为您生成数组,方法是使其成为static const模板类的数据成员。在我太困惑而无法写出的一两个级别的元编程之后,您将在一行看起来像

template <int... Indices>
Type DummyStruct<Indices...>::table[] = { whichCategory(Indices)... };

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

constexpr Type (&table)[] = IndexHelperTemplate<256>::table;

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

于 2012-11-09T21:12:11.137 回答
4

在 C++14 中执行此操作的方法如下所示:

#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};
   const result_t &fake_const_result = result;
   const_cast<result_t::reference>(fake_const_result[65]) = Alphabet;
   //....
   return result;
}

const ::std::array<Type, 128> table = MagicFunction();

不再需要聪明的模板黑客。不过,由于 C++14 并没有真正constexpr对标准库中的内容和不需要的内容进行足够彻底的审查,因此const_cast必须使用一个可怕的 hack。

当然,MagicFunction最好不要修改任何全局变量或以其他方式违反constexpr规则。但如今这些规则相当宽松。例如,您可以修改所需的所有局部变量,尽管通过引用传递它们或获取它们的地址可能效果不佳。

请参阅我对 C++17 的另一个答案,它可以让您放弃一些看起来很难看的黑客。

于 2017-06-30T02:00:17.643 回答