Compilar hash de cadena de tiempo

100

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>::valuetambién podría ser una solución viable.

deft_code
fuente
Parece que tampoco puedo encontrar nada, pero pude ver tener que forzar su función hash en un constexpr.
luke
4
¿Existe un compilador que ya admita literales definidos por el usuario? Gcc no lo hace ( gcc.gnu.org/projects/cxx0x.html ) y tampoco he encontrado que se mencionen para VC10. Sin el soporte del compilador, solo puede ser un trabajo de conjetura, pero los literales definidos por el usuario con plantilla parece que deberían ser posibles.
Georg Fritzsche
1
¿Es lindo pero no útil? Si el valor del conmutador es una cadena de tiempo de ejecución, también debe verificar si hay colisiones. Tal vez empaquetar es mejor (mi respuesta tiene una función de paquete para rellenar 9 caracteres en 64 bits).
u0b34a0f6ae
@ u0b34a0f6ae ¿Por qué comprobar si hay colisiones?
cubuspl42
El compilador debería emitir un error si dos valores de caso son iguales.
Mark Storer

Respuestas:

88

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:

// CRC32 Table (zlib polynomial)
static constexpr uint32_t crc_table[256] = {
    0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
    0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
    0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
...
};
template<size_t idx>
constexpr uint32_t crc32(const char * str)
{
    return (crc32<idx-1>(str) >> 8) ^ crc_table[(crc32<idx-1>(str) ^ str[idx]) & 0x000000FF];
}

// This is the stop-recursion function
template<>
constexpr uint32_t crc32<size_t(-1)>(const char * str)
{
    return 0xFFFFFFFF;
}

// This doesn't take into account the nul char
#define COMPILE_TIME_CRC32_STR(x) (crc32<sizeof(x) - 2>(x) ^ 0xFFFFFFFF)

enum TestEnum
{
    CrcVal01 = COMPILE_TIME_CRC32_STR("stack-overflow"),
};

CrcVal01 es igual a 0x335CC04A

¡Espero que esto te ayudará!

Clemente JACOB
fuente
4
Si, absolutamente. Lo probé contra el algoritmo de tiempo de ejecución Python CRC32 (proveniente de zlib) y los resultados son los mismos. De hecho, ¡lo que estás tratando de lograr es exactamente para qué utilizo esta técnica!
Clement JACOB
2
Gracias por publicar esto, ¡es muy útil!
Tamás Szelei
2
Faltaba un indicador de compilación. Además, tuve que convertir a size_t el valor -1 en la función de plantilla de detención de recursividad. La versión actualizada está disponible aquí (trabajando desde Clang 3.3): goo.gl/vPMkfB
Clement JACOB
1
constexprno está disponible en VS2013, excepto en noviembre de 2013 CTP blogs.msdn.com/b/vcblog/archive/2013/11/18/…
2
Estás recurriendo dos veces. Esta solución tiene una complejidad exponencial con respecto a la longitud de la cadena, y el compilador no es lo suficientemente inteligente como para eludir la segunda llamada. Verifique otras respuestas para una posible solución a este problema.
CygnusX1
30

Al menos según mi lectura de §7.1.5 / 3 y §5.19, lo siguiente podría ser legítimo:

unsigned constexpr const_hash(char const *input) {
    return *input ?
        static_cast<unsigned int>(*input) + 33 * const_hash(input + 1) :
        5381;
}

Esto parece seguir las reglas básicas en §7.1.5 / 3:

  1. La forma es: "expresión de retorno";
  2. Su único parámetro es un puntero, que es un tipo escalar y, por lo tanto, un tipo literal.
  3. Su retorno es unsigned int, que también es escalar (y por lo tanto literal).
  4. No hay una conversión implícita al tipo de retorno.

Existe alguna duda sobre si los *inputs 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:

switch(std::hash(value)) {
    case const_hash("one"): one(); break;
    case const_hash("two"): two(); break;
    // ...
    default: other(); break;
}

Sin embargo, para cumplir con los requisitos de §5.19 / 2/6/2, es posible que deba hacer algo como esto:

// one of the `constexpr`s is probably redundant, but I haven't figure out which.
char constexpr * constexpr v_one = "one"; 

// ....

case const_hash(v_one): one(); break;
  1. Estoy usando los números de 'barra oblicua' adicionales para referirme a viñetas no numeradas, por lo que este es el segundo punto dentro de si el sexto punto bajo §5.19 / 2. Creo que podría tener que hablar con Pete Becker sobre si es posible agregar algún tipo de números / letras / números romanos en la jerarquía para identificar piezas como esta ...
