Biên dịch băm chuỗi thời gian


100

Tôi đã đọc ở một vài nơi khác nhau rằng bằng cách sử dụng các ký tự chuỗi mới của C ++ 11, có thể tính toán băm của chuỗi tại thời điểm biên dịch. Tuy nhiên, dường như không ai sẵn sàng ra mặt và nói rằng điều đó sẽ có thể xảy ra hoặc nó sẽ được thực hiện như thế nào.

  • Điều này có khả thi không?
  • Nhà điều hành sẽ trông như thế nào?

Tôi đặc biệt quan tâm đến các trường hợp sử dụng như thế này.

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

Lưu ý: hàm băm thời gian biên dịch không phải giống hệt như tôi đã viết. Tôi đã cố gắng hết sức để đoán xem giải pháp cuối cùng sẽ như thế nào, nhưng meta_hash<"string"_meta>::valuecũng có thể là một giải pháp khả thi.


Tôi dường như không thể tìm thấy bất cứ điều gì, nhưng tôi có thể thấy phải buộc hàm băm của bạn vào một constexpr.
luke

4
Có trình biên dịch nào đã hỗ trợ các ký tự do người dùng xác định không? Gcc thì không ( gcc.gnu.org/projects/cxx0x.html ) và tôi cũng không thấy chúng được đề cập cho VC10. Nếu không có sự hỗ trợ của trình biên dịch, nó chỉ có thể là công việc phỏng đoán, nhưng các ký tự mẫu do người dùng xác định trông giống như nó sẽ có thể.
Georg Fritzsche

1
Nó dễ thương nhưng không hữu ích? Nếu giá trị chuyển đổi là một chuỗi thời gian chạy, bạn cũng cần kiểm tra các va chạm. Có lẽ đóng gói sẽ tốt hơn (câu trả lời của tôi có chức năng đóng gói để nhồi 9 ký tự vào 64 bit).
u0b34a0f6ae

@ u0b34a0f6ae Tại sao phải kiểm tra va chạm?
cubuspl42,

Trình biên dịch sẽ xuất hiện lỗi nếu hai giá trị trường hợp bằng nhau.
Mark Storer

Câu trả lời:


88

Điều này hơi muộn, nhưng tôi đã thành công trong việc triển khai hàm CRC32 thời gian biên dịch với việc sử dụng constexpr. Vấn đề với nó là tại thời điểm viết bài, nó chỉ hoạt động với GCC chứ không phải MSVC cũng như trình biên dịch Intel.

Đây là đoạn mã:

// 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 bằng 0x335CC04A

Hy vọng điều này sẽ giúp bạn!


4
Chắc chắn rồi. Tôi đã thử nghiệm nó dựa trên thuật toán thời gian chạy Python CRC32 (đến từ zlib) và kết quả giống nhau. Trên thực tế, những gì bạn đang cố gắng đạt được chính là lý do tại sao tôi sử dụng kỹ thuật này!
Clement JACOB

2
Cảm ơn vì đã đăng bài này, nó rất hữu ích!
Tamás Szelei

2
Bạn đã thiếu một cờ biên dịch. Hơn nữa, tôi đã phải truyền đến size_t giá trị -1 trong hàm mẫu đệ quy dừng. Phiên bản cập nhật có sẵn tại đây (hoạt động từ Clang 3.3): goo.gl/vPMkfB
Clement JACOB

1
constexprkhông có sẵn trong VS2013, ngoại trừ trong tháng 11 2013 CTP blogs.msdn.com/b/vcblog/archive/2013/11/18/...

2
Bạn đang lặp lại hai lần. Giải pháp này có độ phức tạp theo cấp số nhân đối với độ dài chuỗi và trình biên dịch không đủ thông minh để giải quyết cuộc gọi thứ hai. Kiểm tra các câu trả lời khác để có thể khắc phục sự cố này.
CygnusX1

30

Ít nhất bằng cách tôi đọc §7.1.5 / 3 và §5.19, những điều sau đây có thể hợp pháp:

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

