Compilare l'hashing della stringa del tempo


100

Ho letto in diversi posti che usando i nuovi valori letterali di stringa di C ++ 11 potrebbe essere possibile calcolare l'hash di una stringa in fase di compilazione. Tuttavia, nessuno sembra essere pronto a dire che sarà possibile o come sarebbe stato fatto.

  • È possibile?
  • Come sarebbe l'operatore?

Sono particolarmente interessato a casi d'uso come questo.

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 funzione hash del tempo di compilazione non deve essere esattamente come l'ho scritta. Ho fatto del mio meglio per indovinare come sarebbe stata la soluzione finale, ma meta_hash<"string"_meta>::valuepotrebbe anche essere una soluzione praticabile.


Nemmeno io riesco a trovare nulla, ma potrei vedere dover forzare la tua funzione di hashing in un constexpr.
Luca

4
Esiste un compilatore che supporta già i valori letterali definiti dall'utente? Gcc no ( gcc.gnu.org/projects/cxx0x.html ) e non li ho trovati menzionati neanche per VC10. Senza il supporto del compilatore può essere solo una supposizione, ma i valori letterali definiti dall'utente basati su modelli sembrano essere possibili.
Georg Fritzsche

1
È carino ma non utile? Se il valore dello switch è una stringa di runtime, è necessario controllare anche le collisioni. Forse imballare è meglio (la mia risposta ha una funzione di pacchetto per inserire 9 caratteri in 64 bit).
u0b34a0f6ae

@ u0b34a0f6ae Perché controllare le collisioni?
cubuspl42

Il compilatore dovrebbe generare un errore se due valori case sono uguali.
Mark Storer

Risposte:


88

Questo è un po 'tardi, ma sono riuscito a implementare una funzione CRC32 in fase di compilazione con l'uso di constexpr. Il problema è che al momento della scrittura, funziona solo con GCC e non con MSVC né con il compilatore Intel.

Ecco lo snippet di codice:

// 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 è uguale a 0x335CC04A

Spero che questo ti possa aiutare!


4
Si assolutamente. L'ho testato con l'algoritmo di runtime Python CRC32 (proveniente da zlib) e i risultati sono gli stessi. In effetti, quello che stai cercando di ottenere è esattamente il motivo per cui uso questa tecnica!
Clemente JACOB,

2
Grazie per aver postato questo, è molto utile!
Tamás Szelei

2
Ti mancava un flag di compilazione. Inoltre ho dovuto lanciare a size_t il valore -1 nella funzione di modello di ricorsione di arresto. La versione aggiornata è disponibile qui (funziona da Clang 3.3): goo.gl/vPMkfB
Clement JACOB

1
constexprnon è disponibile in VS2013, tranne che nel novembre 2013 CTP blogs.msdn.com/b/vcblog/archive/2013/11/18/…

2
Stai ricorrendo due volte. Questa soluzione ha una complessità esponenziale rispetto alla lunghezza della stringa e il compilatore non è abbastanza intelligente da elide la seconda chiamata. Controlla altre risposte per una possibile soluzione per questo problema.
CygnusX1

30

Almeno dalla mia lettura del §7.1.5 / 3 e §5.19, quanto segue potrebbe essere legittimo:

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

Questo sembra seguire le regole di base in §7.1.5 / 3:

  1. La forma è: "espressione di ritorno;"
  2. Il suo unico parametro è un puntatore, che è un tipo scalare e quindi un tipo letterale.
  3. Il suo ritorno è unsigned int, che è anche scalare (e quindi letterale).
  4. Non vi è alcuna conversione implicita nel tipo restituito.

Ci sono alcuni dubbi sul fatto che i *inputs implichino una conversione illegale da lvalue a rvalue, e non sono sicuro di aver compreso abbastanza bene le regole in §5.19 / 2/6/2 1 e §4.1 per esserne sicuro.

Da un punto di vista pratico, questo codice è accettato da (per un esempio) g ++, almeno fino a g ++ 4.7.1.

L'utilizzo sarebbe qualcosa del tipo:

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

Per soddisfare i requisiti del §5.19 / 2/6/2 potresti dover fare qualcosa del genere però:

// 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. Sto usando i numeri "barra" extra per fare riferimento a punti elenco non numerati, quindi questo è il secondo punto elenco all'interno se il sesto punto elenco in §5.19 / 2. Penso che dovrei parlare con Pete Becker se sia possibile aggiungere una sorta di numeri / lettere / numeri romani nella gerarchia per identificare pezzi come questo ...