Jerry Coffin
fuente
3
Hay dos cosas mal en esto. 1: No se permiten llamadas recursivas constexpr, 2: No tiene ninguna condición de detención (dónde *input == nullptr) y, según tengo entendido, constexprno puede tener una.
Motti
9
¿Dónde dice que la recursividad no está permitida en un constexpr? Por lo que puedo ver, solo dice que cualquier función que llame debe estar marcada como constexprt (§5.19 / 2/2). Cometí un error en la condición de terminación, que ahora he solucionado (accidentalmente usé || donde debería haber estado &&).
Jerry Coffin
3
constexpr recursivo. Leí algunas actas de reuniones de 2008. Hubo una discusión sobre permitir o rechazar funciones de constexpr recursivas. La esencia era que la redacción actual no lo prohibía, y lo implicaba ligeramente. El comité consideró que una característica tan poderosa debería explicarse explícitamente. Eso fue en 2008, no sé qué pasó desde entonces.
deft_code
3
Correcto, probablemente debería haber señalado que iba desde N3000, que (creo) es actualmente el borrador más reciente. Estoy bastante seguro de que estuvo prohibido en algún momento, pero al menos por ahora parece estar permitido.
Jerry Coffin
2
Um, el operador && está devolviendo un bool, por lo que esta función probablemente no esté haciendo lo que quieres. En particular, devuelve 0 para cualquier cadena vacía y posiblemente algunas otras cadenas que comienzan con un char que se convierte en (unsigned)-1si hay alguno; y devuelve 1 para todas las demás cadenas. ¿Reescribir con operador condicional ternario?
ndkrempel
13

Este es un intento de resolver el problema del OP de la manera más exacta posible.

namespace my_hash {
  template<class>struct hasher;
  template<>
  struct hasher<std::string> {
    std::size_t constexpr operator()(char const *input)const {
      return *input ?
        static_cast<unsigned int>(*input) + 33 * (*this)(input + 1) :
        5381;
    }
    std::size_t operator()( const std::string& str ) const {
      return (*this)(str.c_str());
    }
  };
  template<typename T>
  std::size_t constexpr hash(T&& t) {
    return hasher< typename std::decay<T>::type >()(std::forward<T>(t));
  }
  inline namespace literals {
    std::size_t constexpr operator "" _hash(const char* s,size_t) {
      return hasher<std::string>()(s);
    }
  }
}
using namespace my_hash::literals;
void one() {} void two() {} void other() {}

void foo( const std::string& value )
{
  switch( my_hash::hash(value) )
  {
    case "one"_hash: one(); break;
    case "two"_hash: two(); break;
    /*many more cases*/
    default: other(); break;
  }
}

ejemplo vivo .

Tenga en cuenta la principal diferencia: std::hashno se puede usar, ya que no tenemos control sobre std::hashel algoritmo de, y debemos volver a implementarlo como constexprpara evaluarlo en tiempo de compilación. Además, no hay hash "transparentes" std, por lo que no puede (sin crear un std::string) hash en un búfer de caracteres sin formato como std::string.

std::stringPegué el hasher personalizado (con const char*soporte transparente ) en un my_hashespacio de nombres, para que pueda almacenarlo en un std::unordered_mapsi 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 switchdeclaración casees peligroso. Querrá hacer una ==comparación después para confirmar que funcionó.

Yakk - Adam Nevraumont
fuente
2
¿El enlace de ejemplo en vivo parece estar equivocado / desactualizado?
fuenfundachtzig
@fuenfundachtzig ¿Creerías que lo acabo de arreglar?
Yakk - Adam Nevraumont
13

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

#include <iostream>
#include <string>
#include <vector>

static constexpr unsigned int crc_table[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3,    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};


template<int size, int idx = 0, class dummy = void>
struct MM{
  static constexpr unsigned int crc32(const char * str, unsigned int prev_crc = 0xFFFFFFFF)
  {
      return MM<size, idx+1>::crc32(str, (prev_crc >> 8) ^ crc_table[(prev_crc ^ str[idx]) & 0xFF] );
  }
};

// This is the stop-recursion function
template<int size, class dummy>
struct MM<size, size, dummy>{
  static constexpr unsigned int crc32(const char * str, unsigned int prev_crc = 0xFFFFFFFF)
  {
      return prev_crc^ 0xFFFFFFFF;
  }
};

// This don't take into account the nul char
#define COMPILE_TIME_CRC32_STR(x) (MM<sizeof(x)-1>::crc32(x))


