Llene una matriz utilizando Constexpr en tiempo de comstackción

Me gustaría llenar una matriz de enumeración usando constexpr. El contenido de la matriz sigue un cierto patrón.

Tengo una enumeración que separa el conjunto de caracteres ASCII en cuatro categorías.

enum Type { Alphabet, Number, Symbol, Other, }; constexpr Type table[128] = /* blah blah */; 

Me gustaría tener una matriz de 128 Type . Pueden estar en una estructura. El índice de la matriz corresponderá a los caracteres ASCII y el valor será el Type de cada carácter.

Entonces puedo consultar esta matriz para descubrir a qué categoría pertenece un personaje ASCII. Algo como

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

Me gustaría saber si esto es posible sin algunos macro hacks largos.

Actualmente, inicializo la tabla haciendo lo siguiente.

 constexpr bool IsAlphabet (char c) { return ((c >= 0x41 && 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 }; 

donde INITIALIZE es el punto de entrada de algunos macro hacks muy largos. Algo como

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

Me gustaría una forma de poblar esta matriz o una estructura que contenga la matriz sin la necesidad de este macro hack …

Tal vez algo como

 struct Table { Type _[128]; }; constexpr Table table = MagicFunction(); 

Entonces, la pregunta es ¿cómo escribir esta MagicFunction ?

Nota: Estoy al tanto de cctype y Me gusta, esta pregunta es más de un Is this possible? en lugar de Is this the best way to do it? .

Cualquier ayuda sería apreciada.

Gracias,

Ignorando TODOS los problemas, índices para el rescate:

 template struct seq{}; template struct gen_seq : gen_seq{}; template struct gen_seq<0, Is...> : seq{}; template constexpr Table MagicFunction(seq){ return {{ whichCategory(Is)... }}; } constexpr Table MagicFunction(){ return MagicFunction(gen_seq<128>{}); } 

Ejemplo en vivo

En C ++ 17 ::std::array ha sido actualizado para ser más amigable con constexpr y usted puede hacer lo mismo que en C ++ 14, pero sin algunos de los hacks de aspecto aterrador para evitar la falta de constexpr en lugares cruciales . Aquí es cómo se vería el código allí:

 #include  enum Type { Alphabet, Number, Symbol, Other, }; constexpr ::std::array MagicFunction() { using result_t = ::std::array; result_t result = {Other}; result[65] = Alphabet; //.... return result; } const ::std::array table = MagicFunction(); 

Nuevamente, MagicFunction aún necesita obedecer las reglas de constexpr bastante flojas. Principalmente, no puede modificar ninguna variable global o usar new (lo que implica modificar el estado global, es decir, el montón) u otras cosas similares.

En mi humilde opinión, la mejor manera de hacerlo es simplemente escribir un pequeño progtwig de instalación que generará la table para usted. Y luego puede descartar el progtwig de instalación o registrarlo junto con el código fuente generado.

La parte difícil de esta pregunta es solo un duplicado de esta otra: ¿es posible crear e inicializar una matriz de valores mediante la metaprogtwigción de plantillas?

El truco es que es imposible escribir algo como

 Type table[256] = some_expression(); 

en el scope del archivo, porque las matrices globales se pueden inicializar solo con listas de inicializadores literales (nivel de fuente). No puede inicializar una matriz global con el resultado de una función constexpr , incluso si de alguna manera pudiera obtener esa función para devolver una std::initializer_list , que no puede porque su constructor no está declarado constexpr .

Entonces, lo que tienes que hacer es obtener el comstackdor para generar la matriz por ti, convirtiéndolo en un miembro de datos static const de una clase de plantilla. Después de uno o dos niveles de metaprogtwigción que estoy demasiado confundido para escribir, tocará fondo en una línea que se parece a algo así como

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

donde Indices es un paquete de parámetros que se ve como 0,1,2,... 254,255 . Usted construye ese paquete de parámetros usando una plantilla de ayuda recursiva, o tal vez simplemente usando algo de Boost. Y luego puedes escribir

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

… Pero ¿por qué harías todo eso, cuando la tabla solo tiene 256 entradas que nunca cambiarán a menos que cambie ASCII? El camino correcto es la forma más simple: precomputa las 256 entradas y escribe la tabla de forma explícita, sin plantillas, constexpr o cualquier otra magia.

La forma de hacer esto en C ++ 14 tiene este aspecto:

 #include  enum Type { Alphabet, Number, Symbol, Other, }; constexpr ::std::array MagicFunction() { using result_t = ::std::array; result_t result = {Other}; const result_t &fake_const_result = result; const_cast(fake_const_result[65]) = Alphabet; //.... return result; } const ::std::array table = MagicFunction(); 

Ya no se necesita hacker de plantilla inteligente. Sin embargo, debido a que C ++ 14 realmente no se sometió a una revisión lo suficientemente exhaustiva de lo que tenía que ser constexpr en la biblioteca estándar, se debe usar un hack horrible que involucre a const_cast .

Y, por supuesto, MagicFunction no MagicFunction modificar ninguna variable global o violar las reglas constexpr . Pero esas reglas son bastante liberales hoy en día. Puede, por ejemplo, modificar todas las variables locales que desee, aunque pasarlas por referencia o tomar sus direcciones puede que no funcionen tan bien.

Vea mi otra respuesta para C ++ 17, que le permite soltar algunos de los hacks feos.