Skip to content

Commit 92ed57a

Browse files
committed
feat(cpp): add map type support
1 parent a4b3eb1 commit 92ed57a

File tree

3 files changed

+182
-10
lines changed

3 files changed

+182
-10
lines changed

crates/cpp/src/lib.rs

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct Includes {
7575
needs_wit: bool,
7676
needs_memory: bool,
7777
needs_array: bool,
78+
needs_map: bool,
7879
}
7980

8081
#[derive(Default)]
@@ -435,6 +436,9 @@ impl Cpp {
435436
if self.dependencies.needs_bit {
436437
self.include("<bit>");
437438
}
439+
if self.dependencies.needs_map {
440+
self.include("<map>");
441+
}
438442
}
439443

440444
fn start_new_file(&mut self, condition: Option<bool>) -> FileContext {
@@ -914,7 +918,7 @@ impl CppInterfaceGenerator<'_> {
914918
TypeDefKind::Stream(_) => todo!("generate for stream"),
915919
TypeDefKind::Handle(_) => todo!("generate for handle"),
916920
TypeDefKind::FixedLengthList(_, _) => todo!(),
917-
TypeDefKind::Map(_, _) => todo!(),
921+
TypeDefKind::Map(k, v) => self.type_map(id, name, k, v, &ty.docs),
918922
TypeDefKind::Unknown => unreachable!(),
919923
}
920924
}
@@ -1741,7 +1745,12 @@ impl CppInterfaceGenerator<'_> {
17411745
self.type_name(ty, from_namespace, flavor)
17421746
)
17431747
}
1744-
TypeDefKind::Map(_, _) => todo!(),
1748+
TypeDefKind::Map(key, value) => {
1749+
self.r#gen.dependencies.needs_map = true;
1750+
let k = self.type_name(key, from_namespace, flavor);
1751+
let v = self.type_name(value, from_namespace, flavor);
1752+
format!("std::map<{k}, {v}>")
1753+
}
17451754
TypeDefKind::Unknown => todo!(),
17461755
},
17471756
Type::ErrorContext => todo!(),
@@ -2266,7 +2275,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a>
22662275
_value: &wit_bindgen_core::wit_parser::Type,
22672276
_docs: &wit_bindgen_core::wit_parser::Docs,
22682277
) {
2269-
todo!("map types are not yet supported in the C++ backend")
2278+
// nothing to do here
22702279
}
22712280

