C++ 列表哈希和N3980

C++ 列表哈希和N3980,c++,algorithm,hash-function,C++,Algorithm,Hash Function,我很难适应@HowardHinnant提出的待定C++1z提案 从头计算表格散列的工作原理与N3980中描述的散列算法(幽灵、杂音等)相同。这并没有那么复杂:只需通过hash_append()序列化任何用户定义类型的对象,并让hash函数在执行过程中将指针更新为随机数表 当尝试实现列表散列的一个好特性时,问题就开始了:如果对象发生变化,则计算对散列的增量更新非常便宜。对于“手工”制表哈希,只需重新计算对象受影响字节的哈希 我的问题是:如何在忠实于N3980的中心主题(类型不知道35;)的同时,向

我很难适应@HowardHinnant提出的待定C++1z提案

从头计算表格散列的工作原理与N3980中描述的散列算法(幽灵、杂音等)相同。这并没有那么复杂:只需通过hash_append()序列化任何用户定义类型的对象,并让hash函数在执行过程中将指针更新为随机数表

当尝试实现列表散列的一个好特性时,问题就开始了:如果对象发生变化,则计算对散列的增量更新非常便宜。对于“手工”制表哈希,只需重新计算对象受影响字节的哈希

我的问题是:如何在忠实于N3980的中心主题(类型不知道35;)的同时,向
uhash
函数对象传达增量更新

说明设计难点:假设我有一个用户定义的X类型,其中有各种类型的数据成员席

struct X 
{
   T1 x1;
   ...
   TN xN;
};
现在创建一个对象并计算其散列

X x { ... }; // initialize
std::size_t h = uhash<MyTabulationAlgorithm>(x);
其中,
[开始,停止)
表示与x2数据成员相对应的随机数表的范围。但是,为了以增量方式(即便宜地!)更新任意突变的哈希,每个数据成员都需要知道其包含类的序列化字节流中自己的子范围。这感觉不像N3980的精神。例如,向包含类添加新数据成员,将改变类布局,从而改变序列化字节流中的偏移量


应用程序:表格哈希法非常古老,最近已经证明它具有非常好的数学特性(请参见Wikipedia链接)。它在棋盘游戏编程社区中也非常流行(例如计算机象棋和围棋)其中,董事会职位扮演X的角色,而移动则扮演小更新的角色(例如,将工件从源位置移动到目标位置)。如果N3980不仅能够适应这种表格散列,而且还能适应廉价的增量更新,那就太好了。

您似乎可以通过告诉
MyTableationalGorithm
忽略所有类成员的值来做到这一点,但已更改的除外:

x.x2 = 42;
IncrementalHashAdaptor<MyTabulationAlgorithm, T2> inc{x.x2};
hash_append(inc, x);
h ^= inc;
显然,这只适用于对象位置既可以从外部确定,又对应于传递给哈希算法的内存块的成员;但这应该对应于绝大多数情况

您可能希望将
incrementalhashadapter
和下面的
hash\u append
包装到
uhash\u incremental
实用程序中;这是留给读者的练习


性能上有一个问号;假设
HashAlgorithm::ignore(…)
对编译器是可见的,并且不复杂,它应该能够很好地优化;如果没有出现这种情况,您应该能够在程序启动时使用类似的策略来计算
X::x2
的字节流地址。

类布局中的填充会打乱地址计算,或者我遗漏了什么吗?@TemplateRex如果有然后,填充
X
hash\u append
X.x2
作为单个内存块传递给哈希算法,并且
key
将等于
p
。请注意,
ignore
仅对参与
X
值表示的内存块进行调用。这一点不错。另一点是否定的不太清楚的是,使用表格散列,您需要对旧的
x2
值进行异或运算,然后对新值进行异或运算。因此,递增哈希适配器需要在
x
的对象状态下接收完整的“增量”(即
x.x2
包括其初始值和新值42)例如,在计算机象棋中,您有一个状态/位置类和一个封装更改的增量/移动类。然后,
uhash
State
上工作,增量hashAdapter在
Delta
上工作(也可能在
State
上工作)。
h ^= hash_update(x.x2, start, stop); 
x.x2 = 42;
IncrementalHashAdaptor<MyTabulationAlgorithm, T2> inc{x.x2};
hash_append(inc, x);
h ^= inc;
template<class HashAlgorithm, class T>
struct IncrementalHashAdaptor
{
    T& t;
    HashAlgorithm h = {};
    bool found = false;
    void operator()(void const* key, std::size_t len) noexcept
    {
        if (/* t contained within [key, key + len) */) {
            assert(!found);
            found = true;
            char const* p = addressof(t);
            h.ignore(key, (p - key));
            h(p, sizeof(T));
            h.ignore(p + sizeof(T), len - (p - key) - sizeof(T));
        }
        else {
            h.ignore(key, len);
        }
    }
    operator std:size_t() const { assert(found); return h; }
};