Điều này dường như tuân theo các quy tắc cơ bản trong §7.1.5 / 3:

  1. Dạng là: "return expression;"
  2. Tham số duy nhất của nó là một con trỏ, là kiểu vô hướng và do đó là kiểu chữ.
  3. Trả về của nó là int không dấu, cũng là vô hướng (và do đó theo nghĩa đen).
  4. Không có chuyển đổi ngầm thành kiểu trả về.

Có một số câu hỏi liệu những điều này *inputcó liên quan đến việc chuyển đổi giá trị sang giá trị bất hợp pháp hay không và tôi không chắc mình hiểu các quy tắc trong §5.19 / 2/6/2 1 và §4.1 đủ tốt để chắc chắn về điều đó.

Từ quan điểm thực tế, mã này được chấp nhận bởi (ví dụ) g ++, ít nhất là từ g ++ 4.7.1.

Cách sử dụng sẽ giống như:

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

Tuy nhiên, để tuân thủ các yêu cầu của §5.19 / 2/6/2, bạn có thể phải làm điều gì đó như sau:

// 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. Tôi đang sử dụng các số 'gạch chéo' bổ sung để tham chiếu đến các dấu đầu dòng không được đánh số, vì vậy đây là dấu đầu dòng thứ hai bên trong nếu dấu đầu dòng thứ sáu theo §5.19 / 2. Tôi nghĩ rằng tôi có thể phải nói chuyện với Pete Becker về việc liệu có thể thêm một số loại số / chữ cái / chữ số la mã xuống hệ thống phân cấp để xác định các phần như thế này ...

3
Hai điều sai với điều này. 1: Cuộc gọi đệ quy không được phép trong constexpr, 2: Bạn không có điều kiện tạm dừng (ở đâu *input == nullptr) và theo tôi hiểu thì constexprbạn không thể có điều kiện đó .
Motti

9
Ở đâu nó nói rằng đệ quy không được phép trong constexpr? Theo như tôi thấy, nó chỉ nói rằng bất kỳ chức năng nào bạn gọi phải được đánh dấu constexprt (§5.19 / 2/2). Tôi đã mắc lỗi trong điều kiện kết thúc, điều kiện này hiện tôi đã sửa (tôi đã vô tình sử dụng || nơi đáng lẽ ra &&).
Jerry Coffin

3
constexpr đệ quy. Tôi đã đọc một số biên bản cuộc họp từ năm 2008. Đã có cuộc thảo luận về việc cho phép hoặc không cho phép các hàm constexpr đệ quy. Ý chính là từ ngữ hiện tại không cấm nó, và hơi ngụ ý nó. Ủy ban cảm thấy rằng một tính năng mạnh mẽ như vậy nên được viết rõ ràng. Đó là vào năm 2008, tôi không biết chuyện gì đã xảy ra kể từ đó.
deft_code

3
Đúng - tôi có lẽ nên chỉ ra rằng tôi đã đi từ N3000, mà (tôi tin rằng) hiện là bản nháp gần đây nhất. Tôi khá chắc rằng nó đã bị cấm tại một thời điểm, nhưng ít nhất là bây giờ nó dường như được cho phép.
Jerry Coffin

2
Ừm, toán tử && đang trả về bool, vì vậy hàm này có thể không hoạt động theo ý bạn. Đặc biệt, nó trả về 0 cho bất kỳ chuỗi trống nào và có thể một số chuỗi khác bắt đầu bằng một ký tự sẽ chuyển đổi thành (unsigned)-1nếu có; và trả về 1 cho tất cả các chuỗi khác. Viết lại với toán tử điều kiện bậc ba?
ndkrempel

13

Đây là một nỗ lực để giải quyết vấn đề của OP chính xác nhất có thể.

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

ví dụ trực tiếp .

Lưu ý sự khác biệt chính - std::hashkhông thể được sử dụng, vì chúng tôi không có quyền kiểm soát std::hashthuật toán của và chúng tôi phải thực hiện lại nó như một constexprđể đánh giá nó tại thời điểm biên dịch. Ngoài ra, không có hàm băm "trong suốt" std, vì vậy bạn không thể (không tạo một std::string) hàm băm một bộ đệm ký tự thô là a std::string.