template<unsigned int crc>
void PrintCrc()
{
    std::cout << crc << std::endl;
}


int main()
{

    PrintCrc<COMPILE_TIME_CRC32_STR("HAH")>();
}

Ver prueba de concepto aquí

tower120
fuente
1
Gracias, la respuesta de Jacob funciona bien para lo que quería en GCC pero msvc arrojaba errores con cadenas más grandes. Su respuesta funciona en msvc con las cadenas más grandes que necesito hash.
Daniel Moodie
8

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:

De lo contrario (S contiene una plantilla de operador literal), L se trata como una llamada del
operador de formulario "" X <'c1', 'c2', ..., 'ck'> () donde n es la secuencia de caracteres fuente c1c2 ... ck. [Nota: La secuencia c1c2 ... ck solo puede contener caracteres del juego de caracteres fuente básico. —Nota final]

Combine eso con constexpry 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):

template<char c, char... str>
struct hash {
    static const unsigned result = c + hash<str...>::result;
};

template<char c>
struct hash {
    static const unsigned result = c;
};

template<char... str> 
constexpr unsigned
operator "" _hash() {
    return hash<str>::result;
}

// update: probably wrong, because the above 
// form is not allowed for string-literals:    
const unsigned h = "abcd"_hash;

Sin embargo, si el enfoque de Jerrys funciona, lo siguiente debería funcionar:

constexpr unsigned operator "" _hash(const char* s, size_t) {
    return const_hash(s);
}
Georg Fritzsche
fuente
Buena (y necesaria) combinación de plantillas de longitud variable y constexprliteral 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).
Motti
Mezclé los párrafos y pensé que este caso era una excepción; lamentablemente, no parece ser así.
Georg Fritzsche
1
@Motti: ¿dónde está gf usando el literal de cadena como parámetro de plantilla? ¿Está confundiendo al pasar la plantilla variada de caracteres como una cadena literal?
deft_code
A partir de la propuesta original, parece que la versión de la plantilla para cadenas literales no se aceptó (¿debido a problemas de concatenación? Stackoverflow.com/questions/1108008/any-ideas-for-c1y/… - comments) - muy mal.
Georg Fritzsche
1
La última versión operator ""_hashme funciona en Xcode 5.0.2.
cubuspl42
8

Otra solución basada en la de Clement JACOB, usando C ++ 11 constexpr (no el extendido C ++ 14) pero con una sola recursividad.

namespace detail {
// CRC32 Table (zlib polynomial)
static constexpr uint32_t crc_table[256] = { 0x00000000L, 0x77073096L, ... }

template<size_t idx>
constexpr uint32_t combine_crc32(const char * str, uint32_t part) {
  return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
}

template<size_t idx>
constexpr uint32_t crc32(const char * str) {
  return combine_crc32<idx>(str, crc32<idx - 1>(str));
}

// This is the stop-recursion function
template<>
constexpr uint32_t crc32<size_t(-1)>(const char * str) {
  return 0xFFFFFFFF;
}

} //namespace detail

template <size_t len>
constexpr uint32_t ctcrc32(const char (&str)[len]) {
  return detail::crc32<len - 2>(str) ^ 0xFFFFFFFF;
}

Alguna explicación

  • Estamos usando una sola recursividad, por lo que la función funciona bien incluso para cadenas más largas.
  • La función extra combine_crc32nos permite almacenar el resultado de una recursividad bajo una variable party usarla dos veces. Esta función es una solución para la limitación de C ++ 11 que no permite declaraciones de variables locales.
  • La ctcrc32función espera un literal de cadena, que se pasa como const 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.
CygnusX1
fuente
2
Esta terminó siendo mi implementación favorita, gracias.
Daniel Moodie
6

Lo siguiente funciona en GCC 4.6.1 y puede usar hasho packen bloques de interruptores.

/* Fast simple string hash (Bernstein?) */                                       
constexpr unsigned int hash(const char *s, int off = 0) {                        
    return !s[off] ? 5381 : (hash(s, off+1)*33) ^ s[off];                           
}                                                                                

/* Pack the string into an unsigned int                                          
 * Using 7 bits (ascii) it packs 9 chars into a uint64_t                         
 */                                                                              
template <class T = uint64_t, unsigned int Bits = 7>                             
constexpr T pack(const char *s, unsigned int off = 0) {                          
    return (Bits*off >= CHAR_BIT*sizeof(T) || !s[off]) ? 0 :                     
        (((T)s[off] << (Bits*off)) | pack(s,off+1));                             
}  

