Kompilasi hashing string waktu

100

Saya telah membaca di beberapa tempat berbeda bahwa menggunakan literal string baru C ++ 11 dimungkinkan untuk menghitung hash string pada waktu kompilasi. Namun, tampaknya tidak ada yang siap untuk keluar dan mengatakan bahwa itu akan mungkin atau bagaimana itu akan dilakukan.

  • Apakah ini mungkin?
  • Seperti apa rupa operatornya?

Saya sangat tertarik dengan kasus penggunaan seperti ini.

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

Catatan: fungsi hash waktu kompilasi tidak harus terlihat persis seperti yang saya tulis. Saya melakukan yang terbaik untuk menebak seperti apa solusi akhirnya, tetapi meta_hash<"string"_meta>::valuejuga bisa menjadi solusi yang layak.

deft_code
sumber
Sepertinya saya juga tidak dapat menemukan apa pun, tetapi saya bisa melihat harus memaksa fungsi hashing Anda menjadi konstekspr.
Lukas
4
Apakah ada kompiler yang sudah mendukung literal yang ditentukan pengguna? Gcc tidak ( gcc.gnu.org/projects/cxx0x.html ) dan saya juga belum menemukan mereka disebutkan untuk VC10. Tanpa dukungan compiler hanya bisa menebak bekerja, tetapi literal ditetapkan pengguna templated terlihat seperti itu harus mungkin.
Georg Fritzsche
1
Itu lucu tapi tidak berguna? Jika nilai sakelar adalah string waktu proses, Anda juga perlu memeriksa tabrakan. Mungkin pengepakan lebih baik (jawaban saya memiliki fungsi paket untuk memasukkan 9 karakter menjadi 64 bit).
u0b34a0f6ae
@ u0b34a0f6ae Mengapa memeriksa tabrakan?
cubuspl42
Kompilator harus mengeluarkan kesalahan jika dua nilai kasus sama.
Mark Storer

Jawaban:

88

Ini agak terlambat, tetapi saya berhasil menerapkan fungsi CRC32 waktu kompilasi dengan menggunakan constexpr . Masalahnya adalah pada saat penulisan, ini hanya bekerja dengan GCC dan bukan kompiler MSVC atau Intel.

Berikut ini cuplikan kodenya:

// 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 sama dengan 0x335CC04A

Semoga ini bisa membantu Anda!

Clement JACOB
sumber
4
Ya, tentu saja. Saya mengujinya terhadap algoritma runtime Python CRC32 (berasal dari zlib) dan hasilnya sama. Sebenarnya, apa yang Anda coba capai adalah mengapa saya menggunakan teknik ini!
Clement JACOB
2
Terima kasih telah memposting ini, ini sangat berguna!
Tamás Szelei
2
Anda kehilangan bendera kompilasi. Selain itu saya harus memasukkan ke size_t nilai -1 dalam fungsi template rekursi berhenti. Versi yang diperbarui tersedia di sini (bekerja dari Clang 3.3): goo.gl/vPMkfB
Clement JACOB
1
constexprtidak tersedia di VS2013, kecuali pada November 2013 CTP blogs.msdn.com/b/vcblog/archive/2013/11/18/...
2
Anda mengulang dua kali. Solusi ini memiliki kompleksitas eksponensial sehubungan dengan panjang string, dan kompilator tidak cukup pintar untuk mengeliminasi panggilan kedua. Periksa jawaban lain untuk kemungkinan perbaikan untuk masalah ini.
CygnusX1
30

Setidaknya dengan membaca §7.1.5 / 3 dan §5.19, berikut ini mungkin sah:

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

Ini tampaknya mengikuti aturan dasar dalam §7.1.5 / 3:

  1. Bentuknya adalah: "ekspresi kembali;"
  2. Parameter satu-satunya adalah pointer, yang merupakan tipe skalar, dan oleh karena itu tipe literal.
  3. Kembalinya adalah unsigned int, yang juga skalar (dan karenanya literal).
  4. Tidak ada konversi implisit ke tipe pengembalian.

Ada beberapa pertanyaan apakah ini *inputmelibatkan konversi nilai l ilegal ke nilai r, dan saya tidak yakin saya memahami aturan di §5.19 / 2/6/2 1 dan §4.1 cukup baik untuk memastikannya.

Dari sudut pandang praktis, kode ini diterima oleh (misalnya) g ++, setidaknya sejauh g ++ 4.7.1.

Penggunaannya akan seperti ini:

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

Untuk memenuhi persyaratan §5.19 / 2/6/2 Anda mungkin harus melakukan hal seperti ini:

// 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. Saya menggunakan angka 'garis miring' ekstra untuk merujuk ke poin peluru yang tidak diberi nomor, jadi ini adalah poin peluru kedua di dalam jika poin keenam di bawah §5.19 / 2. Saya pikir saya mungkin harus berbicara dengan Pete Becker tentang apakah mungkin menambahkan semacam angka / huruf / angka romawi ke bawah hierarki untuk mengidentifikasi bagian seperti ini ...
Jerry Coffin
sumber
3
Ada dua hal yang salah dengan ini. 1: Panggilan berulang tidak diperbolehkan masuk constexpr, 2: Anda tidak memiliki kondisi terputus-putus (di mana *input == nullptr) dan seperti yang saya pahami constexprAnda tidak dapat memilikinya.
Motti
9
Di mana dikatakan rekursi tidak diperbolehkan dalam sebuah konstekspr? Sejauh yang saya bisa lihat, itu hanya mengatakan bahwa fungsi apa pun yang Anda panggil harus ditandai sendiri constexprt (§5.19 / 2/2). Saya membuat kesalahan dalam kondisi penghentian, yang sekarang telah saya perbaiki (saya tidak sengaja menggunakan || di mana seharusnya &&).
Jerry Coffin
3
constexpr rekursif. Saya membaca beberapa notulen rapat dari tahun 2008. Ada diskusi tentang mengizinkan atau melarang fungsi constexpr rekursif. Intinya adalah kata-kata saat ini tidak melarangnya, dan sedikit menyiratkannya. Panitia merasa bahwa fitur yang begitu kuat harus dijabarkan secara eksplisit. Itu terjadi di tahun 2008, saya tidak tahu apa yang terjadi sejak saat itu.
deft_code
3
Benar - Saya mungkin harus menunjukkan bahwa saya menggunakan N3000, yang (menurut saya) adalah draf terbaru. Saya cukup yakin itu dilarang pada satu waktu, tetapi setidaknya untuk saat ini tampaknya diizinkan.
Jerry Coffin
2
Um, operator && mengembalikan bool, jadi fungsi ini mungkin tidak melakukan apa yang Anda inginkan. Secara khusus ia mengembalikan 0 untuk setiap string kosong dan mungkin beberapa string lain yang dimulai dengan sebuah char yang akan diubah (unsigned)-1jika ada; dan mengembalikan 1 untuk semua string lainnya. Tulis ulang dengan operator bersyarat terner?
ndkrempel
13

Ini adalah upaya untuk menyelesaikan masalah OP secepat mungkin.

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

contoh hidup .

Perhatikan perbedaan utama - std::hashtidak dapat digunakan, karena kita tidak memiliki kendali atas std::hashalgoritme, dan kita harus menerapkannya kembali sebagai constexpruntuk mengevaluasinya pada waktu kompilasi. Selain itu, tidak ada hash "transparan" di dalamnya std, jadi Anda tidak bisa (tanpa membuat std::string) hash buffer karakter mentah sebagai std::string.

Saya memasukkan std::stringhasher khusus (dengan const char*dukungan transparan ) ke dalam my_hashnamespace, sehingga Anda dapat menyimpannya std::unordered_mapjika Anda membutuhkan konsistensi.

Berdasarkan jawaban @JerryCoffin yang sangat baik dan utas komentar di bawahnya, tetapi dengan upaya untuk menulisnya dengan praktik terbaik C ++ 11 saat ini (bukan mengantisipasinya!).

Perhatikan bahwa menggunakan "hash mentah" untuk switchpernyataan caseitu berbahaya. Anda akan ingin melakukan ==perbandingan setelahnya untuk memastikannya berhasil.

Yakk - Adam Nevraumont
sumber
2
Tautan contoh langsung sepertinya salah / ketinggalan zaman?
fuenfundachtzig
@fuenfundachtzig Apakah Anda percaya saya baru saja memperbaikinya?
Yakk - Adam Nevraumont
13

Cuplikan ini didasarkan pada Clement JACOB. Tapi bekerja dengan dentang juga. Dan itu harus lebih cepat dalam kompilasi (hanya memiliki satu panggilan rekursif, bukan dua seperti di posting asli).

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

Lihat bukti konsep di sini

tower120
sumber
1
Terima kasih, jawaban Jacob berfungsi dengan baik untuk apa yang saya inginkan di GCC tetapi D3D memberikan kesalahan dengan string yang lebih besar. Jawaban Anda berfungsi pada D3D dengan string lebih besar yang perlu saya hash.
Daniel Moodie
8

Perhatikan bahwa formulir yang ditampilkan di sini tidak diterima ke dalam standar, seperti yang disebutkan di bawah.

Pemrosesan string waktu kompilasi diperkirakan menjadi mungkin melalui literal yang ditentukan pengguna yang diusulkan di N2765 .
Seperti yang telah saya sebutkan, saya tidak tahu ada kompilator yang saat ini mengimplementasikannya dan tanpa dukungan kompilator hanya ada dugaan kerja.

Dalam §2.13.7.3 dan 4 draf, kami memiliki yang berikut ini:

Jika tidak (S berisi templat operator literal), L diperlakukan sebagai panggilan dari
operator formulir "" X <'c1', 'c2', ..., 'ck'> () di mana n adalah urutan karakter sumber c1c2 ... ck. [Catatan: Urutan c1c2 ... ck hanya dapat berisi karakter dari kumpulan karakter sumber dasar. —Kirim catatan]

