Derleme zamanı dizesi karması


101

Birkaç farklı yerde, C ++ 11'in yeni dizgi değişmezlerini kullanarak, derleme zamanında bir dizenin karmasını hesaplamanın mümkün olabileceğini okudum. Ancak kimse çıkıp bunun mümkün olacağını ya da nasıl yapılacağını söylemeye hazır görünmüyor.

  • Mümkün mü?
  • Operatör nasıl görünürdü?

Özellikle bunun gibi kullanım durumlarıyla ilgileniyorum.

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

Not: derleme zamanı hash işlevi tam olarak yazdığım gibi görünmek zorunda değildir. Nihai çözümün neye benzeyeceğini tahmin etmek için elimden gelenin en iyisini yaptım, ancak meta_hash<"string"_meta>::valueuygulanabilir bir çözüm de olabilirdi.


Ben de bir şey bulamıyorum, ama hash fonksiyonunuzu bir constexpr'e zorlamak zorunda olduğunuzu görebiliyordum.
luke

4
Kullanıcı tanımlı değişmezleri zaten destekleyen bir derleyici var mı? Gcc bilmiyor ( gcc.gnu.org/projects/cxx0x.html ) ve bunlardan VC10 için bahsedildiğini de bulamadım. Derleyici desteği olmadan yalnızca tahmin çalışması yapılabilir, ancak şablonlu kullanıcı tanımlı değişmez değerler mümkün gibi görünür .
Georg Fritzsche

1
Sevimli ama kullanışlı değil mi? Anahtar değeri bir çalışma zamanı dizesiyse, çarpışmaları da kontrol etmeniz gerekir. Belki paketleme daha iyidir (cevabımın 9 karakteri 64 bite doldurmak için bir paket işlevi vardır).
u0b34a0f6ae

@ u0b34a0f6ae Neden çarpışmaları kontrol etmeli?
cubuspl42

İki durum değeri eşitse derleyici bir hata vermelidir.
Mark Storer

Yanıtlar:


89

Bu biraz gecikti, ancak .txt kullanarak derleme zamanı CRC32 işlevini uygulamayı başardım constexpr. Bununla ilgili sorun, yazım sırasında, MSVC veya Intel derleyicisiyle değil, yalnızca GCC ile çalışmasıdır.

İşte kod pasajı:

// 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 0x335CC04A'ya eşittir

Umarım bu size yardımcı olur!


4
Evet kesinlikle. Python CRC32 çalışma zamanı algoritmasına (zlib'den gelen) karşı test ettim ve sonuçlar aynı. Aslında, başarmaya çalıştığınız şey tam olarak bu tekniği neden kullandığım!
Clement JACOB

2
Bunu gönderdiğiniz için teşekkürler, çok faydalı!
Tamás Szelei

2
Bir derleme bayrağı eksikti. Dahası, dur özyineleme şablon fonksiyonunda -1 değerini size_t'ye çevirmek zorunda kaldım. Güncellenmiş sürüme buradan ulaşılabilir (Clang 3.3'ten itibaren): goo.gl/vPMkfB
Clement JACOB


2
İki kez yineliyorsun. Bu çözüm, dizgi uzunluğuna göre üstel bir karmaşıklığa sahiptir ve derleyici ikinci çağrıyı atlatacak kadar akıllı değildir. Bu sorunun olası çözümü için diğer yanıtları kontrol edin.
CygnusX1

30

En azından §7.1.5 / 3 ve §5.19 okumalarıma göre, aşağıdakiler meşru olabilir:

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

Bu, §7.1.5 / 3'teki temel kuralları izliyor gibi görünüyor:

  1. Form şudur: "dönüş ifadesi;"
  2. Tek parametresi, skaler bir tür olan bir göstericidir ve bu nedenle bir değişmez tiptir.
  3. Dönüşü, aynı zamanda skaler olan (ve dolayısıyla gerçek olan) işaretsiz int'dir.
  4. Dönüş türüne örtük bir dönüştürme yoktur.

*inputLarın değer dönüşümü için yasa dışı bir değer içerip içermediğine dair bazı sorular var ve §5.19 / 2/6/2 1 ve §4.1'deki kuralları bundan emin olmak için yeterince iyi anladığımdan emin değilim .

Pratik bir bakış açısıyla, bu kod (bir örnek için) g ++ tarafından, en azından g ++ 4.7.1 kadar geriye doğru kabul edilir.

Kullanım şöyle bir şey olacaktır:

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

§5.19 / 2/6/2 şartlarına uymak için, buna benzer bir şey yapmanız gerekebilir:

// 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. Numaralandırılmamış madde işaretli noktalara atıfta bulunmak için ekstra 'eğik çizgi' sayılarını kullanıyorum, bu nedenle bu, §5.19 / 2 altındaki altıncı madde işaretiyse içerideki ikinci madde işareti noktasıdır. Sanırım böyle parçaları tanımlamak için hiyerarşiye bir tür sayı / harf / roma rakamları eklemenin mümkün olup olmadığı konusunda Pete Becker ile konuşmam gerekebilir ...