GCC aparentemente (?) No permite llamadas recursivas en las que pasamos s+1con sun puntero, por lo que utilizo la offvariable.

u0b34a0f6ae
fuente
5

Si tiene un compilador de c ++ 17 y string_view, esto se vuelve trivial, simplemente escriba la versión normal:

constexpr uint32_t crc32(std::string_view str)
{
    uint32_t crc = 0xffffffff;
    for (auto c : str)
        crc = (crc >> 8) ^ crc_table[(crc ^ c) & 0xff];
    return crc ^ 0xffffffff;
}
Guillaume Gris
fuente
Tenga en cuenta que el compilador puede decidir no procesar esto en tiempo de compilación si simplemente escribe 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");
Guillaume Gris
3

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:

namespace detail {

    // CRC32 Table (zlib polynomial)
    static constexpr uint32_t crc_table[256] = { 0x00000000L, 0x77073096L, ... };

    constexpr uint32_t combine_crc32(size_t idx, const char * str, uint32_t part) {
        return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
    }

    constexpr uint32_t crc32(size_t idx, const char * str) {
        return idx == size_t(-1) ? 
            0xFFFFFFFF : combine_crc32(idx, str, crc32(idx - 1, str));
    }
}

uint32_t ctcrc32(std::string const& str) {
    size_t len = str.size() + 1;
    return detail::crc32(len - 2, str.c_str()) ^ 0xFFFFFFFF;
}

template <size_t len>
constexpr uint32_t ctcrc32(const char (&str)[len]) {
    return detail::crc32(len - 2, str) ^ 0xFFFFFFFF;
}

Necesita str.size() + 1porque lenen la segunda sobrecarga se strlen(str) + 1debe 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 para const char *, size_to std::string_view.

Bosquecillo
fuente
2

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.

#include <functional>
#include <cassert>
using namespace std;


constexpr size_t cx_hash(const char* input) {
    size_t hash = sizeof(size_t) == 8 ? 0xcbf29ce484222325 : 0x811c9dc5;
    const size_t prime = sizeof(size_t) == 8 ? 0x00000100000001b3 : 0x01000193;

    while (*input) {
        hash ^= static_cast<size_t>(*input);
        hash *= prime;
        ++input;
    }

    return hash;
}


int main() {
    /* Enter your code here. Read input from STDIN. Print output to STDOUT */

    auto a = cx_hash("test");
    hash<string> func;
    auto b = func("test");
    assert(a == b);

    return 0;
}

https://github.com/manuelgustavo/cx_hash

manuel saraiva
fuente
0

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.

#include <inttypes.h>

namespace detail
{
    // CRC32 Table (zlib polynomial)
    static constexpr uint32_t crc_table[256] =
    {
        0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
        ...
    };

    constexpr uint32_t combine_crc32( const char c, uint32_t part )
    {
        return (part >> 8) ^ crc_table[(part ^ c) & 0x000000FF];
    }

    constexpr uint32_t crc32( const char * str, size_t idx )
    {
        return combine_crc32( str[idx], idx ? crc32( str, idx - 1 ) : 0xFFFFFFFF );
    }
} //namespace detail

constexpr uint32_t ctcrc32( const char* str, size_t len )
{
    return detail::crc32( str, len ) ^ 0xFFFFFFFF;
}

size_t constexpr operator "" _hash( const char* str, size_t len )
{
    return ctcrc32( str, len );
}

Alternativa con algoritmo de Dan Bernstein (djb2) (respuestas combinadas de Jerry Coffin + Georg Fritzsche )

unsigned constexpr const_hash( char const *input )
{
    return *input ?
        static_cast<unsigned int>(*input) + 33 * const_hash( input + 1 ) :
        5381;
}
size_t constexpr operator "" _hash( const char* str, size_t len )
{
    return const_hash( str );
}

Tabla crc32:

static constexpr uint32_t crc_table[256] =
    {
        0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
        0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
        0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
        0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
        0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
        0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
        0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
        0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
        0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
        0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
        0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
        0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
        0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
        0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
        0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
        0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
        0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
        0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
        0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
        0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
        0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
        0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
        0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
        0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
        0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
        0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
        0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
        0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
        0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
        0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
        0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
        0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
        0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
        0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
        0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
        0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
        0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
        0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
        0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
        0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
        0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
        0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
        0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
        0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
        0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
        0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
        0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
        0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
        0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
        0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
        0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
        0x2d02ef8dL
    };
Zacharias
fuente