mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: support a Struct as map key (minimal draft)
This commit is contained in:
parent
2090e4a12f
commit
b1006b22e6
@ -149,13 +149,13 @@ fn (mut d DenseArray) expand() int {
|
||||
return push_index
|
||||
}
|
||||
|
||||
type MapHashFn = fn (voidptr) u64
|
||||
type MapHashFn = fn (new_key voidptr) u64
|
||||
|
||||
type MapEqFn = fn (voidptr, voidptr) bool
|
||||
type MapEqFn = fn (new_key voidptr, existing_map_key voidptr) bool
|
||||
|
||||
type MapCloneFn = fn (voidptr, voidptr)
|
||||
type MapCloneFn = fn (existing_map_key voidptr, new_key voidptr)
|
||||
|
||||
type MapFreeFn = fn (voidptr)
|
||||
type MapFreeFn = fn (existing_map_key voidptr)
|
||||
|
||||
// map is the internal representation of a V `map` type.
|
||||
pub struct map {
|
||||
|
@ -3073,12 +3073,61 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
|
||||
g.autofree_scope_stmts << af.str()
|
||||
}
|
||||
|
||||
fn (mut g Gen) map_check_presence(struct_name string, c_fn_name string, v_method_name string) {
|
||||
if c_fn_name == '' {
|
||||
verror('struct `${struct_name}`, used as map key, should implement a `${v_method_name}` method with a reference receiver')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) map_fn_ptrs(key_typ ast.TypeSymbol) (string, string, string, string) {
|
||||
mut hash_fn := ''
|
||||
mut key_eq_fn := ''
|
||||
mut clone_fn := ''
|
||||
mut free_fn := '&map_free_nop'
|
||||
match key_typ.kind {
|
||||
.struct_ {
|
||||
for method in key_typ.methods {
|
||||
if method.receiver_type.nr_muls() == 0 {
|
||||
continue
|
||||
}
|
||||
match method.name {
|
||||
'map_hash' {
|
||||
if method.return_type != ast.u64_type || method.params.len != 1 {
|
||||
eprintln('the map_hash method of struct ${key_typ.name} should return `u64`, and should have no parameters')
|
||||
continue
|
||||
}
|
||||
hash_fn = '&${key_typ.cname}_map_hash'
|
||||
}
|
||||
'map_eq' {
|
||||
if method.return_type != ast.bool_type || method.params.len != 2 {
|
||||
eprintln('the map_eq method of struct ${key_typ.name} should return `bool`, and should have exactly 1 parameter of type `&{${key_typ.name}}`')
|
||||
continue
|
||||
}
|
||||
key_eq_fn = '&${key_typ.cname}_map_eq'
|
||||
}
|
||||
'map_clone' {
|
||||
if method.return_type != ast.void_type || method.params.len != 2 {
|
||||
eprintln('the map_clone method of struct ${key_typ.name} should have exactly 1 parameter of type `&{${key_typ.name}}` and its receiver should be `mut x ${key_typ.name}`, and should not return anything')
|
||||
continue
|
||||
}
|
||||
clone_fn = '&${key_typ.cname}_map_clone'
|
||||
}
|
||||
'map_free' {
|
||||
if method.return_type != ast.void_type || method.params.len != 1 {
|
||||
eprintln('the map_free method of struct ${key_typ.name} should have no parameters, and should not return anything')
|
||||
continue
|
||||
}
|
||||
free_fn = '&${key_typ.cname}_map_free'
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
g.map_check_presence(key_typ.name, hash_fn, 'map_hash')
|
||||
g.map_check_presence(key_typ.name, key_eq_fn, 'map_eq')
|
||||
g.map_check_presence(key_typ.name, clone_fn, 'map_clone')
|
||||
g.map_check_presence(key_typ.name, free_fn, 'map_free')
|
||||
return hash_fn, key_eq_fn, clone_fn, free_fn
|
||||
}
|
||||
.alias {
|
||||
alias_key_type := (key_typ.info as ast.Alias).parent_type
|
||||
return g.map_fn_ptrs(g.table.sym(alias_key_type))
|
||||
|
@ -117,7 +117,7 @@ pub fn (mut p Parser) parse_map_type() ast.Type {
|
||||
key_sym := p.table.sym(key_type)
|
||||
is_alias := key_sym.kind == .alias
|
||||
key_type_supported := key_type in [ast.string_type_idx, ast.voidptr_type_idx]
|
||||
|| key_sym.kind in [.enum_, .placeholder, .any]
|
||||
|| key_sym.kind in [.enum_, .placeholder, .any, .struct_]
|
||||
|| ((key_type.is_int() || key_type.is_float() || is_alias) && !key_type.is_ptr())
|
||||
if !key_type_supported {
|
||||
if is_alias {
|
||||
@ -125,7 +125,7 @@ pub fn (mut p Parser) parse_map_type() ast.Type {
|
||||
return 0
|
||||
}
|
||||
s := p.table.type_to_str(key_type)
|
||||
p.error_with_pos('maps only support string, integer, float, rune, enum or voidptr keys for now (not `${s}`)',
|
||||
p.error_with_pos('maps only support string, integer, float, rune, enum, struct or voidptr keys for now (not `${s}`)',
|
||||
p.tok.pos())
|
||||
return 0
|
||||
}
|
||||
|
41
vlib/v/tests/map_struct_as_key_test.v
Normal file
41
vlib/v/tests/map_struct_as_key_test.v
Normal file
@ -0,0 +1,41 @@
|
||||
struct Abc {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
fn (s &Abc) map_hash() u64 {
|
||||
// eprintln('${@METHOD} called, s: $s')
|
||||
return u64(s.x) << 32 + u64(s.y)
|
||||
}
|
||||
|
||||
fn (s &Abc) map_eq(b &Abc) bool {
|
||||
// eprintln('${@METHOD} called, s: $s')
|
||||
return s.x == b.x && s.y == b.y
|
||||
}
|
||||
|
||||
fn (mut s Abc) map_clone(new &Abc) {
|
||||
// eprintln('${@METHOD} called, s: $s | new: $new')
|
||||
unsafe {
|
||||
*s = *new
|
||||
}
|
||||
}
|
||||
|
||||
fn (s &Abc) map_free() {
|
||||
eprintln('${@METHOD} called, s: ${s}')
|
||||
}
|
||||
|
||||
fn test_map_with_struct_as_key() {
|
||||
s0 := Abc{}
|
||||
s1 := Abc{1, 2}
|
||||
mut m := map[Abc]int{}
|
||||
m[s0] = 123
|
||||
m[s1] = 456
|
||||
assert m[s0] == 123
|
||||
assert m[s1] == 456
|
||||
dump(m)
|
||||
m[s1] = 789
|
||||
dump(m[s1])
|
||||
dump(m)
|
||||
assert m[s0] == 123
|
||||
assert m[s1] == 789
|
||||
}
|
Loading…
Reference in New Issue
Block a user