diff --git a/vlib/compiler/cgen.v b/vlib/compiler/cgen.v index f238a2f7e8..c17d544b4d 100644 --- a/vlib/compiler/cgen.v +++ b/vlib/compiler/cgen.v @@ -470,23 +470,38 @@ fn (v &V) interface_table() string { if t.cat != .interface_ { continue } + // interface_name is for example Speaker interface_name := t.name mut methods := '' + mut generated_casting_functions := '' sb.writeln('// NR methods = $t.gen_types.len') for i, gen_type in t.gen_types { - gen_concrete_type_name := gen_type.replace('*', '') + // ptr_ctype can be for example Cat OR Cat_ptr: + ptr_ctype := gen_type.replace('*', '_ptr') + // cctype is the Cleaned Concrete Type name, *without ptr*, + // i.e. cctype is always just Cat, not Cat_ptr: + cctype := gen_type.replace('*', '') + + // Speaker_Cat_index = 0 + interface_index_name := '_${interface_name}_${ptr_ctype}_index' + + generated_casting_functions += ' +${interface_name} I_${cctype}_to_${interface_name}(${cctype} x) { + return (${interface_name}){ + ._object = (void*) memdup(&x, sizeof(${cctype})), + ._interface_idx = ${interface_index_name} }; +} +' methods += '{\n' for j, method in t.methods { // Cat_speak - methods += ' (void*) ${gen_concrete_type_name}_${method.name}' + methods += ' (void*) ${cctype}_${method.name}' if j < t.methods.len - 1 { methods += ', \n' } } methods += '\n},\n\n' - // Speaker_Cat_index = 0 - concrete_type_name := gen_type.replace('*', '_ptr') - sb.writeln('int _${interface_name}_${concrete_type_name}_index = $i;') + sb.writeln('int ${interface_index_name} = $i;') } if t.gen_types.len > 0 { // methods = '{TCCSKIP(0)}' @@ -499,7 +514,10 @@ fn (v &V) interface_table() string { // See https://github.com/zenith391/vgtk3/issues/7 sb.writeln('void* (* ${interface_name}_name_table[][1]) = ' + '{ {NULL} }; ') } - continue + if generated_casting_functions.len > 0 { + sb.writeln('// Casting functions for interface "${interface_name}" :') + sb.writeln( generated_casting_functions ) + } } return sb.str() } diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 215131b022..41bfe9a9bf 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -1115,12 +1115,18 @@ fn (p mut Parser) fn_call_args(f mut Fn, generic_param_types []string) { if arg.typ.ends_with('er') || arg.typ[0] == `I` { t := p.table.find_type(arg.typ) if t.cat == .interface_ { - // perform((Speaker) { ._object = &dog, - // _interface_idx = _Speaker_Dog_index }) - if !f.is_method { - concrete_type_name := typ.replace('*', '_ptr') - p.cgen.set_placeholder(ph, '($arg.typ) { ._object = &') - p.gen(', ._interface_idx = _${arg.typ}_${concrete_type_name}_index} ') + // NB: here concrete_type_name can be 'Dog' OR 'Dog_ptr' + // cgen should have generated a _I_Dog_to_Speaker conversion function + // C: perform( _I_Dog_to_Speaker((Dog){...}) ) + // In case of _ptr, there is no need for conversion, so the generated + // code will be just: + // C: perform( dog_ptr ) + concrete_type_name := typ.replace('*', '_ptr') + // concrete_type_name here can be say Dog, or ui__Group_ptr (in vui) + //eprintln('arg.typ: $arg.typ | concrete_type_name: $concrete_type_name ') + if !concrete_type_name.ends_with('_ptr') { + p.cgen.set_placeholder(ph, 'I_${concrete_type_name}_to_${arg.typ}(') + p.gen(')') } p.table.add_gen_type(arg.typ, typ) } diff --git a/vlib/compiler/tests/interfaces_map_test.v b/vlib/compiler/tests/interfaces_map_test.v new file mode 100644 index 0000000000..3f92af5241 --- /dev/null +++ b/vlib/compiler/tests/interfaces_map_test.v @@ -0,0 +1,49 @@ +module main + +interface Speaker { + say() string +} + +struct ChatRoom { +mut: + talkers map[string]Speaker +} + +fn new_room() &ChatRoom { + return &ChatRoom{ + talkers: map[string]Speaker + } +} + +fn (r mut ChatRoom) add(name string, s Speaker) { + r.talkers[name] = s +} + +fn test_using_a_map_of_speaker_interfaces(){ + mut room := new_room() + room.add('my cat', Cat{name: 'Tigga'} ) + room.add('my dog', Dog{name: 'Pirin'} ) + room.add('stray dog', Dog{name: 'Anoni'} ) + room.add('me', Human{name: 'Bilbo'} ) + room.add('she', Human{name: 'Maria'} ) + mut text := '' + for name, subject in room.talkers { + line := '${name:12s}: ${subject.say()}' + println(line) + text += line + } + assert text.contains(' meows ') + assert text.contains(' barks ') + assert text.contains(' says ') +} + +// + +struct Cat { name string } +fn (c &Cat) say() string { return '${c.name} meows "MEOW!"' } + +struct Dog { name string } +fn (d &Dog) say() string { return '${d.name} barks "Bau Bau!"' } + +struct Human { name string } +fn (h &Human) say() string { return '${h.name} says "Hello"' }