└── complex-rust-to-cpp ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs ├── cbindgen.toml ├── cpp ├── doit.cpp ├── my_ffi_forwards.h └── my_ffi_inlines.h └── src ├── main.rs └── owned_slice.rs /complex-rust-to-cpp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.9" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "ansi_term" 14 | version = "0.11.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 17 | dependencies = [ 18 | "winapi", 19 | ] 20 | 21 | [[package]] 22 | name = "atty" 23 | version = "0.2.14" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 26 | dependencies = [ 27 | "hermit-abi", 28 | "libc", 29 | "winapi", 30 | ] 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "1.2.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 37 | 38 | [[package]] 39 | name = "c2-chacha" 40 | version = "0.2.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" 43 | dependencies = [ 44 | "ppv-lite86", 45 | ] 46 | 47 | [[package]] 48 | name = "cbindgen" 49 | version = "0.13.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "2c955bd5e66e36a03612c877585d75219da64e3d9ae5f14d76c979ccb95de070" 52 | dependencies = [ 53 | "clap", 54 | "log", 55 | "proc-macro2", 56 | "quote", 57 | "serde", 58 | "serde_json", 59 | "syn", 60 | "tempfile", 61 | "toml", 62 | ] 63 | 64 | [[package]] 65 | name = "cc" 66 | version = "1.0.50" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" 69 | 70 | [[package]] 71 | name = "cfg-if" 72 | version = "0.1.10" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 75 | 76 | [[package]] 77 | name = "clap" 78 | version = "2.33.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 81 | dependencies = [ 82 | "ansi_term", 83 | "atty", 84 | "bitflags", 85 | "strsim", 86 | "textwrap", 87 | "unicode-width", 88 | "vec_map", 89 | ] 90 | 91 | [[package]] 92 | name = "complex-rust-to-cpp" 93 | version = "0.1.0" 94 | dependencies = [ 95 | "cbindgen", 96 | "cc", 97 | "env_logger", 98 | "log", 99 | ] 100 | 101 | [[package]] 102 | name = "env_logger" 103 | version = "0.7.1" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 106 | dependencies = [ 107 | "atty", 108 | "humantime", 109 | "log", 110 | "regex", 111 | "termcolor", 112 | ] 113 | 114 | [[package]] 115 | name = "getrandom" 116 | version = "0.1.14" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 119 | dependencies = [ 120 | "cfg-if", 121 | "libc", 122 | "wasi", 123 | ] 124 | 125 | [[package]] 126 | name = "hermit-abi" 127 | version = "0.1.8" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" 130 | dependencies = [ 131 | "libc", 132 | ] 133 | 134 | [[package]] 135 | name = "humantime" 136 | version = "1.3.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 139 | dependencies = [ 140 | "quick-error", 141 | ] 142 | 143 | [[package]] 144 | name = "itoa" 145 | version = "0.4.5" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 148 | 149 | [[package]] 150 | name = "lazy_static" 151 | version = "1.4.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 154 | 155 | [[package]] 156 | name = "libc" 157 | version = "0.2.67" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" 160 | 161 | [[package]] 162 | name = "log" 163 | version = "0.4.8" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 166 | dependencies = [ 167 | "cfg-if", 168 | ] 169 | 170 | [[package]] 171 | name = "memchr" 172 | version = "2.3.3" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 175 | 176 | [[package]] 177 | name = "ppv-lite86" 178 | version = "0.2.6" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" 181 | 182 | [[package]] 183 | name = "proc-macro2" 184 | version = "1.0.9" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" 187 | dependencies = [ 188 | "unicode-xid", 189 | ] 190 | 191 | [[package]] 192 | name = "quick-error" 193 | version = "1.2.3" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 196 | 197 | [[package]] 198 | name = "quote" 199 | version = "1.0.2" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" 202 | dependencies = [ 203 | "proc-macro2", 204 | ] 205 | 206 | [[package]] 207 | name = "rand" 208 | version = "0.7.3" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 211 | dependencies = [ 212 | "getrandom", 213 | "libc", 214 | "rand_chacha", 215 | "rand_core", 216 | "rand_hc", 217 | ] 218 | 219 | [[package]] 220 | name = "rand_chacha" 221 | version = "0.2.1" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" 224 | dependencies = [ 225 | "c2-chacha", 226 | "rand_core", 227 | ] 228 | 229 | [[package]] 230 | name = "rand_core" 231 | version = "0.5.1" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 234 | dependencies = [ 235 | "getrandom", 236 | ] 237 | 238 | [[package]] 239 | name = "rand_hc" 240 | version = "0.2.0" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 243 | dependencies = [ 244 | "rand_core", 245 | ] 246 | 247 | [[package]] 248 | name = "redox_syscall" 249 | version = "0.1.56" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 252 | 253 | [[package]] 254 | name = "regex" 255 | version = "1.3.4" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" 258 | dependencies = [ 259 | "aho-corasick", 260 | "memchr", 261 | "regex-syntax", 262 | "thread_local", 263 | ] 264 | 265 | [[package]] 266 | name = "regex-syntax" 267 | version = "0.6.14" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" 270 | 271 | [[package]] 272 | name = "remove_dir_all" 273 | version = "0.5.2" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" 276 | dependencies = [ 277 | "winapi", 278 | ] 279 | 280 | [[package]] 281 | name = "ryu" 282 | version = "1.0.2" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" 285 | 286 | [[package]] 287 | name = "serde" 288 | version = "1.0.104" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" 291 | dependencies = [ 292 | "serde_derive", 293 | ] 294 | 295 | [[package]] 296 | name = "serde_derive" 297 | version = "1.0.104" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" 300 | dependencies = [ 301 | "proc-macro2", 302 | "quote", 303 | "syn", 304 | ] 305 | 306 | [[package]] 307 | name = "serde_json" 308 | version = "1.0.48" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" 311 | dependencies = [ 312 | "itoa", 313 | "ryu", 314 | "serde", 315 | ] 316 | 317 | [[package]] 318 | name = "strsim" 319 | version = "0.8.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 322 | 323 | [[package]] 324 | name = "syn" 325 | version = "1.0.16" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" 328 | dependencies = [ 329 | "proc-macro2", 330 | "quote", 331 | "unicode-xid", 332 | ] 333 | 334 | [[package]] 335 | name = "tempfile" 336 | version = "3.1.0" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 339 | dependencies = [ 340 | "cfg-if", 341 | "libc", 342 | "rand", 343 | "redox_syscall", 344 | "remove_dir_all", 345 | "winapi", 346 | ] 347 | 348 | [[package]] 349 | name = "termcolor" 350 | version = "1.1.0" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 353 | dependencies = [ 354 | "winapi-util", 355 | ] 356 | 357 | [[package]] 358 | name = "textwrap" 359 | version = "0.11.0" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 362 | dependencies = [ 363 | "unicode-width", 364 | ] 365 | 366 | [[package]] 367 | name = "thread_local" 368 | version = "1.0.1" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 371 | dependencies = [ 372 | "lazy_static", 373 | ] 374 | 375 | [[package]] 376 | name = "toml" 377 | version = "0.5.6" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" 380 | dependencies = [ 381 | "serde", 382 | ] 383 | 384 | [[package]] 385 | name = "unicode-width" 386 | version = "0.1.7" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" 389 | 390 | [[package]] 391 | name = "unicode-xid" 392 | version = "0.2.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 395 | 396 | [[package]] 397 | name = "vec_map" 398 | version = "0.8.1" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 401 | 402 | [[package]] 403 | name = "wasi" 404 | version = "0.9.0+wasi-snapshot-preview1" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 407 | 408 | [[package]] 409 | name = "winapi" 410 | version = "0.3.8" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 413 | dependencies = [ 414 | "winapi-i686-pc-windows-gnu", 415 | "winapi-x86_64-pc-windows-gnu", 416 | ] 417 | 418 | [[package]] 419 | name = "winapi-i686-pc-windows-gnu" 420 | version = "0.4.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 423 | 424 | [[package]] 425 | name = "winapi-util" 426 | version = "0.1.3" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" 429 | dependencies = [ 430 | "winapi", 431 | ] 432 | 433 | [[package]] 434 | name = "winapi-x86_64-pc-windows-gnu" 435 | version = "0.4.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 438 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "complex-rust-to-cpp" 3 | version = "0.1.0" 4 | authors = ["Emilio Cobos Álvarez "] 5 | edition = "2018" 6 | 7 | [build-dependencies] 8 | cbindgen = "0.13" 9 | cc = "1.0" 10 | log = "0.4" 11 | env_logger = "0.7" 12 | 13 | [dependencies] 14 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/README.md: -------------------------------------------------------------------------------- 1 | # Exposing a complex data-structure from rust to C++ 2 | 3 | This is a somewhat reduced example of how you can expose complex rust 4 | data-structures to C++ seamlessly. 5 | 6 | It uses C++20 because I didn't want to write my own `span` :-) 7 | 8 | I wrote a [blog post explaining 9 | it](https://crisal.io/words/2020/02/28/C++-rust-ffi-patterns-1-complex-data-structures.html). 10 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/build.rs: -------------------------------------------------------------------------------- 1 | extern crate cbindgen; 2 | extern crate cc; 3 | 4 | use std::env; 5 | use std::path::PathBuf; 6 | 7 | fn main() { 8 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); 9 | let cbindgen_toml = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("cbindgen.toml"); 10 | 11 | let bindings = cbindgen::Builder::new() 12 | .with_config(cbindgen::Config::from_file(&cbindgen_toml).unwrap()) 13 | // This wouldn't be needed if it was a library crate or something. 14 | .with_src("src/main.rs") 15 | .generate() 16 | .unwrap(); 17 | 18 | let _changed = bindings.write_to_file(out_dir.join("my_ffi.h")); 19 | 20 | let cpp_file = "cpp/doit.cpp"; 21 | 22 | cc::Build::new() 23 | .cpp(true) 24 | .file(cpp_file) 25 | .flag("-I") 26 | .flag(&format!("{}", out_dir.display())) 27 | .flag("-I") 28 | .flag("cpp") 29 | .flag("-std=c++20") 30 | .compile("libdoit.a"); 31 | 32 | println!("cargo:rerun-if-changed={}", cbindgen_toml.display()); 33 | println!("cargo:rerun-if-changed={}", cpp_file); 34 | } 35 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/cbindgen.toml: -------------------------------------------------------------------------------- 1 | autogen_warning = "/* DO NOT MODIFY MANUALLY, see build.rs, and cbindgen.toml */" 2 | include_guard = "my_ffi_h" 3 | namespaces = ["my_ffi"] 4 | trailer = """ 5 | #include "my_ffi_inlines.h" 6 | """ 7 | includes = ["my_ffi_forwards.h"] 8 | 9 | [struct] 10 | # generates operator== 11 | derive_eq = true 12 | # generates operator!= 13 | derive_neq = true 14 | 15 | [enum] 16 | # Generates IsFoo() methods. 17 | derive_helper_methods = true 18 | # Generates `const T& AsFoo() const` methods. 19 | derive_const_casts = true 20 | # Adds an `assert(IsFoo())` on each `AsFoo()` method. 21 | cast_assert_name = "assert" 22 | # Generates destructors 23 | derive_tagged_enum_destructor = true 24 | # Generates copy-constructors. 25 | derive_tagged_enum_copy_constructor = true 26 | # Generates copy-assignment operators. 27 | derive_tagged_enum_copy_assignment = true 28 | # Generates a private default-constructor for enums that doesn't initialize 29 | # anything. Either you do this or you provide your own default constructor. 30 | private_default_tagged_enum_constructor = true 31 | 32 | [export.body] 33 | "OwnedSlice" = """ 34 | inline void Clear(); 35 | inline void CopyFrom(const OwnedSlice&); 36 | 37 | // cpp shenanigans. 38 | inline OwnedSlice(); 39 | inline ~OwnedSlice(); 40 | inline OwnedSlice(const OwnedSlice&); 41 | inline OwnedSlice& operator=(const OwnedSlice&); 42 | 43 | std::span AsSpan() { 44 | return { ptr, len }; 45 | } 46 | 47 | inline std::span AsSpan() const { 48 | return { ptr, len }; 49 | } 50 | 51 | bool IsEmpty() { 52 | return AsSpan().empty(); 53 | } 54 | 55 | inline bool operator==(const OwnedSlice&) const; 56 | inline bool operator!=(const OwnedSlice&) const; 57 | """ 58 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/cpp/doit.cpp: -------------------------------------------------------------------------------- 1 | #include "my_ffi.h" 2 | 3 | using namespace my_ffi; 4 | 5 | static float ProcessNode(const TreeNode& node) { 6 | switch (node.tag) { 7 | case TreeNode::Tag::Leaf: 8 | return node.AsLeaf(); 9 | case TreeNode::Tag::Sum: { 10 | float result = 0.0; 11 | for (auto& child : node.AsSum().AsSpan()) { 12 | result += ProcessNode(child); 13 | } 14 | return result; 15 | } 16 | case TreeNode::Tag::Cmp: { 17 | float left = ProcessNode(*node.AsCmp()._0); 18 | float right = ProcessNode(*node.AsCmp()._1); 19 | return fabs(left - right) < 1e-6; 20 | } 21 | } 22 | assert(false && "Should be unreachable"); 23 | return 0.0f; 24 | } 25 | 26 | extern "C" float DoTheMathInCPlusPlus(const TreeNode* node) { 27 | // This (silly) copy shouldn't leak or anything. Just here for demo purposes. 28 | TreeNode nodeCopy = *node; 29 | float answer = ProcessNode(*node); 30 | assert(answer == ProcessNode(nodeCopy)); 31 | return answer; 32 | } 33 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/cpp/my_ffi_forwards.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /* 9 | * Here go definitions that cbindgen-generated code needs, for one reason or 10 | * another 11 | */ 12 | 13 | namespace my_ffi { 14 | 15 | // A layout-compatible (but not ABI-compatible!) version of Rust's Box. 16 | // 17 | // Basically, a non-null unique_ptr, where operator== and operator!= have value 18 | // semantics. 19 | template 20 | struct Box { 21 | Box() = delete; // For good measure. 22 | 23 | ~Box() { 24 | assert(m_raw); 25 | m_raw->~T(); 26 | free(m_raw); 27 | } 28 | 29 | Box(const Box& other) { 30 | m_raw = (T*)malloc(sizeof(T)); 31 | new (m_raw) T(*other); 32 | } 33 | 34 | Box& operator=(const Box& other) const { 35 | if (this != &other) { 36 | this->~Box(); 37 | new (this) Box(other); 38 | } 39 | return *this; 40 | } 41 | 42 | const T* operator->() const { 43 | assert(m_raw); 44 | return m_raw; 45 | } 46 | 47 | const T& operator*() const { 48 | assert(m_raw); 49 | return *m_raw; 50 | } 51 | 52 | T* operator->() { 53 | assert(m_raw); 54 | return m_raw; 55 | } 56 | 57 | T& operator*() { 58 | assert(m_raw); 59 | return *m_raw; 60 | } 61 | 62 | bool operator==(const Box& other) const { return *(*this) == *other; } 63 | bool operator!=(const Box& other) const { return *(*this) != *other; } 64 | 65 | private: 66 | T* m_raw; 67 | }; 68 | 69 | } // namespace my_ffi 70 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/cpp/my_ffi_inlines.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "my_ffi.h" 4 | 5 | /* 6 | * Here goes the implementation of the methods and such that we define in 7 | * cbindgen.toml. 8 | */ 9 | 10 | namespace my_ffi { 11 | 12 | template 13 | void OwnedSlice::Clear() { 14 | // This assumes that your C++ code and your Rust code share allocators, 15 | // otherwise it should do something else (call into rust to call drop_in_place 16 | // or something). 17 | if (!len) 18 | return; 19 | for (auto& val : AsSpan()) 20 | val.~T(); 21 | free(ptr); 22 | ptr = (T*)alignof(T); 23 | len = 0; 24 | } 25 | 26 | template 27 | void OwnedSlice::CopyFrom(const OwnedSlice& other) { 28 | Clear(); 29 | 30 | auto other_span = other.AsSpan(); 31 | if (other_span.empty()) 32 | return; 33 | 34 | ptr = (T*)malloc(sizeof(T) * other_span.size()); 35 | len = other_span.size(); 36 | 37 | size_t i = 0; 38 | for (auto& elem : other_span) 39 | new (ptr + i++) T(elem); 40 | } 41 | 42 | template 43 | OwnedSlice::OwnedSlice() 44 | : ptr((T*)alignof(T)) 45 | , len(0) 46 | {} 47 | 48 | template 49 | OwnedSlice::~OwnedSlice() { 50 | Clear(); 51 | } 52 | 53 | template 54 | OwnedSlice::OwnedSlice(const OwnedSlice& other) : OwnedSlice() { 55 | CopyFrom(other); 56 | } 57 | 58 | template 59 | OwnedSlice& OwnedSlice::operator=(const OwnedSlice& other) { 60 | if (this != &other) 61 | CopyFrom(other); 62 | return *this; 63 | } 64 | 65 | template 66 | bool OwnedSlice::operator==(const OwnedSlice& other) const { 67 | // Apparently span doesn't implement operator==? 68 | auto self_span = AsSpan(); 69 | auto other_span = other.AsSpan(); 70 | if (self_span.size() != other_span.size()) 71 | return false; 72 | for (size_t i = 0; i < self_span.size(); ++i) 73 | if (self_span[i] != other_span[i]) 74 | return false; 75 | return true; 76 | } 77 | 78 | template 79 | bool OwnedSlice::operator!=(const OwnedSlice& other) const { 80 | return !(*this == other); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/src/main.rs: -------------------------------------------------------------------------------- 1 | // The my_ffi::Box and my_ffi::OwnedSlice implementations rely on sharing the 2 | // allocator with the rust code. Otherwise we'd need to call into Rust to free 3 | // our stuff. 4 | #[global_allocator] 5 | static A: std::alloc::System = std::alloc::System; 6 | 7 | mod owned_slice; 8 | 9 | use owned_slice::OwnedSlice; 10 | 11 | #[derive(Clone, Debug)] 12 | #[repr(C, u8)] 13 | pub enum TreeNode { 14 | /// This node just has a value. 15 | Leaf(f32), 16 | /// This node sums all the children. 17 | Sum(OwnedSlice), 18 | /// This node returns 1 if the two things are the same, and zero otherwise. 19 | Cmp(Box, Box), 20 | } 21 | 22 | extern "C" { 23 | fn DoTheMathInCPlusPlus(node: *const TreeNode) -> f32; 24 | } 25 | 26 | fn main() { 27 | let my_complex_node = TreeNode::Sum(vec![ 28 | TreeNode::Leaf(1.0), 29 | TreeNode::Cmp( 30 | Box::new(TreeNode::Leaf(0.0)), 31 | Box::new(TreeNode::Sum(vec![ 32 | TreeNode::Leaf(2.0), 33 | TreeNode::Leaf(-1.0), 34 | TreeNode::Leaf(-5.0), 35 | TreeNode::Leaf(4.0), 36 | ].into())) 37 | ) 38 | ].into()); 39 | 40 | assert_eq!(unsafe { DoTheMathInCPlusPlus(&my_complex_node) }, 2.0); 41 | } 42 | -------------------------------------------------------------------------------- /complex-rust-to-cpp/src/owned_slice.rs: -------------------------------------------------------------------------------- 1 | #![allow(unsafe_code)] 2 | 3 | use std::ops::{Deref, DerefMut}; 4 | use std::ptr::NonNull; 5 | use std::{fmt, iter, mem, slice}; 6 | use std::marker::PhantomData; 7 | 8 | /// A struct that basically replaces a `Box<[T]>`, but which cbindgen can 9 | /// understand. 10 | /// 11 | /// We could rely on the struct layout of `Box<[T]>` per: 12 | /// 13 | /// https://github.com/rust-lang/unsafe-code-guidelines/blob/master/reference/src/layout/pointers.md 14 | /// 15 | /// But handling fat pointers with cbindgen both in structs and argument 16 | /// positions more generally is a bit tricky. 17 | /// 18 | /// cbindgen:derive-eq=false 19 | /// cbindgen:derive-neq=false 20 | #[repr(C)] 21 | pub struct OwnedSlice { 22 | ptr: NonNull, 23 | len: usize, 24 | _phantom: PhantomData, 25 | } 26 | 27 | impl Default for OwnedSlice { 28 | #[inline] 29 | fn default() -> Self { 30 | Self { 31 | len: 0, 32 | ptr: NonNull::dangling(), 33 | _phantom: PhantomData, 34 | } 35 | } 36 | } 37 | 38 | impl Drop for OwnedSlice { 39 | #[inline] 40 | fn drop(&mut self) { 41 | if self.len != 0 { 42 | let _ = mem::replace(self, Self::default()).into_vec(); 43 | } 44 | } 45 | } 46 | 47 | unsafe impl Send for OwnedSlice {} 48 | unsafe impl Sync for OwnedSlice {} 49 | 50 | impl Clone for OwnedSlice { 51 | #[inline] 52 | fn clone(&self) -> Self { 53 | Self::from_slice(&**self) 54 | } 55 | } 56 | 57 | impl fmt::Debug for OwnedSlice { 58 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 59 | self.deref().fmt(formatter) 60 | } 61 | } 62 | 63 | impl PartialEq for OwnedSlice { 64 | fn eq(&self, other: &Self) -> bool { 65 | self.deref().eq(other.deref()) 66 | } 67 | } 68 | 69 | impl Eq for OwnedSlice {} 70 | 71 | impl OwnedSlice { 72 | /// Convert the OwnedSlice into a boxed slice. 73 | #[inline] 74 | pub fn into_box(self) -> Box<[T]> { 75 | self.into_vec().into_boxed_slice() 76 | } 77 | 78 | /// Convert the OwnedSlice into a Vec. 79 | #[inline] 80 | pub fn into_vec(self) -> Vec { 81 | let ret = unsafe { Vec::from_raw_parts(self.ptr.as_ptr(), self.len, self.len) }; 82 | mem::forget(self); 83 | ret 84 | } 85 | 86 | /// Iterate over all the elements in the slice taking ownership of them. 87 | #[inline] 88 | pub fn into_iter(self) -> impl Iterator + ExactSizeIterator { 89 | self.into_vec().into_iter() 90 | } 91 | 92 | /// Convert the regular slice into an owned slice. 93 | #[inline] 94 | pub fn from_slice(s: &[T]) -> Self 95 | where 96 | T: Clone, 97 | { 98 | Self::from(s.to_vec()) 99 | } 100 | } 101 | 102 | impl Deref for OwnedSlice { 103 | type Target = [T]; 104 | 105 | #[inline(always)] 106 | fn deref(&self) -> &Self::Target { 107 | unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } 108 | } 109 | } 110 | 111 | impl DerefMut for OwnedSlice { 112 | #[inline(always)] 113 | fn deref_mut(&mut self) -> &mut Self::Target { 114 | unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } 115 | } 116 | } 117 | 118 | impl From> for OwnedSlice { 119 | #[inline] 120 | fn from(mut b: Box<[T]>) -> Self { 121 | let len = b.len(); 122 | let ptr = unsafe { NonNull::new_unchecked(b.as_mut_ptr()) }; 123 | mem::forget(b); 124 | Self { 125 | len, 126 | ptr, 127 | _phantom: PhantomData, 128 | } 129 | } 130 | } 131 | 132 | impl From> for OwnedSlice { 133 | #[inline] 134 | fn from(b: Vec) -> Self { 135 | Self::from(b.into_boxed_slice()) 136 | } 137 | } 138 | 139 | impl iter::FromIterator for OwnedSlice { 140 | #[inline] 141 | fn from_iter>(iter: I) -> Self { 142 | Vec::from_iter(iter).into() 143 | } 144 | } 145 | --------------------------------------------------------------------------------