22722281
fn type_builtin(
@@ -3540,12 +3549,107 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
35403549
}
35413550
abi::Instruction::AsyncTaskReturn { .. } => todo!(),
35423551
abi::Instruction::DropHandle { .. } => todo!(),
3543-
abi::Instruction::MapLower { .. }
3544-
| abi::Instruction::MapLift { .. }
3545-
| abi::Instruction::IterMapKey { .. }
3546-
| abi::Instruction::IterMapValue { .. }
3547-
| abi::Instruction::GuestDeallocateMap { .. } => {
3548-
todo!("map types are not yet supported in this backend")
3552+
abi::Instruction::MapLower {
3553+
key,
3554+
value,
3555+
realloc,
3556+
} => {
3557+
let tmp = self.tmp();
3558+
let body = self.blocks.pop().unwrap();
3559+
let val = format!("map{tmp}");
3560+
let ptr = format!("ptr{tmp}");
3561+
let len = format!("len{tmp}");
3562+
let idx = format!("idx{tmp}");
3563+
let entry = self.r#gen.sizes.record([*key, *value]);
3564+
let size = entry.size.format(POINTER_SIZE_EXPRESSION);
3565+
let align = entry.align.format(POINTER_SIZE_EXPRESSION);
3566+
self.push_str(&format!("auto&& {val} = {};\n", operands[0]));
3567+
self.push_str(&format!("auto {len} = (size_t)({val}.size());\n"));
3568+
uwriteln!(
3569+
self.src,
3570+
"auto {ptr} = ({ptr_type})({len} > 0 ? cabi_realloc(nullptr, 0, {align}, {len} * {size}) : nullptr);",
3571+
ptr_type = self.r#gen.r#gen.opts.ptr_type()
3572+
);
3573+
uwriteln!(self.src, "size_t {idx} = 0;");
3574+
uwriteln!(
3575+
self.src,
3576+
"for (auto&& [iter_map_key, iter_map_value] : {val}) {{"
3577+
);
3578+
uwriteln!(self.src, "auto base = {ptr} + {idx} * {size};");
3579+
uwrite!(self.src, "{}", body.0);
3580+
uwriteln!(self.src, "++{idx};");
3581+
uwriteln!(self.src, "}}");
3582+
if realloc.is_some() {
3583+
// ownership transfers
3584+
}
3585+
results.push(ptr);
3586+
results.push(len);
3587+
}
3588+
abi::Instruction::MapLift { key, value, .. } => {
3589+
let body = self.blocks.pop().unwrap();
3590+
let tmp = self.tmp();
3591+
let entry = self.r#gen.sizes.record([*key, *value]);
3592+
let size = entry.size.format(POINTER_SIZE_EXPRESSION);
3593+
let key_type =
3594+
self.r#gen
3595+
.type_name(key, &self.namespace, Flavor::InStruct);
3596+
let value_type =
3597+
self.r#gen
3598+
.type_name(value, &self.namespace, Flavor::InStruct);
3599+
let len = format!("len{tmp}");
3600+
let base = format!("base{tmp}");
3601+
let result = format!("result{tmp}");
3602+
uwriteln!(self.src, "auto {base} = {};", operands[0]);
3603+
uwriteln!(self.src, "auto {len} = {};", operands[1]);
3604+
uwriteln!(
3605+
self.src,
3606+
"std::map<{key_type}, {value_type}> {result};"
3607+
);
3608+
if self.r#gen.r#gen.opts.api_style == APIStyle::Symmetric
3609+
&& matches!(self.variant, AbiVariant::GuestExport)
3610+
{
3611+
assert!(self.needs_dealloc);
3612+
uwriteln!(self.src, "if ({len}>0) _deallocate.push_back({base});");
3613+
}
3614+
uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{");
3615+
uwriteln!(self.src, "auto base = {base} + i * {size};");
3616+
uwrite!(self.src, "{}", body.0);
3617+
let body_key = &body.1[0];
3618+
let body_value = &body.1[1];
3619+
uwriteln!(
3620+
self.src,
3621+
"{result}.insert(std::make_pair({}, {}));",
3622+
move_if_necessary(body_key),
3623+
move_if_necessary(body_value)
3624+
);
3625+
uwriteln!(self.src, "}}");
3626+
results.push(move_if_necessary(&result));
3627+
}
3628+
abi::Instruction::IterMapKey { .. } => {
3629+
results.push("iter_map_key".to_string());
3630+
}
3631+
abi::Instruction::IterMapValue { .. } => {
3632+
results.push("iter_map_value".to_string());
3633+
}
3634+
abi::Instruction::GuestDeallocateMap { key, value } => {
3635+
let (body, results) = self.blocks.pop().unwrap();
3636+
assert!(results.is_empty());
3637+
let tmp = self.tmp();
3638+
let ptr = self.tempname("ptr", tmp);
3639+
let len = self.tempname("len", tmp);
3640+
uwriteln!(self.src, "uint8_t* {ptr} = {};", operands[0]);
3641+
uwriteln!(self.src, "size_t {len} = {};", operands[1]);
3642+
let i = self.tempname("i", tmp);
3643+
uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{");
3644+
let entry = self.r#gen.sizes.record([*key, *value]);
3645+
let size = entry.size.format(POINTER_SIZE_EXPRESSION);
3646+
uwriteln!(self.src, "uint8_t* base = {ptr} + {i} * {size};");
3647+
uwriteln!(self.src, "(void) base;");
3648+
uwrite!(self.src, "{body}");
3649+
uwriteln!(self.src, "}}");
3650+
uwriteln!(self.src, "if ({len} > 0) {{");
3651+
uwriteln!(self.src, "free((void*) ({ptr}));");
3652+
uwriteln!(self.src, "}}");
35493653
}
35503654
}
35513655
}

crates/test/src/cpp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl LanguageMethods for Cpp {
4646
_args: &[String],
4747
) -> bool {
4848
return match name {
49-
"issue1514-6.wit" | "named-fixed-length-list.wit" | "map.wit" => true,
49+
"issue1514-6.wit" | "named-fixed-length-list.wit" => true,
5050
_ => false,
5151
} || config.async_;
5252
}

tests/runtime/map/test.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <assert.h>
2+
#include <test_cpp.h>
3+
4+
namespace test_exports = ::exports::test::maps::to_test;
5+
6+
std::map<wit::string, uint32_t> test_exports::NamedRoundtrip(std::map<uint32_t, wit::string> a) {
7+
std::map<wit::string, uint32_t> result;
8+
for (auto&& [id, name] : a) {
9+
result.insert(std::make_pair(std::move(name), id));
10+
}
11+
return result;
12+
}
13+
14+
std::map<wit::string, wit::vector<uint8_t>> test_exports::BytesRoundtrip(std::map<wit::string, wit::vector<uint8_t>> a) {
15+
return a;
16+
}
17+
18+
std::map<uint32_t, wit::string> test_exports::EmptyRoundtrip(std::map<uint32_t, wit::string> a) {
19+
return a;
20+
}
21+
22+
std::map<wit::string, std::optional<uint32_t>> test_exports::OptionRoundtrip(std::map<wit::string, std::optional<uint32_t>> a) {
23+
return a;
24+
}
25+
26+
test_exports::LabeledEntry test_exports::RecordRoundtrip(test_exports::LabeledEntry a) {
27+
return a;
28+
}
29+
30+
std::map<wit::string, uint32_t> test_exports::InlineRoundtrip(std::map<uint32_t, wit::string> a) {
31+
std::map<wit::string, uint32_t> result;
32+
for (auto&& [k, v] : a) {
33+
result.insert(std::make_pair(std::move(v), k));
34+
}
35+
return result;
36+
}
37+
38+
std::map<uint32_t, wit::string> test_exports::LargeRoundtrip(std::map<uint32_t, wit::string> a) {
39+
return a;
40+
}
41+
42+
std::tuple<std::map<wit::string, uint32_t>, std::map<wit::string, wit::vector<uint8_t>>> test_exports::MultiParamRoundtrip(std::map<uint32_t, wit::string> a, std::map<wit::string, wit::vector<uint8_t>> b) {
43+
std::map<wit::string, uint32_t> ids;
44+
for (auto&& [id, name] : a) {
45+
ids.insert(std::make_pair(std::move(name), id));
46+
}
47+
return std::make_tuple(std::move(ids), std::move(b));
48+
}
49+
50+
std::map<wit::string, std::map<uint32_t, wit::string>> test_exports::NestedRoundtrip(std::map<wit::string, std::map<uint32_t, wit::string>> a) {
51+
return a;
52+
}
53+
54+
test_exports::MapOrString test_exports::VariantRoundtrip(test_exports::MapOrString a) {
55+
return a;
56+
}
57+
58+
std::expected<std::map<uint32_t, wit::string>, wit::string> test_exports::ResultRoundtrip(std::expected<std::map<uint32_t, wit::string>, wit::string> a) {
59+
return a;
60+
}
61+
62+
std::tuple<std::map<uint32_t, wit::string>, uint64_t> test_exports::TupleRoundtrip(std::tuple<std::map<uint32_t, wit::string>, uint64_t> a) {
63+
return a;
64+
}
65+
66+
std::map<uint32_t, wit::string> test_exports::SingleEntryRoundtrip(std::map<uint32_t, wit::string> a) {
67+
return a;
68+
}

0 commit comments

Comments
 (0)