3
Bunda yanlış olan iki şey var. 1: Özyinelemeli aramalara izin verilmiyor constexpr, 2: Durma koşulunuz yok (nerede *input == nullptr) ve anladığım kadarıyla constexprbir tane olamazsınız.
Motti

9
Bir constexpr'de özyinelemeye izin verilmediğini söyler? Görebildiğim kadarıyla, sadece çağırdığınız herhangi bir işlevin kendilerinin constexprt (§5.19 / 2/2) olarak işaretlenmesi gerektiğini söylüyor. Sonlandırma koşulunda, şimdi düzelttiğim bir hata yaptım (yanlışlıkla || olması gereken yerde && kullandım).
Jerry Coffin

3
özyinelemeli constexpr. 2008'den kalma bazı toplantı tutanaklarını okudum. Özyinelemeli constexpr işlevlerine izin vermek veya vermemekle ilgili tartışmalar oldu. İşin özü, mevcut ifadenin onu yasaklamamasıydı ve hafifçe ima ediyordu. Komite, böylesine güçlü bir özelliğin açıkça ifade edilmesi gerektiğini hissetti. Bu 2008 yılındaydı, o zamandan beri ne olduğunu bilmiyorum.
deft_code

3
Doğru - muhtemelen şu anda en son taslak olan (inanıyorum) N3000'den gittiğime işaret etmeliydim. Bir zamanlar yasak olduğuna eminim, ama en azından şimdilik izin veriliyor gibi görünüyor.
Jerry Coffin

2
Um, && operatörü bir bool döndürüyor, bu nedenle bu işlev muhtemelen istediğiniz şeyi yapmıyor. Özellikle, herhangi bir boş dizge için 0 döndürür ve muhtemelen bir karakterle başlayan bazı diğer dizeler (unsigned)-1varsa, buna dönüşür ; ve diğer tüm dizeler için 1 döndürür. Üçlü koşullu operatör ile yeniden yazılsın mı?
ndkrempel

13

Bu, OP'nin sorununu olabildiğince tam olarak çözme girişimidir.

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

canlı örnek .

- Ana farka dikkat std::hashbiz üzerinde kontrol yok gibi, kullanılamaz std::hash'ın algoritması ve biz gereken bir olarak reimplement constexprderleme anında değerlendirmek için. Ayrıca, içinde "saydam" karmalar yoktur std, bu nedenle std::stringham karakter tamponunu bir std::string.

Ben sıkışmış std::string(saydam ile özel hasher const char*bir içine destek) my_hashBir depolamak böylece, ad std::unordered_maptutarlılık gerekiyorsa.

@ JerryCoffin'in mükemmel cevabına ve altındaki yorum dizisine dayanıyor, ancak bunu mevcut C ++ 11 en iyi uygulamaları ile yazma girişimiyle (onları tahmin etmenin aksine!).

Bir switchifade için "ham karma" kullanmanın casetehlikeli olduğunu unutmayın. Daha ==sonra işe yaradığını doğrulamak için bir karşılaştırma yapmak isteyeceksiniz .


2
Canlı örnek bağlantısı yanlış / güncelliğini yitirmiş gibi görünüyor?
fuenfundachtzig

@fuenfundachtzig Bunu düzelttiğime inanır mısın?
Yakk - Adam Nevraumont

13

Bu pasaj, Clement JACOB'a dayanmaktadır. Ama clang ile de çalışır. Ve derlemede daha hızlı olmalıdır (orijinal gönderideki gibi iki değil, yalnızca bir özyinelemeli çağrı vardır).

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

Burada kavram kanıtını görün


1
Teşekkürler, Jacob'ın cevabı GCC'de istediğim şey için iyi çalışıyor, ancak msvc daha büyük dizelerle hatalar atıyordu. Cevabınız, karma oluşturmam gereken daha büyük dizelerle msvc üzerinde çalışıyor.
Daniel Moodie

8

Burada gösterilen formun, aşağıda belirtildiği gibi standarda kabul edilmediğini unutmayın.

Derleme zamanı dizisi işlemenin, N2765'te önerilen kullanıcı tanımlı değişmez değerler aracılığıyla mümkün olacağı tahmin edilmektedir .
Daha önce de belirttiğim gibi, şu anda onu uygulayan herhangi bir derleyici bilmiyorum ve derleyici desteği olmadan sadece tahmin çalışması yapılabilir.

§2.13.7.3 ve 4'ünde taslak biz şu var:

Aksi takdirde (S değişmez bir operatör şablonu içerir), L form
operatörünün bir çağrısı olarak kabul edilir "" X <'c1', 'c2', ..., 'ck'> () burada n kaynak karakter dizisi c1c2'dir ... ck. [Not: c1c2 ... ck dizisi yalnızca temel kaynak karakter setinden karakterler içerebilir. - notu gönder]

Bunu ile birleştirin constexprve zaman dizisi işlemesini derlemeliyiz.

