基本上我想在我的代码中能够做到这一点:
Engine.getById(WSID('some-id'));
哪个应该被
转换 Engine.getById('1a61bc96');
在编译成asm之前。所以在编译时。
这是我的尝试
constexpr int WSID(const char* str) {
boost::crc_32_type result;
result.process_bytes(str,sizeof(str));
return result.checksum();
}
但是我尝试使用MSVC 18(CTP 2013年11月)进行编译时得到了这个
error C3249: illegal statement or sub-expression for 'constexpr' function
如果在编译期间完成,我怎样才能使用这种方式获得WSID
函数?
试过这个:Compile time string hashing
warning C4592: 'crc32': 'constexpr' call evaluation failed; function will be called at run-time
修改
我第一次在Jason Gregory的游戏引擎架构中听说过这种技术。我联系了那位有意回答我的作者:
我们所做的是通过自定义的小型预处理器传递我们的源代码,该预处理器搜索
SID('xxxxxx')
形式的文本,并将单引号之间的任何内容转换为其哈希等效项作为十六进制文字({{ 1}})。 [...]你可以想象通过宏和/或某些模板元编程也可以做到这一点,尽管如你所说让编译器为你做这种工作很棘手。这并非不可能,但编写自定义工具更容易,也更灵活。 [...]
另请注意,我们为
0xNNNNNNNN
文字选择了单引号。这样做是为了让我们在代码编辑器中获得一些合理的语法突出显示,但如果出现问题并且某些未预处理的代码通过编译器,它会引发语法错误,因为单引号通常保留用于单字符文字。另请注意,让您的小预处理工具在某种数据库中缓存字符串至关重要,这样可以在给定哈希码的情况下查找原始字符串。当您调试代码并检查
SID('xxxx')
变量时,调试器通常会向您显示相当难以理解的哈希代码。但是使用SID数据库,您可以编写一个插件,将这些哈希代码转换回其字符串等效项。这样,您就会在观看窗口中看到SID(' foo'),而不是StringId
[...]。此外,游戏应该能够加载这个相同的数据库,以便它可以在屏幕上打印字符串而不是十六进制哈希码,以便进行调试[...]。
但是虽然预处理有一些主要优点,但这意味着我必须准备某种修改文件的输出系统(那些将存储在别处,然后我们需要告诉MSVC)。因此,它可能会使编译任务复杂化。有没有办法用python预处理文件,例如没有头痛?但这不是问题,我仍然对使用编译时功能感兴趣(关于缓存我可以使用ID索引)
答案 0 :(得分:15)
这是一个完全在编译时工作的解决方案,但也可以在运行时使用。它是constexpr,模板和宏的混合体。您可能想要更改某些名称或将它们放在单独的文件中,因为它们很短。
请注意,我重用了this answer for the CRC table generation中的代码,并根据this page的代码进行了实施。
我没有在MSVC上测试它,因为我目前还没有在我的Windows VM中安装它,但我相信它应该可行,或者至少可以用来进行微不足道的更改。
以下是代码,您可以直接使用crc32函数,或者更接近您的问题的WSID函数:
#include <cstring>
#include <cstdint>
#include <iostream>
// Generate CRC lookup table
template <unsigned c, int k = 8>
struct f : f<((c & 1) ? 0xedb88320 : 0) ^ (c >> 1), k - 1> {};
template <unsigned c> struct f<c, 0>{enum {value = c};};
#define A(x) B(x) B(x + 128)
#define B(x) C(x) C(x + 64)
#define C(x) D(x) D(x + 32)
#define D(x) E(x) E(x + 16)
#define E(x) F(x) F(x + 8)
#define F(x) G(x) G(x + 4)
#define G(x) H(x) H(x + 2)
#define H(x) I(x) I(x + 1)
#define I(x) f<x>::value ,
constexpr unsigned crc_table[] = { A(0) };
// Constexpr implementation and helpers
constexpr uint32_t crc32_impl(const uint8_t* p, size_t len, uint32_t crc) {
return len ?
crc32_impl(p+1,len-1,(crc>>8)^crc_table[(crc&0xFF)^*p])
: crc;
}
constexpr uint32_t crc32(const uint8_t* data, size_t length) {
return ~crc32_impl(data, length, ~0);
}
constexpr size_t strlen_c(const char* str) {
return *str ? 1+strlen_c(str+1) : 0;
}
constexpr int WSID(const char* str) {
return crc32((uint8_t*)str, strlen_c(str));
}
// Example usage
using namespace std;
int main() {
cout << "The CRC32 is: " << hex << WSID("some-id") << endl;
}
第一部分负责生成常量表,而crc32_impl
是标准的CRC32实现,转换为与C ++ 11 constexpr一起使用的递归样式。
然后crc32
和WSID
只是简单的包装器以方便使用。
答案 1 :(得分:1)
@ tux3的答案很漂亮!但是很难维护,因为你基本上是在预处理器命令中编写自己的CRC32实现。
解决问题的另一种方法是首先回顾并理解需求。如果我理解你的话,关注似乎就是表现。在这种情况下,有第二个时间点可以调用您的函数而不会影响性能:在程序加载时。在这种情况下,您将访问全局变量而不是传递常量。性能方面,初始化后两者应该相同(const从代码中取32位,全局变量从常规内存位置取32位)。
你可以这样做:
static int myWSID = 0;
// don't call this directly
static int WSID(const char* str) {
boost::crc_32_type result;
result.process_bytes(str,sizeof(str));
return result.checksum();
}
// Put this early into your program into the
// initialization code.
...
myWSID = WSID('some-id');
根据您的整体计划,您可能希望使用内联访问器来检索值。
如果可以接受轻微的性能影响,您也可以使用单例模式编写这样的函数。
// don't call this directly
int WSID(const char* str) {
boost::crc_32_type result;
result.process_bytes(str,sizeof(str));
return result.checksum();
}
// call this instead. Note the hard-coded ID string.
// Create one such function for each ID you need to
// have available.
static int myWSID() {
// Note: not thread safe!
static int computedId = 0;
if (computedId == 0)
computedId = WSID('some-id');
return computedId;
}
当然,如果要求编译时评估的原因不同(例如,不希望某些id出现在编译代码中),这些技术将无济于事。
另一个选择是使用Jason Gregory建议的自定义预处理器。如果将所有IDS收集到一个单独的文件中,它可以相当干净地完成。此文件不需要具有C语法。我给它一个扩展名,例如.wsid。自定义预处理器从中生成.H文件。
以下是它的外观:
idcollection.wsid(在自定义预处理器之前):
some_id1
some_id2
some_id3
您的预处理器将生成以下idcollection.h:
#define WSID_some_id1 0xabcdef12
#define WSID_some_id2 0xbcdef123
#define WSID_some_id3 0xcdef1234
在您的代码中,您将调用
Engine.getById(WSID_some_id1);
关于此的几点说明: