| 1 | //! This crate is the `cpp` procedural macro implementation. It is useless |
| 2 | //! without the companion crates `cpp`, and `cpp_build`. |
| 3 | //! |
| 4 | //! For more information, see the [`cpp` crate module level |
| 5 | //! documentation](https://docs.rs/cpp). |
| 6 | #![recursion_limit = "128" ] |
| 7 | |
| 8 | #[macro_use ] |
| 9 | extern crate syn; |
| 10 | extern crate proc_macro; |
| 11 | use proc_macro2::Span; |
| 12 | |
| 13 | use cpp_common::{flags, kw, RustInvocation, FILE_HASH, LIB_NAME, MSVC_LIB_NAME, OUT_DIR, VERSION}; |
| 14 | use std::collections::HashMap; |
| 15 | use std::iter::FromIterator; |
| 16 | use syn::parse::Parser; |
| 17 | use syn::Ident; |
| 18 | |
| 19 | use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt}; |
| 20 | use lazy_static::lazy_static; |
| 21 | use quote::{quote, quote_spanned}; |
| 22 | use std::fs::File; |
| 23 | use std::io::{self, BufReader, Read, Seek, SeekFrom}; |
| 24 | |
| 25 | struct MetaData { |
| 26 | size: usize, |
| 27 | align: usize, |
| 28 | flags: u64, |
| 29 | } |
| 30 | impl MetaData { |
| 31 | fn has_flag(&self, f: u32) -> bool { |
| 32 | self.flags & (1 << f) != 0 |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | lazy_static! { |
| 37 | static ref METADATA: HashMap<u64, Vec<MetaData>> = { |
| 38 | let file = match open_lib_file() { |
| 39 | Ok(x) => x, |
| 40 | Err(e) => { |
| 41 | #[cfg (not(feature = "docs-only" ))] |
| 42 | panic!( |
| 43 | r#" |
| 44 | -- rust-cpp fatal error -- |
| 45 | |
| 46 | Failed to open the target library file. |
| 47 | NOTE: Did you make sure to add the rust-cpp build script? |
| 48 | {}"# , |
| 49 | e |
| 50 | ); |
| 51 | #[cfg (feature = "docs-only" )] |
| 52 | { |
| 53 | eprintln!("Error while opening target library: {}" , e); |
| 54 | return Default::default(); |
| 55 | }; |
| 56 | } |
| 57 | }; |
| 58 | |
| 59 | read_metadata(file).expect( |
| 60 | r#" |
| 61 | -- rust-cpp fatal error -- |
| 62 | |
| 63 | I/O error while reading metadata from target library file."# , |
| 64 | ) |
| 65 | }; |
| 66 | } |
| 67 | |
| 68 | /// NOTE: This panics when it can produce a better error message |
| 69 | fn read_metadata(file: File) -> io::Result<HashMap<u64, Vec<MetaData>>> { |
| 70 | let mut file = BufReader::new(file); |
| 71 | let end = { |
| 72 | const AUTO_KEYWORD: &[&[u8]] = &[&cpp_common::STRUCT_METADATA_MAGIC]; |
| 73 | let aut = aho_corasick::AhoCorasick::new(AUTO_KEYWORD).unwrap(); |
| 74 | let found = aut.stream_find_iter(&mut file).next().expect( |
| 75 | r#" |
| 76 | -- rust-cpp fatal error -- |
| 77 | |
| 78 | Struct metadata not present in target library file. |
| 79 | NOTE: Double-check that the version of cpp_build and cpp_macros match"# , |
| 80 | )?; |
| 81 | found.end() |
| 82 | }; |
| 83 | file.seek(SeekFrom::Start(end as u64))?; |
| 84 | |
| 85 | // Read & convert the version buffer into a string & compare with our |
| 86 | // version. |
| 87 | let mut version_buf = [0; 16]; |
| 88 | file.read_exact(&mut version_buf)?; |
| 89 | let version = |
| 90 | version_buf.iter().take_while(|b| **b != b' \0' ).map(|b| *b as char).collect::<String>(); |
| 91 | |
| 92 | assert_eq!( |
| 93 | version, VERSION, |
| 94 | r#" |
| 95 | -- rust-cpp fatal error -- |
| 96 | |
| 97 | Version mismatch between cpp_macros and cpp_build for same crate."# |
| 98 | ); |
| 99 | let endianness_check = file.read_u64::<LittleEndian>()?; |
| 100 | if endianness_check == 0xffef { |
| 101 | read_metadata_rest::<LittleEndian>(file) |
| 102 | } else if endianness_check == 0xefff000000000000 { |
| 103 | read_metadata_rest::<BigEndian>(file) |
| 104 | } else { |
| 105 | panic!("Endianness check value matches neither little nor big endian." ); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | fn read_metadata_rest<E: ByteOrder>( |
| 110 | mut file: BufReader<File>, |
| 111 | ) -> io::Result<HashMap<u64, Vec<MetaData>>> { |
| 112 | let length: u64 = file.read_u64::<E>()?; |
| 113 | let mut metadata: HashMap> = HashMap::new(); |
| 114 | for _ in 0..length { |
| 115 | let hash: u64 = file.read_u64::<E>()?; |
| 116 | let size: usize = file.read_u64::<E>()? as usize; |
| 117 | let align: usize = file.read_u64::<E>()? as usize; |
| 118 | let flags: u64 = file.read_u64::<E>()?; |
| 119 | |
| 120 | metadata.entry(hash).or_insert_with(default:Vec::new).push(MetaData { size, align, flags }); |
| 121 | } |
| 122 | Ok(metadata) |
| 123 | } |
| 124 | |
| 125 | /// Try to open a file handle to the lib file. This is used to scan it for |
| 126 | /// metadata. We check both `MSVC_LIB_NAME` and `LIB_NAME`, in case we are on |
| 127 | /// or are targeting Windows. |
| 128 | fn open_lib_file() -> io::Result<File> { |
| 129 | if let Ok(file: File) = File::open(path:OUT_DIR.join(MSVC_LIB_NAME)) { |
| 130 | Ok(file) |
| 131 | } else { |
| 132 | File::open(path:OUT_DIR.join(LIB_NAME)) |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | fn find_all_rust_macro( |
| 137 | input: syn::parse::ParseStream, |
| 138 | ) -> Result<Vec<RustInvocation>, syn::parse::Error> { |
| 139 | let mut r = Vec::<RustInvocation>::new(); |
| 140 | while !input.is_empty() { |
| 141 | if input.peek(kw::rust) { |
| 142 | if let Ok(ri) = input.parse::<RustInvocation>() { |
| 143 | r.push(ri); |
| 144 | } |
| 145 | } else if input.peek(syn::token::Brace) { |
| 146 | let c; |
| 147 | braced!(c in input); |
| 148 | r.extend(find_all_rust_macro(&c)?); |
| 149 | } else if input.peek(syn::token::Paren) { |
| 150 | let c; |
| 151 | parenthesized!(c in input); |
| 152 | r.extend(find_all_rust_macro(&c)?); |
| 153 | } else if input.peek(syn::token::Bracket) { |
| 154 | let c; |
| 155 | bracketed!(c in input); |
| 156 | r.extend(find_all_rust_macro(&c)?); |
| 157 | } else { |
| 158 | input.parse::<proc_macro2::TokenTree>()?; |
| 159 | } |
| 160 | } |
| 161 | Ok(r) |
| 162 | } |
| 163 | |
| 164 | /// Find the occurrence of the `stringify!` macro within the macro derive |
| 165 | fn extract_original_macro(input: &syn::DeriveInput) -> Option<proc_macro2::TokenStream> { |
| 166 | #[derive (Default)] |
| 167 | struct Finder(Option<proc_macro2::TokenStream>); |
| 168 | impl<'ast> syn::visit::Visit<'ast> for Finder { |
| 169 | fn visit_macro(&mut self, mac: &'ast syn::Macro) { |
| 170 | if mac.path.segments.len() == 1 && mac.path.segments[0].ident == "stringify" { |
| 171 | self.0 = Some(mac.tokens.clone()); |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | let mut f: Finder = Finder::default(); |
| 176 | syn::visit::visit_derive_input(&mut f, node:input); |
| 177 | f.0 |
| 178 | } |
| 179 | |
| 180 | #[proc_macro_derive (__cpp_internal_closure)] |
| 181 | #[allow (clippy::cognitive_complexity)] |
| 182 | pub fn expand_internal(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| 183 | assert_eq!( |
| 184 | env!("CARGO_PKG_VERSION" ), |
| 185 | VERSION, |
| 186 | "Internal Error: mismatched cpp_common and cpp_macros versions" |
| 187 | ); |
| 188 | |
| 189 | // Parse the macro input |
| 190 | let input = extract_original_macro(&parse_macro_input!(input as syn::DeriveInput)).unwrap(); |
| 191 | |
| 192 | let closure = match syn::parse2::<cpp_common::Closure>(input) { |
| 193 | Ok(x) => x, |
| 194 | Err(err) => return err.to_compile_error().into(), |
| 195 | }; |
| 196 | |
| 197 | // Get the size data compiled by the build macro |
| 198 | let size_data = match METADATA.get(&closure.sig.name_hash()) { |
| 199 | Some(x) => x, |
| 200 | None => { |
| 201 | #[cfg (not(feature = "docs-only" ))] |
| 202 | return quote!(compile_error! { |
| 203 | r#"This cpp! macro is not found in the library's rust-cpp metadata. |
| 204 | NOTE: Only cpp! macros found directly in the program source will be parsed - |
| 205 | NOTE: They cannot be generated by macro expansion."# }) |
| 206 | .into(); |
| 207 | #[cfg (feature = "docs-only" )] |
| 208 | { |
| 209 | return quote! { |
| 210 | macro_rules! __cpp_closure_impl { |
| 211 | ($($x:tt)*) => { panic!("docs-only" ); } |
| 212 | } |
| 213 | } |
| 214 | .into(); |
| 215 | }; |
| 216 | } |
| 217 | }; |
| 218 | |
| 219 | let mut extern_params = Vec::new(); |
| 220 | let mut tt_args = Vec::new(); |
| 221 | let mut call_args = Vec::new(); |
| 222 | for (i, capture) in closure.sig.captures.iter().enumerate() { |
| 223 | let written_name = &capture.name; |
| 224 | let span = written_name.span(); |
| 225 | let mac_name = Ident::new(&format!("var_ {}" , written_name), span); |
| 226 | let mac_cty = Ident::new(&format!("cty_ {}" , written_name), span); |
| 227 | |
| 228 | // Generate the assertion to check that the size and align of the types |
| 229 | // match before calling. |
| 230 | let MetaData { size, align, .. } = size_data[i + 1]; |
| 231 | let sizeof_msg = format!( |
| 232 | "size_of for argument ` {}` does not match between c++ and \ |
| 233 | rust" , |
| 234 | &capture.name |
| 235 | ); |
| 236 | let alignof_msg = format!( |
| 237 | "align_of for argument ` {}` does not match between c++ and \ |
| 238 | rust" , |
| 239 | &capture.name |
| 240 | ); |
| 241 | let assertion = quote_spanned! {span=> |
| 242 | // Perform a compile time check that the sizes match. This should be |
| 243 | // a no-op. |
| 244 | if false { |
| 245 | #[allow(clippy::transmute_num_to_bytes)] |
| 246 | ::core::mem::transmute::<_, [u8; #size]>( |
| 247 | ::core::ptr::read(&$#mac_name)); |
| 248 | } |
| 249 | |
| 250 | // NOTE: Both of these calls should be dead code in opt builds. |
| 251 | #[allow(clippy::size_of_ref)] { assert!(::core::mem::size_of_val(&$#mac_name) == #size, #sizeof_msg); }; |
| 252 | assert!(::core::mem::align_of_val(&$#mac_name) == #align, |
| 253 | #alignof_msg); |
| 254 | }; |
| 255 | |
| 256 | let mb_mut = if capture.mutable { quote_spanned!(span=> mut) } else { quote!() }; |
| 257 | let ptr = if capture.mutable { |
| 258 | quote_spanned!(span=> *mut) |
| 259 | } else { |
| 260 | quote_spanned!(span=> *const) |
| 261 | }; |
| 262 | |
| 263 | let arg_name = Ident::new(&format!("arg_ {}" , written_name), span); |
| 264 | |
| 265 | extern_params.push(quote_spanned!(span=> #arg_name : #ptr u8)); |
| 266 | |
| 267 | tt_args.push(quote_spanned!(span=> #mb_mut $#mac_name : ident as $#mac_cty : tt)); |
| 268 | |
| 269 | call_args.push(quote_spanned!(span=> { |
| 270 | #assertion |
| 271 | &#mb_mut $#mac_name as #ptr _ as #ptr u8 |
| 272 | })); |
| 273 | } |
| 274 | |
| 275 | let extern_name = closure.sig.extern_name(); |
| 276 | let ret_ty = &closure.sig.ret; |
| 277 | let MetaData { size: ret_size, align: ret_align, flags } = size_data[0]; |
| 278 | let is_void = closure.sig.cpp == "void" ; |
| 279 | |
| 280 | let decl = if is_void { |
| 281 | quote! { |
| 282 | fn #extern_name(#(#extern_params),*); |
| 283 | } |
| 284 | } else { |
| 285 | quote! { |
| 286 | fn #extern_name(#(#extern_params,)* _result: *mut #ret_ty); |
| 287 | } |
| 288 | }; |
| 289 | |
| 290 | let call = if is_void { |
| 291 | assert!(ret_size == 0, "`void` should have a size of 0!" ); |
| 292 | quote! { |
| 293 | #extern_name(#(#call_args),*); |
| 294 | #[allow(clippy::useless_transmute)] |
| 295 | ::core::mem::transmute::<(), (#ret_ty)>(()) |
| 296 | } |
| 297 | } else { |
| 298 | // static assert that the size and alignement are the same |
| 299 | let assert_size = quote! { |
| 300 | if false { |
| 301 | const _assert_size: [(); #ret_size] = [(); ::core::mem::size_of::<#ret_ty>()]; |
| 302 | const _assert_align: [(); #ret_align] = [(); ::core::mem::align_of::<#ret_ty>()]; |
| 303 | } |
| 304 | }; |
| 305 | quote!( |
| 306 | #assert_size |
| 307 | let mut result = ::core::mem::MaybeUninit::<#ret_ty>::uninit(); |
| 308 | #extern_name(#(#call_args,)* result.as_mut_ptr()); |
| 309 | result.assume_init() |
| 310 | ) |
| 311 | }; |
| 312 | |
| 313 | let input = proc_macro2::TokenStream::from_iter([closure.body].iter().cloned()); |
| 314 | let rust_invocations = find_all_rust_macro.parse2(input).expect("rust! macro" ); |
| 315 | let init_callbacks = if !rust_invocations.is_empty() { |
| 316 | let rust_cpp_callbacks = |
| 317 | Ident::new(&format!("rust_cpp_callbacks {}" , *FILE_HASH), Span::call_site()); |
| 318 | let offset = (flags >> 32) as isize; |
| 319 | let callbacks: Vec<Ident> = rust_invocations.iter().map(|x| x.id.clone()).collect(); |
| 320 | quote! { |
| 321 | use ::std::sync::Once; |
| 322 | static INIT_INVOCATIONS: Once = Once::new(); |
| 323 | INIT_INVOCATIONS.call_once(|| { |
| 324 | // #rust_cpp_callbacks is in fact an array. Since we cannot represent it in rust, |
| 325 | // we just are gonna take the pointer to it can offset from that. |
| 326 | extern "C" { |
| 327 | #[no_mangle] |
| 328 | static mut #rust_cpp_callbacks: *const ::std::os::raw::c_void; |
| 329 | } |
| 330 | let callbacks_array : *mut *const ::std::os::raw::c_void = &mut #rust_cpp_callbacks; |
| 331 | let mut offset = #offset; |
| 332 | #( |
| 333 | offset += 1; |
| 334 | *callbacks_array.offset(offset - 1) = #callbacks as *const ::std::os::raw::c_void; |
| 335 | )* |
| 336 | }); |
| 337 | } |
| 338 | } else { |
| 339 | quote!() |
| 340 | }; |
| 341 | |
| 342 | let result = quote! { |
| 343 | extern "C" { |
| 344 | #decl |
| 345 | } |
| 346 | |
| 347 | macro_rules! __cpp_closure_impl { |
| 348 | (#(#tt_args),*) => { |
| 349 | { |
| 350 | #init_callbacks |
| 351 | #call |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | }; |
| 356 | result.into() |
| 357 | } |
| 358 | |
| 359 | #[proc_macro_derive (__cpp_internal_class)] |
| 360 | pub fn expand_wrap_class(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| 361 | // Parse the macro input |
| 362 | let input = extract_original_macro(&parse_macro_input!(input as syn::DeriveInput)).unwrap(); |
| 363 | |
| 364 | let class = match ::syn::parse2::<cpp_common::Class>(input) { |
| 365 | Ok(x) => x, |
| 366 | Err(err) => return err.to_compile_error().into(), |
| 367 | }; |
| 368 | |
| 369 | let hash = class.name_hash(); |
| 370 | let class_name = class.name.clone(); |
| 371 | |
| 372 | // Get the size data compiled by the build macro |
| 373 | let size_data = match METADATA.get(&hash) { |
| 374 | Some(x) => x, |
| 375 | None => { |
| 376 | #[cfg (not(feature = "docs-only" ))] |
| 377 | return quote!(compile_error! { |
| 378 | r#"This cpp_class! macro is not found in the library's rust-cpp metadata. |
| 379 | NOTE: Only cpp_class! macros found directly in the program source will be parsed - |
| 380 | NOTE: They cannot be generated by macro expansion."# }) |
| 381 | .into(); |
| 382 | #[cfg (feature = "docs-only" )] |
| 383 | { |
| 384 | let mut result = quote! { |
| 385 | #[doc(hidden)] |
| 386 | impl ::cpp::CppTrait for #class_name { |
| 387 | type BaseType = usize; |
| 388 | const ARRAY_SIZE: usize = 1; |
| 389 | const CPP_TYPE: &'static str = stringify!(#class_name); |
| 390 | } |
| 391 | #[doc = "NOTE: this trait will only be enabled if the C++ underlying type is trivially copyable" ] |
| 392 | impl ::core::marker::Copy for #class_name { } |
| 393 | #[doc = "NOTE: this trait will only be enabled if the C++ underlying type is copyable" ] |
| 394 | impl ::core::clone::Clone for #class_name { fn clone(&self) -> Self { panic!("docs-only" ) } } |
| 395 | #[doc = "NOTE: this trait will only be enabled if the C++ underlying type is default constructible" ] |
| 396 | impl ::core::default::Default for #class_name { fn default() -> Self { panic!("docs-only" ) } } |
| 397 | }; |
| 398 | if class.derives("PartialEq" ) { |
| 399 | result = quote! { #result |
| 400 | impl ::core::cmp::PartialEq for #class_name { |
| 401 | fn eq(&self, other: &#class_name) -> bool { panic!("docs-only" ) } |
| 402 | } |
| 403 | }; |
| 404 | } |
| 405 | if class.derives("PartialOrd" ) { |
| 406 | result = quote! { #result |
| 407 | impl ::core::cmp::PartialOrd for #class_name { |
| 408 | fn partial_cmp(&self, other: &#class_name) -> ::core::option::Option<::core::cmp::Ordering> { |
| 409 | panic!("docs-only" ) |
| 410 | } |
| 411 | } |
| 412 | }; |
| 413 | } |
| 414 | if class.derives("Ord" ) { |
| 415 | result = quote! { #result |
| 416 | impl ::core::cmp::Ord for #class_name { |
| 417 | fn cmp(&self, other: &#class_name) -> ::core::cmp::Ordering { |
| 418 | panic!("docs-only" ) |
| 419 | } |
| 420 | } |
| 421 | }; |
| 422 | } |
| 423 | return result.into(); |
| 424 | }; |
| 425 | } |
| 426 | }; |
| 427 | |
| 428 | let (size, align) = (size_data[0].size, size_data[0].align); |
| 429 | |
| 430 | let base_type = match align { |
| 431 | 1 => quote!(u8), |
| 432 | 2 => quote!(u16), |
| 433 | 4 => quote!(u32), |
| 434 | 8 => quote!(u64), |
| 435 | _ => panic!("unsupported alignment" ), |
| 436 | }; |
| 437 | |
| 438 | let destructor_name = Ident::new(&format!("__cpp_destructor_ {}" , hash), Span::call_site()); |
| 439 | let copyctr_name = Ident::new(&format!("__cpp_copy_ {}" , hash), Span::call_site()); |
| 440 | let defaultctr_name = Ident::new(&format!("__cpp_default_ {}" , hash), Span::call_site()); |
| 441 | |
| 442 | let mut result = quote! { |
| 443 | #[doc(hidden)] |
| 444 | impl ::cpp::CppTrait for #class_name { |
| 445 | type BaseType = #base_type; |
| 446 | const ARRAY_SIZE: usize = #size / #align; |
| 447 | const CPP_TYPE: &'static str = stringify!(#class_name); |
| 448 | } |
| 449 | }; |
| 450 | if !size_data[0].has_flag(flags::IS_TRIVIALLY_DESTRUCTIBLE) { |
| 451 | result = quote! { #result |
| 452 | impl ::core::ops::Drop for #class_name { |
| 453 | fn drop(&mut self) { |
| 454 | unsafe { |
| 455 | extern "C" { fn #destructor_name(_: *mut #class_name); } |
| 456 | #destructor_name(&mut *self); |
| 457 | } |
| 458 | } |
| 459 | } |
| 460 | }; |
| 461 | }; |
| 462 | |
| 463 | if size_data[0].has_flag(flags::IS_COPY_CONSTRUCTIBLE) { |
| 464 | if !size_data[0].has_flag(flags::IS_TRIVIALLY_COPYABLE) && !class.derives("Copy" ) { |
| 465 | let call_construct = quote!( |
| 466 | let mut result = ::core::mem::MaybeUninit::<Self>::uninit(); |
| 467 | #copyctr_name(& *self, result.as_mut_ptr()); |
| 468 | result.assume_init() |
| 469 | ); |
| 470 | result = quote! { #result |
| 471 | impl ::core::clone::Clone for #class_name { |
| 472 | fn clone(&self) -> Self { |
| 473 | unsafe { |
| 474 | extern "C" { fn #copyctr_name(src: *const #class_name, dst: *mut #class_name); } |
| 475 | #call_construct |
| 476 | } |
| 477 | } |
| 478 | } |
| 479 | }; |
| 480 | } else { |
| 481 | result = quote! { #result |
| 482 | impl ::core::marker::Copy for #class_name { } |
| 483 | impl ::core::clone::Clone for #class_name { |
| 484 | fn clone(&self) -> Self { *self } |
| 485 | } |
| 486 | }; |
| 487 | }; |
| 488 | } else if class.derives("Clone" ) { |
| 489 | panic!("C++ class is not copyable" ); |
| 490 | } |
| 491 | |
| 492 | if size_data[0].has_flag(flags::IS_DEFAULT_CONSTRUCTIBLE) { |
| 493 | let call_construct = quote!( |
| 494 | let mut result = ::core::mem::MaybeUninit::<Self>::uninit(); |
| 495 | #defaultctr_name(result.as_mut_ptr()); |
| 496 | result.assume_init() |
| 497 | ); |
| 498 | result = quote! { #result |
| 499 | impl ::core::default::Default for #class_name { |
| 500 | fn default() -> Self { |
| 501 | unsafe { |
| 502 | extern "C" { fn #defaultctr_name(dst: *mut #class_name); } |
| 503 | #call_construct |
| 504 | } |
| 505 | } |
| 506 | } |
| 507 | }; |
| 508 | } else if class.derives("Default" ) { |
| 509 | panic!("C++ class is not default constructible" ); |
| 510 | } |
| 511 | |
| 512 | if class.derives("PartialEq" ) { |
| 513 | let equal_name = Ident::new(&format!("__cpp_equal_ {}" , hash), Span::call_site()); |
| 514 | result = quote! { #result |
| 515 | impl ::core::cmp::PartialEq for #class_name { |
| 516 | fn eq(&self, other: &#class_name) -> bool { |
| 517 | unsafe { |
| 518 | extern "C" { fn #equal_name(a: *const #class_name, b: *const #class_name) -> bool; } |
| 519 | #equal_name(& *self, other) |
| 520 | } |
| 521 | } |
| 522 | } |
| 523 | }; |
| 524 | } |
| 525 | if class.derives("PartialOrd" ) { |
| 526 | let compare_name = Ident::new(&format!("__cpp_compare_ {}" , hash), Span::call_site()); |
| 527 | let f = |func, cmp| { |
| 528 | quote! { |
| 529 | fn #func(&self, other: &#class_name) -> bool { |
| 530 | unsafe { |
| 531 | extern "C" { fn #compare_name(a: *const #class_name, b: *const #class_name, cmp : i32) -> i32; } |
| 532 | #compare_name(& *self, other, #cmp) != 0 |
| 533 | } |
| 534 | } |
| 535 | } |
| 536 | }; |
| 537 | let lt = f(quote! {lt}, -2); |
| 538 | let gt = f(quote! {gt}, 2); |
| 539 | let le = f(quote! {le}, -1); |
| 540 | let ge = f(quote! {ge}, 1); |
| 541 | result = quote! { #result |
| 542 | impl ::core::cmp::PartialOrd for #class_name { |
| 543 | #lt #gt #le #ge |
| 544 | |
| 545 | fn partial_cmp(&self, other: &#class_name) -> ::core::option::Option<::core::cmp::Ordering> { |
| 546 | use ::core::cmp::Ordering; |
| 547 | unsafe { |
| 548 | extern "C" { fn #compare_name(a: *const #class_name, b: *const #class_name, cmp : i32) -> i32; } |
| 549 | ::core::option::Option::Some(match #compare_name(& *self, other, 0) { |
| 550 | -1 => Ordering::Less, |
| 551 | 0 => Ordering::Equal, |
| 552 | 1 => Ordering::Greater, |
| 553 | _ => panic!() |
| 554 | }) |
| 555 | } |
| 556 | } |
| 557 | } |
| 558 | }; |
| 559 | } |
| 560 | if class.derives("Ord" ) { |
| 561 | let compare_name = Ident::new(&format!("__cpp_compare_ {}" , hash), Span::call_site()); |
| 562 | result = quote! { #result |
| 563 | impl ::core::cmp::Ord for #class_name { |
| 564 | fn cmp(&self, other: &#class_name) -> ::core::cmp::Ordering { |
| 565 | unsafe { |
| 566 | use ::core::cmp::Ordering; |
| 567 | extern "C" { fn #compare_name(a: *const #class_name, b: *const #class_name, cmp : i32) -> i32; } |
| 568 | match #compare_name(& *self, other, 0) { |
| 569 | -1 => Ordering::Less, |
| 570 | 0 => Ordering::Equal, |
| 571 | 1 => Ordering::Greater, |
| 572 | _ => panic!() |
| 573 | } |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | }; |
| 578 | } |
| 579 | |
| 580 | if class.derives("Hash" ) { |
| 581 | panic!("Deriving from Hash is not implemented" ) |
| 582 | }; |
| 583 | if class.derives("Debug" ) { |
| 584 | panic!("Deriving from Debug is not implemented" ) |
| 585 | }; |
| 586 | |
| 587 | result.into() |
| 588 | } |
| 589 | |