Support derive(Hash)
diff --git a/gen/src/write.rs b/gen/src/write.rs
index ab9d3c0..873904d 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -134,6 +134,8 @@
_ => {}
}
}
+
+ write_std_specializations(out, apis);
}
for api in apis {
@@ -148,6 +150,36 @@
}
}
+fn write_std_specializations(out: &mut OutFile, apis: &[Api]) {
+ out.set_namespace(Default::default());
+ out.begin_block(Block::Namespace("std"));
+
+ for api in apis {
+ if let Api::Struct(strct) = api {
+ if derive::contains(&strct.derives, Trait::Hash) {
+ out.next_section();
+ let qualified = strct.name.to_fully_qualified();
+ writeln!(out, "template <> struct hash<{}> {{", qualified);
+ writeln!(
+ out,
+ " size_t operator()(const {} &self) const noexcept {{",
+ qualified,
+ );
+ let link_name = mangle::operator(&strct.name, "hash");
+ write!(out, " return ::");
+ for name in &strct.name.namespace {
+ write!(out, "{}::", name);
+ }
+ writeln!(out, "{}(self);", link_name);
+ writeln!(out, " }}");
+ writeln!(out, "}};");
+ }
+ }
+ }
+
+ out.end_block(Block::Namespace("std"));
+}
+
fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
for api in apis {
if let Api::Include(include) = api {
@@ -432,6 +464,15 @@
}
}
+ if derive::contains(&strct.derives, Trait::Hash) {
+ let link_name = mangle::operator(&strct.name, "hash");
+ writeln!(
+ out,
+ "size_t {}(const {} &) noexcept;",
+ link_name, strct.name.cxx,
+ );
+ }
+
out.end_block(Block::ExternC);
}
diff --git a/macro/src/derive.rs b/macro/src/derive.rs
index be8ad82..f614add 100644
--- a/macro/src/derive.rs
+++ b/macro/src/derive.rs
@@ -16,6 +16,7 @@
Trait::Debug => expanded.extend(struct_debug(strct, span)),
Trait::Default => expanded.extend(struct_default(strct, span)),
Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
+ Trait::Hash => expanded.extend(struct_hash(strct, span)),
Trait::Ord => expanded.extend(struct_ord(strct, span)),
Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
@@ -56,6 +57,7 @@
traits.push(quote_spanned!(span=> ::std::cmp::Eq));
has_eq = true;
}
+ Trait::Hash => expanded.extend(enum_hash(enm, span)),
Trait::Ord => expanded.extend(enum_ord(enm, span)),
Trait::PartialEq => {
traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
@@ -155,6 +157,21 @@
}
}
+fn struct_hash(strct: &Struct, span: Span) -> TokenStream {
+ let ident = &strct.name.rust;
+ let fields = strct.fields.iter().map(|field| &field.ident);
+
+ quote_spanned! {span=>
+ impl ::std::hash::Hash for #ident {
+ fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
+ #(
+ ::std::hash::Hash::hash(&self.#fields, state);
+ )*
+ }
+ }
+ }
+}
+
fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
let ident = &strct.name.rust;
let fields = strct.fields.iter().map(|field| &field.ident);
@@ -246,6 +263,18 @@
}
}
+fn enum_hash(enm: &Enum, span: Span) -> TokenStream {
+ let ident = &enm.name.rust;
+
+ quote_spanned! {span=>
+ impl ::std::hash::Hash for #ident {
+ fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
+ ::std::hash::Hash::hash(&self.repr, state);
+ }
+ }
+ }
+}
+
fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
let ident = &enm.name.rust;
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index e065a55..c218267 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -230,6 +230,19 @@
});
}
}
+ Trait::Hash => {
+ let link_name = mangle::operator(&strct.name, "hash");
+ let local_name = format_ident!("__operator_hash_{}", strct.name.rust);
+ operators.extend(quote_spanned! {span=>
+ #[doc(hidden)]
+ #[export_name = #link_name]
+ extern "C" fn #local_name(this: &#ident) -> usize {
+ let mut hasher = ::std::collections::hash_map::DefaultHasher::new();
+ ::std::hash::Hash::hash(this, &mut hasher);
+ ::std::hash::Hasher::finish(&hasher) as usize
+ }
+ });
+ }
_ => {}
}
}
diff --git a/syntax/derive.rs b/syntax/derive.rs
index a8b6bde..1121211 100644
--- a/syntax/derive.rs
+++ b/syntax/derive.rs
@@ -13,6 +13,7 @@
Debug,
Default,
Eq,
+ Hash,
Ord,
PartialEq,
PartialOrd,
@@ -26,6 +27,7 @@
"Debug" => Trait::Debug,
"Default" => Trait::Default,
"Eq" => Trait::Eq,
+ "Hash" => Trait::Hash,
"Ord" => Trait::Ord,
"PartialEq" => Trait::PartialEq,
"PartialOrd" => Trait::PartialOrd,
@@ -50,6 +52,7 @@
Trait::Debug => "Debug",
Trait::Default => "Default",
Trait::Eq => "Eq",
+ Trait::Hash => "Hash",
Trait::Ord => "Ord",
Trait::PartialEq => "PartialEq",
Trait::PartialOrd => "PartialOrd",
diff --git a/syntax/mangle.rs b/syntax/mangle.rs
index 26cd3c0..0d395e3 100644
--- a/syntax/mangle.rs
+++ b/syntax/mangle.rs
@@ -26,7 +26,13 @@
}
pub fn operator(receiver: &Pair, operator: &'static str) -> Symbol {
- join!(receiver.namespace, CXXBRIDGE, receiver.cxx, "operator", operator)
+ join!(
+ receiver.namespace,
+ CXXBRIDGE,
+ receiver.cxx,
+ "operator",
+ operator,
+ )
}
// The C half of a function pointer trampoline.
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index cb0f916..5427b89 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -25,7 +25,7 @@
msg: String,
}
- #[derive(Debug)]
+ #[derive(Debug, Hash)]
enum Enum {
AVal,
BVal = 2020,
@@ -64,6 +64,7 @@
}
#[namespace = "second"]
+ #[derive(Hash)]
struct Second {
i: i32,
e: COwnedEnum,
@@ -184,6 +185,7 @@
}
#[repr(u32)]
+ #[derive(Hash)]
enum COwnedEnum {
CVal1,
CVal2,