3
Due cose sbagliate in questo. 1: le chiamate ricorsive non sono consentite in constexpr, 2: non hai condizioni di arresto (dove *input == nullptr) e da quanto ho capito constexprnon puoi averne una.
Motti

9
Dove dice che la ricorsione non è consentita in un constexpr? Per quanto posso vedere, dice solo che tutte le funzioni chiamate devono essere contrassegnate come constexprt (§5.19 / 2/2). Ho commesso un errore nella condizione di terminazione, che ora ho risolto (ho usato accidentalmente || dove avrebbe dovuto essere &&).
Jerry Coffin

3
coscienza ricorsivapr. Ho letto alcuni verbali delle riunioni del 2008. Si discuteva sull'autorizzazione o la disattivazione delle funzioni constexpr ricorsive. Il succo era che l'attuale formulazione non lo vietava e lo sottintendeva leggermente. Il comitato ha ritenuto che una caratteristica così potente dovrebbe essere esplicitamente enunciata. Era il 2008, non so cosa sia successo da allora.
deft_code

3
Giusto: probabilmente avrei dovuto sottolineare che stavo passando da N3000, che (credo) è attualmente la bozza più recente. Sono abbastanza sicuro che sia stato proibito una volta, ma almeno per ora sembra essere permesso.
Jerry Coffin

2
Um, l'operatore && sta restituendo un bool, quindi questa funzione probabilmente non sta facendo quello che vuoi. In particolare restituisce 0 per ogni stringa vuota e possibilmente alcune altre stringhe che iniziano con un carattere che viene convertito in (unsigned)-1se presente; e restituisce 1 per tutte le altre stringhe. Riscrivere con operatore condizionale ternario?
ndkrempel

13

Questo è un tentativo di risolvere il problema dell'OP nel modo più preciso possibile.

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

esempio dal vivo .

Si noti la differenza principale - std::hashnon può essere utilizzato, come non abbiamo il controllo su std::hash's algoritmo, e noi dobbiamo reimplementare come constexpral fine di valutare al momento della compilazione. Inoltre, non ci sono hash "trasparenti" std, quindi non è possibile (senza creare un std::string) hash un buffer di caratteri non elaborati come file std::string.

Ho bloccato l' std::stringhashs personalizzato (con const char*supporto trasparente ) in uno my_hashspazio dei nomi, quindi puoi archiviarlo in un file std::unordered_mapse hai bisogno di coerenza.

Basato sull'eccellente risposta di @ JerryCoffin e sul thread di commenti sotto di esso, ma con un tentativo di scriverlo con le attuali migliori pratiche C ++ 11 (invece di anticiparle!).

Nota che usare un "hash grezzo" per switchun'istruzione caseè pericoloso. Ti consigliamo di fare un ==confronto in seguito per confermare che ha funzionato.


2
Il collegamento dell'esempio dal vivo sembra essere sbagliato / obsoleto?
fuenfundachtzig

@fuenfundachtzig Ci crederesti che l'ho appena risolto?
Yakk - Adam Nevraumont

13

Questo frammento è basato su quello di Clement JACOB. Ma funziona anche con clang. E dovrebbe essere più veloce nella compilazione (ha solo una chiamata ricorsiva, non due come nel post originale).

#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")>();
}

Vedi la prova di concetto qui


1
Grazie, la risposta di Jacob funziona bene per quello che volevo su GCC ma msvc generava errori con stringhe più grandi. La tua risposta funziona su msvc con le stringhe più grandi di cui ho bisogno per l'hash.
Daniel Moodie

8

Si noti che il modulo mostrato qui non è stato accettato nello standard, come indicato di seguito.

Si suppone che l'elaborazione della stringa in fase di compilazione diventi possibile tramite i valori letterali definiti dall'utente proposti in N2765 .
Come ho già detto, non conosco nessun compilatore che attualmente lo implementa e senza il supporto del compilatore ci può essere solo lavoro di supposizione.

Nei §2.13.7.3 e 4 della bozza abbiamo quanto segue:

Altrimenti (S contiene un modello di operatore letterale), L viene considerato come una chiamata
dell'operatore di forma "" X <'c1', 'c2', ..., 'ck'> () dove n è la sequenza di caratteri sorgente c1c2 ... ck. [Nota: la sequenza c1c2 ... ck può contenere solo caratteri dal set di caratteri di origine di base. —End nota]