Tôi đã gắn trình std::stringbăm tùy chỉnh (với const char*hỗ trợ trong suốt ) vào một my_hashkhông gian tên, vì vậy bạn có thể lưu trữ nó trong một std::unordered_mapnếu bạn cần sự nhất quán.

Dựa trên câu trả lời xuất sắc của @ JerryCoffin và chuỗi bình luận bên dưới nó, nhưng với nỗ lực viết nó bằng các phương pháp hay nhất C ++ 11 hiện tại (trái ngược với việc dự đoán chúng!).

Lưu ý rằng việc sử dụng "hàm băm thô" cho một switchcâu lệnh caselà rất nguy hiểm. Bạn sẽ muốn ==so sánh sau đó để xác nhận rằng nó hoạt động.


2
Liên kết ví dụ trực tiếp dường như bị sai / lỗi thời?
fuenfundachtzig

@fuenfundachtzig Bạn có tin là tôi vừa sửa được không?
Yakk - Adam Nevraumont,

13

Đoạn mã này dựa trên đoạn mã của Clement JACOB. Nhưng cũng hoạt động với tiếng kêu. Và nó sẽ nhanh hơn khi biên dịch (nó chỉ có một lệnh gọi đệ quy, không phải hai như trong bài viết gốc).

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

Xem bằng chứng về khái niệm tại đây


1
Cảm ơn bạn, câu trả lời của Jacob hoạt động tốt cho những gì tôi muốn trên GCC nhưng msvc đã gặp lỗi với chuỗi lớn hơn. Câu trả lời của bạn hoạt động trên msvc với các chuỗi lớn hơn mà tôi cần băm.
Daniel Moodie

8

Lưu ý rằng biểu mẫu hiển thị ở đây không được chấp nhận vào tiêu chuẩn, như đã lưu ý bên dưới.

Quá trình biên dịch chuỗi thời gian được đoán là có thể thực hiện được thông qua các ký tự do người dùng xác định được đề xuất trong N2765 .
Như tôi đã đề cập, tôi không biết bất kỳ trình biên dịch nào hiện đang triển khai nó và nếu không có hỗ trợ trình biên dịch thì chỉ có thể là công việc phỏng đoán.

Trong §2.13.7.3 và 4 của bản nháp, chúng tôi có những điều sau:

Nếu không (S chứa một mẫu toán tử theo nghĩa đen), L được coi như một lệnh gọi của
toán tử biểu mẫu "" X <'c1', 'c2', ..., 'ck'> () trong đó n là chuỗi ký tự nguồn c1c2 ... ck. [Lưu ý: Dãy c1c2 ... ck chỉ có thể chứa các ký tự từ bộ ký tự nguồn cơ bản. —Gửi ghi chú]

Kết hợp điều đó với constexprvà chúng ta nên có xử lý chuỗi thời gian biên dịch.

cập nhật: tôi đã bỏ qua rằng tôi đã đọc sai đoạn văn, biểu mẫu này được phép cho người dùng xác định-số nguyên-chữ và -floating-chữ, nhưng dường như không cho-chuỗi-chữ (§2.13.7.5).
Phần này của đề xuất dường như đã không được chấp nhận.

Điều đó đang được nói, với cái nhìn hạn chế của tôi về C ++ 0x, nó có thể trông giống như thế này (rất có thể tôi đã gặp sai sót):

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;

Nếu cách tiếp cận Jerrys hoạt động, thì những điều sau đây sẽ hoạt động tuy nhiên:

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

Sự kết hợp tuyệt vời (và cần thiết) giữa các mẫu độ dài var và constexprnghĩa đen do người dùng xác định. Tôi không chắc bạn có thể sử dụng một chuỗi ký tự làm tham số mẫu, chúng có liên kết tĩnh không? (ít nhất chúng làm trong C ++ 98 và do đó được chi tiết dưới dạng tham số mẫu).
Motti