güncelleme: yanlış paragrafı okuduğumu göz ardı ettim, bu forma kullanıcı tanımlı tamsayı değişmezleri ve -düzenli-değişmezler için izin veriliyor, ancak görünüşe göre -dizgi-değişmezleri için değil (§2.13.7.5).
Teklifin bu kısmı kabul edilmemiş görünüyor.

Bu varlık C ++ 0x benim sınırlı belirti ile, bu sözü olabilir böyle bir şey (i büyük olasılıkla bir şey yanlış var) bakmak:

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;

Eğer Jerrys yaklaşım çalışır, sonra aşağıdaki ancak çalışması gerekir:

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

Değişken uzunluk şablonlarının ve constexprkullanıcı tanımlı değişmez değerin güzel (ve gerekli) kombinasyonu . Şablon parametresi olarak bir dizge kullanabileceğinizden emin değilim, statik bağlantıları yok mu? (en azından C ++ 98'de yaparlar ve bu nedenle şablon parametreleri olarak verboten olurlar).
Motti

Paragrafları karıştırdım ve bu davanın bir istisna olduğunu düşündüm - ne yazık ki öyle görünmüyor.
Georg Fritzsche

1
@Motti: gf bir şablon parametresi olarak dizge değerini kullanan nerede? Değişken karakter şablonunu bir dize değişmezi olarak geçirirken kafa karıştırıcı mısınız?
deft_code

Görünüşe göre orijinal teklif, dizgi değişmezleri için şablon sürümü kabul edilmedi (birleştirme sorunları nedeniyle? Stackoverflow.com/questions/1108008/any-ideas-for-c1y/… - yorumlar) - çok kötü.
Georg Fritzsche

1
Çalışmalarının son sürümü operator ""_hashXcode 5.0.2'de benim için.
cubuspl42

8

C ++ 11 constexpr kullanan (genişletilmiş C ++ 14 değil), ancak yalnızca bir özyinelemeye sahip olan Clement JACOB'un çözümüne dayanan başka bir çözüm.

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

Bazı açıklamalar

  • Tek bir özyineleme kullanıyoruz, böylece işlev daha uzun dizeler için bile iyi çalışıyor.
  • Ekstra işlev combine_crc32, bir özyinelemenin sonucunu bir değişken altında saklamamızı partve onu iki kez kullanmamızı sağlar. Bu işlev, yerel değişken bildirimlerine izin vermeyen C ++ 11 sınırlaması için bir çözümdür.
  • ctcrc32Fonksiyon olarak iletilen bir dize, bekler const char (&)[len]. Bu şekilde, dizgi uzunluğunu bir şablon parametresi olarak alabiliriz ve makrolara güvenmek zorunda kalmayız.

2
Bu benim en sevdiğim uygulama oldu, teşekkürler.
Daniel Moodie

6

Aşağıdakiler GCC 4.6.1'de çalışır ve bunlardan birini hashveya packanahtar bloklarında kullanabilirsiniz.

/* 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 görünüşte (?) Bir işaretçi s+1ile geçtiğimiz yerde özyinelemeli çağrılara izin vermiyor s, bu yüzden offdeğişkeni kullanıyorum.


5

Bir c ++ 17 derleyiciniz ve string_view'unuz varsa, bu önemsiz hale gelir, sadece normal sürümü yazın:

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

Yalnızca yazarsanız derleyicinin bunu derleme zamanında işlememeye karar verebileceğini unutmayın crc32("mystring")(tipik olarak VS bunu yapma eğilimindedir). Bu sorunu aşmanın püf noktası, crc32'nizin derleme zamanı değerlendirmesine bağlı olan bir constexpr değişkeni oluşturmaktır. Genellikleconstexpr uint32_t val = crc32("mystring");
Guillaume Gris

3

Burada, hem constexpr char dizileri hem de çalışma zamanı dizeleri ile çalışan başka bir C ++ 11 uygulaması (@ CygnusX1 cevabına dayalı) verilmiştir:

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

İhtiyacınız var str.size() + 1çünkü lenikinci aşırı yüklemede strlen(str) + 1sondaki boş karakterden kaynaklanıyor.

Ben için bir aşırı eklemek vermedi const char *ikinci aşırı karıştıran biridir çünkü - Kolayca için aşırı yüklenmeleri ekleyebilir const char *, size_tveya std::string_view.


2

Bu güzel bir soru.

Jerry Coffin'in cevabına dayanarak, Visual Studio 2017'nin std :: hash ile uyumlu başka bir tane oluşturdum.

#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

Hala bir crc32-literal varyantını kaçırıyordum (ki bu şablonlarla mümkün değil), işte CygnusX1'e dayanan önerim . Biraz test yaptım, geri bildirimde bulunmaktan çekinmeyin.

MSVC üzerinde test seti.

Not: Başka bir yerde ek şeyler aramaktan nefret ediyorum, bu yüzden cevabımın altındaki CRC tablosunu kopyaladım.

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

Dan Bernstein'dan algoritma ile alternatif (djb2) ( Jerry Coffin + Georg Fritzsche'den birleşik cevaplar )

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

Crc32 tablosu:

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
    };
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.