Gabungkan itu dengan constexprdan kita harus memiliki pemrosesan string waktu kompilasi.

update: saya mengabaikan bahwa saya membaca paragraf yang salah, formulir ini diizinkan untuk literal-integer-yang ditentukan pengguna dan -floating-literals, tetapi tampaknya tidak untuk -string-literal (§2.13.7.5).
Bagian dari proposal ini sepertinya belum diterima.

Yang sedang berkata, dengan pandangan terbatas saya pada C ++ 0x, mungkin terlihat seperti ini (kemungkinan besar saya salah):

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;

Jika pendekatan Jerry berhasil, maka berikut ini seharusnya berhasil:

constexpr unsigned operator "" _hash(const char* s, size_t) {
    return const_hash(s);
}
Georg Fritzsche
sumber
Kombinasi yang bagus (dan perlu) dari templat panjang var dan constexprliteral yang ditentukan pengguna. Saya tidak yakin Anda dapat menggunakan string literal sebagai parameter template, bukankah mereka memiliki tautan statis? (setidaknya ada di C ++ 98 dan oleh karena itu verboten sebagai parameter template).
Motti
Saya telah mencampuradukkan paragraf dan berpikir bahwa kasus ini adalah pengecualian - sayangnya tampaknya tidak demikian.
Georg Fritzsche
1
@Motti: di mana gf menggunakan string literal sebagai parameter template? Apakah Anda bingung meneruskan template variadic dari karakter sebagai string literal?
deft_code
Tampaknya dari proposal asli, versi template untuk string literal tidak diterima (karena masalah penggabungan? Stackoverflow.com/questions/1108008/any-ideas-for-c1y/… - comments) - sayang sekali.
Georg Fritzsche
1
Versi terakhir operator ""_hashberfungsi untuk saya di Xcode 5.0.2.
cubuspl42
8

Solusi lain berdasarkan Clement JACOB, menggunakan C ++ 11 constexpr (bukan C ++ 14 diperpanjang) tetapi hanya memiliki satu rekursi.

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

Beberapa penjelasan

  • Kami menggunakan rekursi tunggal, sehingga fungsinya bekerja dengan baik bahkan untuk string yang lebih panjang.
  • Fungsi ekstra combine_crc32memungkinkan kita menyimpan hasil rekursi di bawah variabelpart dan menggunakannya dua kali. Fungsi ini adalah panduan untuk batasan C ++ 11 yang melarang deklarasi variabel lokal.
  • The ctcrc32Fungsi mengharapkan literal string, yang dilewatkan sebagai const char (&)[len]. Dengan cara ini kita bisa mendapatkan panjang string sebagai parameter template dan tidak harus bergantung pada makro.
CygnusX1
sumber
2
Ini akhirnya menjadi implementasi favorit saya, terima kasih.
Daniel Moodie
6

Yang berikut ini berfungsi di GCC 4.6.1, dan Anda dapat menggunakan salah satu hashatau packdi blok sakelar.

/* 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 tampaknya (?) Tidak mengizinkan panggilan rekursif di mana kami meneruskan s+1dengan spointer, itulah sebabnya saya menggunakan offvariabel.

u0b34a0f6ae
sumber
5

Jika Anda memiliki compiler c ++ 17 dan string_view, ini menjadi sepele, cukup tulis versi normalnya:

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
sumber
Perhatikan bahwa kompilator mungkin memutuskan untuk tidak memproses ini pada waktu kompilasi jika Anda hanya menulis crc32("mystring")(biasanya VS cenderung melakukan itu). Trik untuk menghindari masalah itu adalah dengan membuat variabel constexpr yang bergantung pada evaluasi waktu kompilasi crc32 Anda. Biasanyaconstexpr uint32_t val = crc32("mystring");
Guillaume Gris
3

Berikut adalah implementasi C ++ 11 lainnya (berdasarkan jawaban @ CygnusX1), yang bekerja dengan array karakter constexpr dan string 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;
}

Anda perlu str.size() + 1karena lendalam kelebihan kedua adalah strlen(str) + 1karena karakter null di akhir.

Saya tidak menambahkan kelebihan beban const char *karena mengacaukan kelebihan beban kedua - Anda dapat dengan mudah menambahkan beban berlebih untuk const char *, size_tatau std::string_view.

Suaka
sumber
2

Ini pertanyaan yang bagus.

Berdasarkan jawaban Jerry Coffin, saya telah membuat yang lain yang kompatibel dengan std :: hash 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
sumber
0

Saya masih kehilangan varian crc32-literal (yang tidak dimungkinkan dengan template), jadi inilah saran saya berdasarkan CygnusX1 . Lakukan beberapa pengujian, silakan berikan umpan balik.

Testet di MSVC.

PS: Saya benci mencari hal tambahan di tempat lain, jadi saya menyalin tabel CRC di bagian bawah jawaban saya.

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

Alternatif dengan algoritme dari Dan Bernstein (djb2) (jawaban gabungan dari 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 );
}

Tabel 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
sumber