Tôi đã trộn lẫn các đoạn và nghĩ rằng trường hợp này là một ngoại lệ - thật đáng buồn là nó không xuất hiện như vậy.
Georg Fritzsche

1
@Motti: gf đang sử dụng chuỗi ký tự làm tham số mẫu ở đâu? Bạn có đang nhầm lẫn khi chuyển mẫu đa dạng của các ký tự dưới dạng một chuỗi ký tự không?
deft_code

Có vẻ như từ đề xuất ban đầu, phiên bản mẫu cho chuỗi ký tự không được chấp nhận (do vấn đề nối? Stackoverflow.com/questions/1108008/any-ideas-for-c1y/… - comments) - quá tệ.
Georg Fritzsche

1
Phiên bản cuối cùng operator ""_hashhoạt động đối với tôi là Xcode 5.0.2.
cubuspl42,

8

Một giải pháp khác dựa trên giải pháp của Clement JACOB, sử dụng C ++ 11 constexpr (không phải C ++ 14 mở rộng) nhưng chỉ có một đệ quy.

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

Một số giải thích

  • Chúng tôi đang sử dụng một đệ quy duy nhất để hàm hoạt động tốt ngay cả đối với các chuỗi dài hơn.
  • Hàm phụ combine_crc32cho phép chúng ta lưu trữ kết quả của một đệ quy dưới một biến partvà sử dụng nó hai lần. Hàm này là một sự thay thế cho C ++ 11 giới hạn không cho phép khai báo biến cục bộ.
  • Các ctcrc32chức năng hy vọng một literal chuỗi, được thông qua như là một const char (&)[len]. Bằng cách này, chúng ta có thể lấy độ dài chuỗi làm tham số mẫu và không phải dựa vào macro.

2
Đây là cách triển khai yêu thích của tôi, cảm ơn bạn.
Daniel Moodie

6

Phần sau hoạt động trong GCC 4.6.1 và bạn có thể sử dụng một trong hai hashhoặc packtrong các khối chuyển đổi.

/* 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 dường như (?) Không cho phép các cuộc gọi đệ quy mà chúng ta truyền s+1với smột con trỏ, đó là lý do tại sao tôi sử dụng offbiến.


5

Nếu bạn có trình biên dịch c ++ 17 và string_view, điều này trở nên nhỏ, chỉ cần viết phiên bản bình thường:

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

Lưu ý rằng trình biên dịch có thể quyết định không xử lý điều này tại thời điểm biên dịch nếu bạn chỉ cần viết crc32("mystring")(thông thường VS có xu hướng làm điều đó). Mẹo để tránh vấn đề đó là tạo một biến constexpr phụ thuộc vào đánh giá thời gian biên dịch của crc32. Điển hìnhconstexpr uint32_t val = crc32("mystring");
Guillaume Gris

3

Đây là một triển khai C ++ 11 khác (dựa trên câu trả lời @ CygnusX1), hoạt động với cả mảng char constexpr và chuỗi thời gian chạy:

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

Bạn cần str.size() + 1bởi vì lentrong lần quá tải thứ hai là strlen(str) + 1do ký tự null ở cuối.

Tôi đã không thêm quá tải cho const char *vì nó gây rối với quá tải thứ hai - Bạn có thể dễ dàng thêm quá tải cho const char *, size_thoặc std::string_view.


2

Đây là một câu hỏi hay.

Dựa trên câu trả lời của Jerry Coffin, tôi đã tạo một cái khác tương thích với std :: hash của 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

Tôi vẫn còn thiếu một biến thể crc32 theo nghĩa đen (không thể có với các mẫu), vì vậy đây là đề xuất của tôi dựa trên CygnusX1 . Đã thực hiện một số thử nghiệm, vui lòng đưa ra phản hồi.

Testet trên MSVC.

Tái bút: Tôi ghét tìm kiếm những thứ bổ sung ở nơi khác, vì vậy tôi đã sao chép bảng CRC ở cuối câu trả lời của mình.

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

Thay thế với thuật toán từ Dan Bernstein (djb2) (câu trả lời kết hợp từ 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 );
}

Bảng 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
    };
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.