He leído en algunos lugares diferentes que usando los nuevos literales de cadena de C ++ 11 podría ser posible calcular el hash de una cadena en tiempo de compilación. Sin embargo, nadie parece estar dispuesto a salir y decir que será posible o cómo se hará.
- es posible?
- ¿Cómo se vería el operador?
Estoy particularmente interesado en casos de uso como este.
void foo( const std::string& value )
{
switch( std::hash(value) )
{
case "one"_hash: one(); break;
case "two"_hash: two(); break;
/*many more cases*/
default: other(); break;
}
}
Nota: la función hash de tiempo de compilación no tiene que verse exactamente como la escribí. Hice lo mejor que pude para adivinar cómo sería la solución final, pero meta_hash<"string"_meta>::value
también podría ser una solución viable.
c++
metaprogramming
c++11
hash
deft_code
fuente
fuente
Respuestas:
Esto es un poco tarde, pero logré implementar una función CRC32 en tiempo de compilación con el uso de
constexpr
. El problema con esto es que en el momento de escribir este artículo, solo funciona con GCC y no con MSVC ni con el compilador Intel.Aquí está el fragmento de código:
CrcVal01
es igual a 0x335CC04A¡Espero que esto te ayudará!
fuente
constexpr
no está disponible en VS2013, excepto en noviembre de 2013 CTP blogs.msdn.com/b/vcblog/archive/2013/11/18/…Al menos según mi lectura de §7.1.5 / 3 y §5.19, lo siguiente podría ser legítimo:
Esto parece seguir las reglas básicas en §7.1.5 / 3:
Existe alguna duda sobre si los
*input
s involucran una conversión ilegal de lvalue a rvalue, y no estoy seguro de entender las reglas en §5.19 / 2/6/2 1 y §4.1 lo suficientemente bien como para estar seguro de eso.Desde un punto de vista práctico, este código es aceptado por (por ejemplo) g ++, al menos desde g ++ 4.7.1.
El uso sería algo como:
Sin embargo, para cumplir con los requisitos de §5.19 / 2/6/2, es posible que deba hacer algo como esto:
fuente
constexpr
, 2: No tiene ninguna condición de detención (dónde*input == nullptr
) y, según tengo entendido,constexpr
no puede tener una.(unsigned)-1
si hay alguno; y devuelve 1 para todas las demás cadenas. ¿Reescribir con operador condicional ternario?Este es un intento de resolver el problema del OP de la manera más exacta posible.
ejemplo vivo .
Tenga en cuenta la principal diferencia:
std::hash
no se puede usar, ya que no tenemos control sobrestd::hash
el algoritmo de, y debemos volver a implementarlo comoconstexpr
para evaluarlo en tiempo de compilación. Además, no hay hash "transparentes"std
, por lo que no puede (sin crear unstd::string
) hash en un búfer de caracteres sin formato comostd::string
.std::string
Pegué el hasher personalizado (conconst char*
soporte transparente ) en unmy_hash
espacio de nombres, para que pueda almacenarlo en unstd::unordered_map
si necesita consistencia.Basado en la excelente respuesta de @ JerryCoffin y el hilo de comentarios debajo, pero con un intento de escribirlo con las mejores prácticas actuales de C ++ 11 (¡en lugar de anticiparlas!).
Tenga en cuenta que usar un "hash sin formato" para una
switch
declaracióncase
es peligroso. Querrá hacer una==
comparación después para confirmar que funcionó.fuente
Este fragmento está basado en el de Clement JACOB. Pero también funciona con clang. Y debería ser más rápido en la compilación (solo tiene una llamada recursiva, no dos como en la publicación original).
Ver prueba de concepto aquí
fuente
Tenga en cuenta que el formulario que se muestra aquí no se aceptó en el estándar, como se indica a continuación.
Se supone que el procesamiento de cadenas de tiempo de compilación es posible a través de literales definidos por el usuario propuestos en N2765 .
Como ya mencioné, no conozco ningún compilador que lo implemente actualmente y sin el soporte del compilador solo puede haber conjeturas.
En §2.13.7.3 y 4 del borrador tenemos lo siguiente:
Combine eso con
constexpr
y deberíamos tener un procesamiento de cadenas de tiempo de compilación.actualización: pasé por alto que estaba leyendo el párrafo incorrecto, este formulario está permitido para literales enteros definidos por el usuario y literales flotantes, pero aparentemente no para literales de cadena (§2.13.7.5).
Esta parte de la propuesta parece no haber sido aceptada.
Dicho esto, con mi visión limitada de C ++ 0x, podría verse algo como esto (lo más probable es que haya algo mal):
Sin embargo, si el enfoque de Jerrys funciona, lo siguiente debería funcionar:
fuente
constexpr
literal definido por el usuario. No estoy seguro de que pueda usar un literal de cadena como parámetro de plantilla, ¿no tienen enlaces estáticos? (lo hacen en C ++ 98 al menos y, por lo tanto, están prohibidos como parámetros de plantilla).operator ""_hash
me funciona en Xcode 5.0.2.Otra solución basada en la de Clement JACOB, usando C ++ 11 constexpr (no el extendido C ++ 14) pero con una sola recursividad.
Alguna explicación
combine_crc32
nos permite almacenar el resultado de una recursividad bajo una variablepart
y usarla dos veces. Esta función es una solución para la limitación de C ++ 11 que no permite declaraciones de variables locales.ctcrc32
función espera un literal de cadena, que se pasa comoconst char (&)[len]
. De esta forma podemos obtener la longitud de la cadena como un parámetro de plantilla y no tenemos que depender de macros.fuente
Lo siguiente funciona en GCC 4.6.1 y puede usar
hash
opack
en bloques de interruptores.GCC aparentemente (?) No permite llamadas recursivas en las que pasamos
s+1
cons
un puntero, por lo que utilizo laoff
variable.fuente
Si tiene un compilador de c ++ 17 y string_view, esto se vuelve trivial, simplemente escriba la versión normal:
fuente
crc32("mystring")
(normalmente VS tiende a hacer eso). El truco para evitar ese problema es crear una variable constexpr que dependa de la evaluación del tiempo de compilación de su crc32. Típicamenteconstexpr uint32_t val = crc32("mystring");
Aquí hay otra implementación de C ++ 11 (basada en la respuesta de @ CygnusX1), que funciona tanto con matrices de caracteres constexpr como con cadenas de tiempo de ejecución:
Necesita
str.size() + 1
porquelen
en la segunda sobrecarga sestrlen(str) + 1
debe al carácter nulo al final.No agregué una sobrecarga para
const char *
porque se estropea con la segunda sobrecarga. Puede agregar fácilmente sobrecargas paraconst char *, size_t
ostd::string_view
.fuente
Esta es una buena pregunta.
Basado en la respuesta de Jerry Coffin, he creado otro que es compatible con std :: hash de Visual Studio 2017.
https://github.com/manuelgustavo/cx_hash
fuente
Todavía me faltaba una variante crc32-literal (que no es posible con plantillas), así que aquí está mi sugerencia basada en CygnusX1 . Hice algunas pruebas, no dude en darnos su opinión.
Testet en MSVC.
PD: Odio buscar cosas adicionales en otro lugar, así que copié la tabla CRC en la parte inferior de mi respuesta.
Alternativa con algoritmo de Dan Bernstein (djb2) (respuestas combinadas de Jerry Coffin + Georg Fritzsche )
Tabla crc32:
fuente