Combinalo con constexpre dovremmo avere l'elaborazione della stringa del tempo di compilazione.

aggiornamento: ho trascurato che stavo leggendo il paragrafo sbagliato, questa forma è consentita per i letterali interi definiti dall'utente e i letterali fluttuanti, ma apparentemente non per i letterali-stringa (§2.13.7.5).
Questa parte della proposta sembra non essere stata accettata.

Detto questo, con il mio sguardo limitato a C ++ 0x, potrebbe sembrare qualcosa del genere (molto probabilmente ho sbagliato qualcosa):

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;

Se l'approccio di Jerrys funziona, allora dovrebbe funzionare comunque:

constexpr unsigned operator "" _hash(const char* s, size_t) {
    return const_hash(s);
}

Bella (e necessaria) combinazione di modelli di lunghezza var e valore constexprletterale definito dall'utente. Non sono sicuro che tu possa usare una stringa letterale come parametro del modello, non hanno un collegamento statico? (lo fanno almeno in C ++ 98 e sono quindi verboten come parametri del modello).
Motti

Ho confuso i paragrafi e ho pensato che questo caso fosse un'eccezione, purtroppo non sembra essere così.
Georg Fritzsche

1
@ Motti: dove gf usa la stringa letterale come parametro del modello? Stai confondendo passando il modello variadico di caratteri come una stringa letterale?
deft_code

Sembra che dalla proposta originale, la versione del modello per i letterali stringa non sia stata accettata (a causa di problemi di concatenazione? Stackoverflow.com/questions/1108008/any-ideas-for-c1y/… - commenti) - peccato.
Georg Fritzsche

1
L'ultima versione di operator ""_hashfunziona per me in Xcode 5.0.2.
cubuspl42

8

Un'altra soluzione basata su quella di Clement JACOB, utilizzando C ++ 11 constexpr (non il C ++ 14 esteso) ma con una sola ricorsione.

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

Qualche spiegazione

  • Stiamo usando una singola ricorsione, in modo che la funzione funzioni bene anche per stringhe più lunghe.
  • La funzione extra combine_crc32ci permette di memorizzare il risultato di una ricorsione sotto una variabile parte di usarla due volte. Questa funzione è una panoramica per la limitazione di C ++ 11 che non consente dichiarazioni di variabili locali.
  • La ctcrc32funzione prevede una stringa letterale, che viene passata come file const char (&)[len]. In questo modo possiamo ottenere la lunghezza della stringa come parametro del modello e non dobbiamo fare affidamento sulle macro.

2
Questa è diventata la mia implementazione preferita, grazie.
Daniel Moodie

6

Quanto segue funziona in GCC 4.6.1 ed è possibile utilizzare hasho packnei blocchi di interruttori.

/* 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));                             
}  

Apparentemente GCC (?) Non consente chiamate ricorsive in cui passiamo s+1con sun puntatore, motivo per cui uso la offvariabile.


5

Se hai un compilatore c ++ 17 e string_view, questo diventa banale, scrivi la versione normale:

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

Si noti che il compilatore potrebbe decidere di non elaborarlo in fase di compilazione se si scrive semplicemente crc32("mystring")(in genere VS tende a farlo). Il trucco per aggirare questo problema è creare una variabile constexpr che dipende dalla valutazione in fase di compilazione del tuo crc32. Tipicamenteconstexpr uint32_t val = crc32("mystring");
Guillaume Gris

3

Ecco un'altra implementazione C ++ 11 (basata sulla risposta @ CygnusX1), che funziona sia con gli array di caratteri constexpr che con le stringhe di runtime:

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

Hai bisogno str.size() + 1perché lennel secondo sovraccarico è strlen(str) + 1dovuto al carattere nullo alla fine.

Non ho aggiunto un sovraccarico per const char *perché incasina il secondo sovraccarico: puoi facilmente aggiungere sovraccarichi per const char *, size_to std::string_view.


2

Questa è una bella domanda.

Sulla base della risposta di Jerry Coffin, ne ho creato un altro compatibile con std :: hash di 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


0

Mi mancava ancora una variante letterale crc32 (che non è possibile con i modelli), quindi ecco il mio suggerimento basato su CygnusX1 . Hai fatto dei test, sentiti libero di dare un feedback.

Testet su MSVC.

PS: odio cercare altre cose da qualche altra parte, quindi ho copiato la tabella CRC in fondo alla mia risposta.

#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 di Dan Bernstein (djb2) (risposte combinate di 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 );
}

Tabella 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
    };
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.