1
0
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:
Delyan Angelov 2022-12-19 18:00:04 +02:00
parent 2090e4a12f
commit b1006b22e6
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
4 changed files with 96 additions and 6 deletions

View File

@ -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 {

View File

@ -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))

View File

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

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