Skip to content

Commit f33c128

Browse files
committed
add typesalt
1 parent e6e67c0 commit f33c128

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

src/StructHelpers.jl

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import ConstructionBase: getproperties, constructorof, setproperties
88
getproperties(o1) == getproperties(o2)
99
end
1010

11-
@inline function structural_hash(o, h::UInt)::UInt
12-
h = Base.hash(typeof(o), h)
11+
start_hash(o, h, typesalt::Nothing) = Base.hash(typeof(o), h)
12+
start_hash(o, h, typesalt) = Base.hash(typesalt, h)
13+
@inline function structural_hash(o, h::UInt, typesalt=nothing)::UInt
14+
h = start_hash(o, h, typesalt)
1315
nt = Tuple(getproperties(o))
1416
Base.hash(nt, h)
1517
end
@@ -40,6 +42,7 @@ const BATTERIES_DEFAULTS = (
4042
kwshow = false,
4143
getproperties = true ,
4244
constructorof = true ,
45+
typesalt = nothing,
4346
)
4447

4548
const BATTERIES_DOCSTRINGS = (
@@ -49,6 +52,7 @@ const BATTERIES_DOCSTRINGS = (
4952
kwshow = "Overload `Base.show` such that the names of each field are printed.",
5053
getproperties = "Overload `ConstructionBase.getproperties`.",
5154
constructorof = "Overload `ConstructionBase.constructorof`.",
55+
typesalt = "Only used if `hash=true`. In this case the `hash` will be purely computed from `typesalt` and `getproperties(T)`. The type `T` will not be used otherwise. This makes the hash more likely to stay constant, when executing on a different machine or julia version",
5256
)
5357

5458
if (keys(BATTERIES_DEFAULTS) != keys(BATTERIES_DOCSTRINGS))
@@ -104,11 +108,20 @@ macro batteries(T, kw...)
104108
Got: $nt
105109
""")
106110
end
107-
if !(val isa Bool)
111+
if val isa Bool
112+
113+
elseif pname == :typesalt
114+
if !(val isa Union{Nothing,Integer})
115+
error("""`typesalt` must be literally `nothing` or an unsigned integer. Got:
116+
typesalt = $(typesalt)::$(typeof(typesalt))
117+
""")
118+
end
119+
else
108120
error("""
109-
All options must be literally `true` or `false`.
121+
Bad keyword argument value:
110122
Got: $nt
111123
Offending Keyword: $pname
124+
Offending value : $val
112125
""")
113126
end
114127
end
@@ -124,7 +137,7 @@ macro batteries(T, kw...)
124137
fieldnames = Base.fieldnames(Base.eval(__module__, T))
125138
end
126139
if nt.hash
127-
def = :(Base.hash(o::$T, h::UInt) = $(structural_hash)(o,h))
140+
def = :(Base.hash(o::$T, h::UInt) = $(structural_hash)(o,h, $(nt.typesalt)))
128141
push!(ret.args, def)
129142
end
130143
if nt.eq

test/runtests.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ struct Empty2 end
2727
@batteries Empty1
2828
@batteries Empty2
2929

30+
struct Salt1 end
31+
struct Salt1b end
32+
struct Salt2 end
33+
@batteries Salt1 typesalt = 1
34+
@batteries Salt1b typesalt = 1
35+
@batteries Salt2 typesalt = 2
36+
struct NoSalt end
37+
@batteries NoSalt
38+
3039
struct SErrors;a;b;c;end
3140

3241
@testset "@batteries" begin
@@ -67,4 +76,8 @@ struct SErrors;a;b;c;end
6776
@test_throws Exception @macroexpand @batteries SErrors kwconstructor="true"
6877
@test_throws Exception @macroexpand @batteries SErrors nonsense=true
6978
@macroexpand @batteries SErrors kwconstructor=true
79+
80+
@test hash(Salt1()) === hash(Salt1b())
81+
@test hash(Salt1()) != hash(NoSalt())
82+
@test hash(Salt1()) != hash(Salt2())
7083
end

0 commit comments

Comments
 (0)