Repository: dtolnay/cxx Branch: master Commit: 9634c7d5167e Files: 463 Total size: 1.2 MB Directory structure: gitextract_dh2rnm1j/ ├── .bazelignore ├── .bazelrc ├── .bcr/ │ ├── README.md │ ├── config.yml │ ├── metadata.template.json │ ├── presubmit.yml │ └── source.template.json ├── .buckconfig ├── .buckroot ├── .clang-format ├── .clang-tidy ├── .devcontainer/ │ ├── Dockerfile │ ├── README.md │ ├── build.Dockerfile │ └── devcontainer.json ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── buck2.yml │ ├── ci.yml │ ├── install.yml │ ├── release.yml │ └── site.yml ├── .gitignore ├── .vscode/ │ ├── README.md │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── .watchmanconfig ├── BUCK ├── BUILD.bazel ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── MODULE.bazel ├── README.md ├── book/ │ ├── .gitignore │ ├── README.md │ ├── book.toml │ ├── build.js │ ├── build.sh │ ├── css/ │ │ └── cxx.css │ ├── diagram/ │ │ ├── .gitignore │ │ ├── Makefile │ │ └── overview.tex │ ├── eslint.config.mjs │ ├── package.json │ ├── src/ │ │ ├── 404.md │ │ ├── SUMMARY.md │ │ ├── async.md │ │ ├── attributes.md │ │ ├── binding/ │ │ │ ├── box.md │ │ │ ├── cxxstring.md │ │ │ ├── cxxvector.md │ │ │ ├── fn.md │ │ │ ├── rawptr.md │ │ │ ├── result.md │ │ │ ├── sharedptr.md │ │ │ ├── slice.md │ │ │ ├── str.md │ │ │ ├── string.md │ │ │ ├── uniqueptr.md │ │ │ └── vec.md │ │ ├── bindings.md │ │ ├── build/ │ │ │ ├── bazel.md │ │ │ ├── cargo.md │ │ │ ├── cmake.md │ │ │ └── other.md │ │ ├── building.md │ │ ├── concepts.md │ │ ├── context.md │ │ ├── extern-c++.md │ │ ├── extern-rust.md │ │ ├── index.md │ │ ├── reference.md │ │ ├── shared.md │ │ └── tutorial.md │ └── theme/ │ └── head.hbs ├── build.rs ├── compile_flags.txt ├── demo/ │ ├── BUCK │ ├── BUILD.bazel │ ├── Cargo.toml │ ├── build.rs │ ├── include/ │ │ └── blobstore.h │ └── src/ │ ├── blobstore.cc │ └── main.rs ├── flags/ │ ├── Cargo.toml │ └── src/ │ ├── impl.rs │ └── lib.rs ├── gen/ │ ├── README.md │ ├── build/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src/ │ │ ├── cargo.rs │ │ ├── cfg.rs │ │ ├── deps.rs │ │ ├── error.rs │ │ ├── intern.rs │ │ ├── lib.rs │ │ ├── out.rs │ │ ├── paths.rs │ │ ├── target.rs │ │ └── vec.rs │ ├── cmd/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src/ │ │ ├── app.rs │ │ ├── cfg.rs │ │ ├── main.rs │ │ ├── output.rs │ │ └── test.rs │ ├── lib/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── src/ │ │ │ ├── error.rs │ │ │ └── lib.rs │ │ └── tests/ │ │ └── test.rs │ └── src/ │ ├── block.rs │ ├── builtin/ │ │ ├── alignmax.h │ │ ├── deleter_if.h │ │ ├── destroy.h │ │ ├── friend_impl.h │ │ ├── manually_drop.h │ │ ├── maybe_uninit.h │ │ ├── maybe_uninit_detail.h │ │ ├── ptr_len.h │ │ ├── relocatable_or_array.h │ │ ├── repr_fat.h │ │ ├── rust_error.h │ │ ├── rust_slice_uninit.h │ │ ├── rust_str_uninit.h │ │ ├── shared_ptr.h │ │ ├── trycatch.h │ │ ├── trycatch_detail.h │ │ └── vector.h │ ├── builtin.rs │ ├── cfg.rs │ ├── check.rs │ ├── error.rs │ ├── file.rs │ ├── fs.rs │ ├── guard.rs │ ├── ifndef.rs │ ├── include.rs │ ├── mod.rs │ ├── names.rs │ ├── namespace.rs │ ├── nested.rs │ ├── out.rs │ ├── pragma.rs │ └── write.rs ├── include/ │ └── cxx.h ├── macro/ │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src/ │ ├── attrs.rs │ ├── cfg.rs │ ├── derive.rs │ ├── expand.rs │ ├── generics.rs │ ├── lib.rs │ ├── tests.rs │ ├── tokens.rs │ └── type_id.rs ├── reindeer.toml ├── rust-toolchain.toml ├── src/ │ ├── cxx.cc │ ├── cxx_string.rs │ ├── cxx_vector.rs │ ├── exception.rs │ ├── extern_type.rs │ ├── fmt.rs │ ├── function.rs │ ├── hash.rs │ ├── lib.rs │ ├── lossy.rs │ ├── macros/ │ │ ├── assert.rs │ │ └── mod.rs │ ├── memory.rs │ ├── opaque.rs │ ├── result.rs │ ├── rust_slice.rs │ ├── rust_str.rs │ ├── rust_string.rs │ ├── rust_type.rs │ ├── rust_vec.rs │ ├── shared_ptr.rs │ ├── symbols/ │ │ ├── exception.rs │ │ ├── mod.rs │ │ ├── rust_slice.rs │ │ ├── rust_str.rs │ │ ├── rust_string.rs │ │ └── rust_vec.rs │ ├── type_id.rs │ ├── unique_ptr.rs │ ├── unwind.rs │ ├── vector.rs │ └── weak_ptr.rs ├── syntax/ │ ├── atom.rs │ ├── attrs.rs │ ├── cfg.rs │ ├── check.rs │ ├── derive.rs │ ├── discriminant.rs │ ├── doc.rs │ ├── error.rs │ ├── file.rs │ ├── ident.rs │ ├── impls.rs │ ├── improper.rs │ ├── instantiate.rs │ ├── mangle.rs │ ├── map.rs │ ├── message.rs │ ├── mod.rs │ ├── names.rs │ ├── namespace.rs │ ├── parse.rs │ ├── pod.rs │ ├── primitive.rs │ ├── qualified.rs │ ├── query.rs │ ├── report.rs │ ├── repr.rs │ ├── resolve.rs │ ├── set.rs │ ├── signature.rs │ ├── symbol.rs │ ├── tokens.rs │ ├── toposort.rs │ ├── trivial.rs │ ├── types.rs │ ├── unpin.rs │ └── visit.rs ├── tests/ │ ├── BUCK │ ├── BUILD.bazel │ ├── README.md │ ├── compiletest.rs │ ├── cpp_compile/ │ │ ├── mod.rs │ │ └── smoke_test.rs │ ├── cpp_ui_tests.rs │ ├── cxx_gen.rs │ ├── cxx_string.rs │ ├── cxx_vector.rs │ ├── ffi/ │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── cast.rs │ │ ├── lib.rs │ │ ├── module.rs │ │ ├── tests.cc │ │ └── tests.h │ ├── test.rs │ ├── ui/ │ │ ├── array_len_expr.rs │ │ ├── array_len_expr.stderr │ │ ├── array_len_suffix.rs │ │ ├── array_len_suffix.stderr │ │ ├── async_fn.rs │ │ ├── async_fn.stderr │ │ ├── bad_explicit_impl.rs │ │ ├── bad_explicit_impl.stderr │ │ ├── by_value_not_supported.rs │ │ ├── by_value_not_supported.stderr │ │ ├── const_fn.rs │ │ ├── const_fn.stderr │ │ ├── cxx_crate_name_qualified_cxx_string.rs │ │ ├── cxx_crate_name_qualified_cxx_string.stderr │ │ ├── data_enums.rs │ │ ├── data_enums.stderr │ │ ├── deny_elided_lifetimes.rs │ │ ├── deny_elided_lifetimes.stderr │ │ ├── deny_missing_docs.rs │ │ ├── deny_missing_docs.stderr │ │ ├── derive_bit_struct.rs │ │ ├── derive_bit_struct.stderr │ │ ├── derive_default.rs │ │ ├── derive_default.stderr │ │ ├── derive_duplicate.rs │ │ ├── derive_duplicate.stderr │ │ ├── derive_noncopy.rs │ │ ├── derive_noncopy.stderr │ │ ├── drop_shared.rs │ │ ├── drop_shared.stderr │ │ ├── duplicate_method.rs │ │ ├── duplicate_method.stderr │ │ ├── empty_enum.rs │ │ ├── empty_enum.stderr │ │ ├── empty_struct.rs │ │ ├── empty_struct.stderr │ │ ├── enum_assoc.rs │ │ ├── enum_assoc.stderr │ │ ├── enum_inconsistent.rs │ │ ├── enum_inconsistent.stderr │ │ ├── enum_match_without_wildcard.rs │ │ ├── enum_match_without_wildcard.stderr │ │ ├── enum_out_of_bounds.rs │ │ ├── enum_out_of_bounds.stderr │ │ ├── enum_overflows.rs │ │ ├── enum_overflows.stderr │ │ ├── enum_receiver.rs │ │ ├── enum_receiver.stderr │ │ ├── enum_unsatisfiable.rs │ │ ├── enum_unsatisfiable.stderr │ │ ├── expected_named.rs │ │ ├── expected_named.stderr │ │ ├── explicit_impl_of_bad_unique_ptr.rs │ │ ├── explicit_impl_of_bad_unique_ptr.stderr │ │ ├── extern_fn_abi.rs │ │ ├── extern_fn_abi.stderr │ │ ├── extern_shared_struct.rs │ │ ├── extern_shared_struct.stderr │ │ ├── extern_type_bound.rs │ │ ├── extern_type_bound.stderr │ │ ├── extern_type_generic.rs │ │ ├── extern_type_generic.stderr │ │ ├── extern_type_lifetime_bound.rs │ │ ├── extern_type_lifetime_bound.stderr │ │ ├── fallible_fnptr.rs │ │ ├── fallible_fnptr.stderr │ │ ├── function_with_body.rs │ │ ├── function_with_body.stderr │ │ ├── generic_enum.rs │ │ ├── generic_enum.stderr │ │ ├── impl_trait_for_type.rs │ │ ├── impl_trait_for_type.stderr │ │ ├── include.rs │ │ ├── include.stderr │ │ ├── lifetime_extern_cxx.rs │ │ ├── lifetime_extern_cxx.stderr │ │ ├── lifetime_extern_rust.rs │ │ ├── lifetime_extern_rust.stderr │ │ ├── missing_unsafe.rs │ │ ├── missing_unsafe.stderr │ │ ├── multiple_parse_error.rs │ │ ├── multiple_parse_error.stderr │ │ ├── mut_return.rs │ │ ├── mut_return.stderr │ │ ├── non_integer_discriminant_enum.rs │ │ ├── non_integer_discriminant_enum.stderr │ │ ├── nonempty_impl_block.rs │ │ ├── nonempty_impl_block.stderr │ │ ├── nonlocal_rust_type.rs │ │ ├── nonlocal_rust_type.stderr │ │ ├── opaque_autotraits.rs │ │ ├── opaque_autotraits.stderr │ │ ├── opaque_not_sized.rs │ │ ├── opaque_not_sized.stderr │ │ ├── pin_mut_alias.rs │ │ ├── pin_mut_alias.stderr │ │ ├── pin_mut_opaque.rs │ │ ├── pin_mut_opaque.stderr │ │ ├── ptr_in_fnptr.rs │ │ ├── ptr_in_fnptr.stderr │ │ ├── ptr_missing_unsafe.rs │ │ ├── ptr_missing_unsafe.stderr │ │ ├── ptr_no_const_mut.rs │ │ ├── ptr_no_const_mut.stderr │ │ ├── ptr_unsupported.rs │ │ ├── ptr_unsupported.stderr │ │ ├── raw_ident_namespace.rs │ │ ├── raw_ident_namespace.stderr │ │ ├── reference_to_reference.rs │ │ ├── reference_to_reference.stderr │ │ ├── repr_align_suffixed.rs │ │ ├── repr_align_suffixed.stderr │ │ ├── repr_unsupported.rs │ │ ├── repr_unsupported.stderr │ │ ├── reserved_lifetime.rs │ │ ├── reserved_lifetime.stderr │ │ ├── reserved_name.rs │ │ ├── reserved_name.stderr │ │ ├── result_no_display.rs │ │ ├── result_no_display.stderr │ │ ├── root_namespace.rs │ │ ├── root_namespace.stderr │ │ ├── rust_pinned.rs │ │ ├── rust_pinned.stderr │ │ ├── self_lifetimes.rs │ │ ├── self_lifetimes.stderr │ │ ├── self_type_and_receiver.rs │ │ ├── self_type_and_receiver.stderr │ │ ├── slice_of_pinned.rs │ │ ├── slice_of_pinned.stderr │ │ ├── slice_of_type_alias.rs │ │ ├── slice_of_type_alias.stderr │ │ ├── slice_unsupported.rs │ │ ├── slice_unsupported.stderr │ │ ├── struct_align.rs │ │ ├── struct_align.stderr │ │ ├── struct_cycle.rs │ │ ├── struct_cycle.stderr │ │ ├── type_alias_rust.rs │ │ ├── type_alias_rust.stderr │ │ ├── undeclared_lifetime.rs │ │ ├── undeclared_lifetime.stderr │ │ ├── unique_ptr_as_mut.rs │ │ ├── unique_ptr_as_mut.stderr │ │ ├── unique_ptr_to_opaque.rs │ │ ├── unique_ptr_to_opaque.stderr │ │ ├── unique_ptr_twice.rs │ │ ├── unique_ptr_twice.stderr │ │ ├── unnamed_receiver.rs │ │ ├── unnamed_receiver.stderr │ │ ├── unpin_impl.rs │ │ ├── unpin_impl.stderr │ │ ├── unrecognized_receiver.rs │ │ ├── unrecognized_receiver.stderr │ │ ├── unsupported_elided.rs │ │ ├── unsupported_elided.stderr │ │ ├── vec_opaque.rs │ │ ├── vec_opaque.stderr │ │ ├── vector_autotraits.rs │ │ ├── vector_autotraits.stderr │ │ ├── wrong_type_id.rs │ │ └── wrong_type_id.stderr │ └── unique_ptr.rs ├── third-party/ │ ├── .cargo/ │ │ └── .gitignore │ ├── .gitignore │ ├── BUCK │ ├── BUILD.bazel │ ├── Cargo.toml │ ├── bazel/ │ │ ├── BUILD.anstyle-1.0.13.bazel │ │ ├── BUILD.bazel │ │ ├── BUILD.cc-1.2.53.bazel │ │ ├── BUILD.clap-4.5.54.bazel │ │ ├── BUILD.clap_builder-4.5.54.bazel │ │ ├── BUILD.clap_lex-0.7.7.bazel │ │ ├── BUILD.codespan-reporting-0.13.1.bazel │ │ ├── BUILD.equivalent-1.0.2.bazel │ │ ├── BUILD.find-msvc-tools-0.1.8.bazel │ │ ├── BUILD.foldhash-0.2.0.bazel │ │ ├── BUILD.hashbrown-0.16.1.bazel │ │ ├── BUILD.indexmap-2.13.0.bazel │ │ ├── BUILD.proc-macro2-1.0.105.bazel │ │ ├── BUILD.quote-1.0.43.bazel │ │ ├── BUILD.rustversion-1.0.22.bazel │ │ ├── BUILD.scratch-1.0.9.bazel │ │ ├── BUILD.serde-1.0.228.bazel │ │ ├── BUILD.serde_core-1.0.228.bazel │ │ ├── BUILD.serde_derive-1.0.228.bazel │ │ ├── BUILD.shlex-1.3.0.bazel │ │ ├── BUILD.syn-2.0.114.bazel │ │ ├── BUILD.termcolor-1.4.1.bazel │ │ ├── BUILD.unicode-ident-1.0.22.bazel │ │ ├── BUILD.unicode-width-0.2.2.bazel │ │ ├── BUILD.winapi-util-0.1.11.bazel │ │ ├── BUILD.windows-link-0.2.1.bazel │ │ ├── BUILD.windows-sys-0.61.2.bazel │ │ ├── alias_rules.bzl │ │ ├── crates.bzl │ │ └── defs.bzl │ ├── fixups/ │ │ ├── proc-macro2/ │ │ │ └── fixups.toml │ │ ├── quote/ │ │ │ └── fixups.toml │ │ ├── rustversion/ │ │ │ └── fixups.toml │ │ ├── scratch/ │ │ │ └── fixups.toml │ │ ├── serde/ │ │ │ └── fixups.toml │ │ ├── serde_core/ │ │ │ └── fixups.toml │ │ ├── serde_derive/ │ │ │ └── fixups.toml │ │ ├── winapi-util/ │ │ │ └── fixups.toml │ │ └── windows-sys/ │ │ └── fixups.toml │ └── src/ │ └── lib.rs └── tools/ ├── bazel/ │ ├── BUILD.bazel │ ├── extension.bzl │ └── rust_cxx_bridge.bzl ├── buck/ │ ├── rust_cxx_bridge.bzl │ └── toolchains/ │ └── BUCK └── cargo/ └── build.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bazelignore ================================================ buck-out/ target/ tools/buck/buck2/ ================================================ FILE: .bazelrc ================================================ ############################################################################### ## Bazel Configuration Flags ## ## `.bazelrc` is a Bazel configuration file. ## https://bazel.build/docs/best-practices#bazelrc-file ############################################################################### build --enable_platform_specific_config build:linux --@rules_rust//:extra_rustc_flags=-Clink-arg=-fuse-ld=lld build:linux --cxxopt=-std=c++17 build:macos --cxxopt=-std=c++17 ############################################################################### ## Custom user flags ## ## This should always be the last thing in the `.bazelrc` file to ensure ## consistent behavior when setting flags in that file as `.bazelrc` files are ## evaluated top to bottom. ############################################################################### try-import %workspace%/user.bazelrc ================================================ FILE: .bcr/README.md ================================================ # Bazel Central Registry When the ruleset is released, we want it to be published to the Bazel Central Registry automatically: This folder contains configuration files to automate the publish step. See for authoritative documentation about these files. ================================================ FILE: .bcr/config.yml ================================================ fixedReleaser: login: dtolnay email: dtolnay@gmail.com ================================================ FILE: .bcr/metadata.template.json ================================================ { "homepage": "https://cxx.rs", "maintainers": [ { "github": "dtolnay", "github_user_id": 1940490, "email": "dtolnay@gmail.com", "name": "David Tolnay" } ], "repository": [ "github:dtolnay/cxx" ], "versions": [], "yanked_versions": {} } ================================================ FILE: .bcr/presubmit.yml ================================================ matrix: platform: - macos_arm64 - ubuntu2404 - windows bazel: [8.x, 9.x] tasks: verify_targets: name: Verify build targets platform: ${{ platform }} bazel: ${{ bazel }} build_targets: - '@cxx.rs//...' test_targets: - '@cxx.rs//...' ================================================ FILE: .bcr/source.template.json ================================================ { "integrity": "", "strip_prefix": "{REPO}-{VERSION}", "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{VERSION}.tar.gz" } ================================================ FILE: .buckconfig ================================================ [cells] root = . prelude = tools/buck/prelude toolchains = tools/buck/toolchains none = none [external_cells] prelude = bundled [cell_aliases] config = prelude fbcode = none fbsource = none [project] # Hide BUCK files under target/package/ from `buck build ...`. Otherwise: # $ buck build ... # //target/package/cxx-0.3.0/tests:ffi references non-existing file or directory 'target/package/cxx-0.3.0/tests/ffi/lib.rs' # # Also hide some Bazel-managed directories that contain symlinks to the repo root. ignore = \ .git, \ bazel-bin, \ bazel-cxx, \ bazel-out, \ bazel-testlogs, \ target [parser] target_platform_detector_spec = target:root//...->prelude//platforms:default ================================================ FILE: .buckroot ================================================ ================================================ FILE: .clang-format ================================================ AlwaysBreakTemplateDeclarations: true MaxEmptyLinesToKeep: 3 ReflowComments: false ================================================ FILE: .clang-tidy ================================================ Checks: clang-analyzer-*, clang-diagnostic-*, cppcoreguidelines-*, modernize-*, -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-type-const-cast, -cppcoreguidelines-pro-type-member-init, -cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-special-member-functions, -modernize-concat-nested-namespaces, -modernize-return-braced-init-list, -modernize-type-traits, -modernize-use-constraints, -modernize-use-nodiscard, -modernize-use-ranges, -modernize-use-trailing-return-type, HeaderFilterRegex: cxx\.h ================================================ FILE: .devcontainer/Dockerfile ================================================ FROM dtolnay/devcontainer:latest ================================================ FILE: .devcontainer/README.md ================================================ This directory contains the container setup used when developing CXX inside of GitHub [Codespaces]. [Codespaces]: https://github.com/features/codespaces ================================================ FILE: .devcontainer/build.Dockerfile ================================================ FROM mcr.microsoft.com/devcontainers/rust:bookworm RUN apt-get update \ && export DEBIAN_FRONTEND=noninteractive \ && apt-get -y install --no-install-recommends clang lld zstd \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && wget -q -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 \ && wget -q -O /tmp/buck.zst https://github.com/facebook/buck2/releases/download/latest/buck2-x86_64-unknown-linux-gnu.zst \ && wget -q -O /usr/local/bin/buildifier https://github.com/bazelbuild/buildtools/releases/latest/download/buildifier-linux-amd64 \ && unzstd /tmp/buck.zst -o /usr/local/bin/buck \ && chmod +x /usr/local/bin/bazel /usr/local/bin/buck /usr/local/bin/buildifier \ && rm /tmp/buck.zst \ && rustup component add rust-analyzer rust-src ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "name": "Rust", "build": { "dockerfile": "Dockerfile" }, "runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], "settings": { "terminal.integrated.shell.linux": "/bin/bash", "lldb.executable": "/usr/bin/lldb", "files.watcherExclude": { "**/target/**": true } }, "extensions": [ "BazelBuild.vscode-bazel", "ms-vscode.cpptools", "rust-lang.rust-analyzer", "vadimcn.vscode-lldb" ] } ================================================ FILE: .gitattributes ================================================ /MODULE.bazel.lock linguist-generated /third-party/BUCK linguist-generated /third-party/bazel/** linguist-generated ================================================ FILE: .github/FUNDING.yml ================================================ github: dtolnay ================================================ FILE: .github/workflows/buck2.yml ================================================ name: Buck2 on: push: workflow_dispatch: schedule: [cron: "40 1,13 * * *"] permissions: contents: read jobs: buck2: name: Buck2 on ${{matrix.os == 'ubuntu' && 'Linux' || matrix.os == 'macos' && 'macOS' || matrix.os == 'windows' && 'Windows' || '???'}} runs-on: ${{matrix.os}}-latest strategy: fail-fast: false matrix: os: [ubuntu, macos, windows] timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: rust-src - uses: dtolnay/install-buck2@latest - run: buck2 run demo - run: buck2 build ... - run: buck2 test ... ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: pull_request: workflow_dispatch: schedule: [cron: "40 1 * * *"] permissions: contents: read jobs: pre_ci: uses: dtolnay/.github/.github/workflows/pre_ci.yml@master test: name: ${{matrix.name || format('Rust {0}', matrix.rust)}} needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ${{matrix.runs-on || format('{0}-latest', matrix.os)}} strategy: fail-fast: false matrix: rust: [nightly, beta, stable, 1.85.0] os: [ubuntu] cc: [g++] flags: [''] include: - name: Cargo on macOS rust: nightly os: macos - name: Cargo on Windows (msvc) rust: nightly-x86_64-pc-windows-msvc os: windows - name: Clang rust: nightly os: ubuntu cc: clang++ flags: -std=c++20 - name: Clang (no exceptions) rust: nightly os: ubuntu cc: clang++ flags: -std=c++20 -fno-exceptions - name: C++14 on Linux rust: nightly os: ubuntu cc: g++ flags: -std=c++14 - name: C++14 on macOS rust: nightly os: macos flags: -std=c++14 - name: C++14 on Windows rust: nightly-x86_64-pc-windows-msvc os: windows flags: /std:c++14 - name: C++17 on Linux rust: nightly os: ubuntu cc: g++ flags: -std=c++17 - name: C++17 on macOS rust: nightly os: macos flags: -std=c++17 - name: C++17 on Windows rust: nightly-x86_64-pc-windows-msvc os: windows flags: /std:c++17 - name: C++20 on Linux rust: nightly os: ubuntu cc: g++ flags: -std=c++20 - name: C++20 on macOS rust: nightly os: macos flags: -std=c++20 runs-on: macos-15 - name: C++20 on Windows rust: nightly-x86_64-pc-windows-msvc os: windows flags: /std:c++20 - name: Pedantic rust: nightly os: ubuntu cc: clang++ flags: -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-c++20-compat -Wno-implicit-int-conversion -Wno-missing-prototypes -Wno-padded -Wno-sign-conversion -Wno-undefined-func-template -Wno-unsafe-buffer-usage -Wno-unused-macros env: CXX: ${{matrix.cc}} CXXFLAGS: ${{matrix.flags}} ${{matrix.os == 'windows' && '/EHsc /WX' || '-Werror -Wall -Wpedantic'}} RUSTFLAGS: --cfg deny_warnings -Dwarnings timeout-minutes: 45 steps: - name: Enable symlinks (windows) if: matrix.os == 'windows' run: git config --global core.symlinks true - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} components: rust-src - name: Determine test suite subset # Our Windows and macOS jobs are the longest running, so exclude the # relatively slow compiletest from them to speed up end-to-end CI time, # except during cron builds when no human is presumably waiting on the # build. The extra coverage is not particularly valuable and we can # still ensure the test is kept passing on the basis of the scheduled # builds. run: | echo RUSTFLAGS=$RUSTFLAGS >> $GITHUB_ENV echo exclude=--exclude cxx-test-suite >> $GITHUB_OUTPUT env: RUSTFLAGS: ${{env.RUSTFLAGS}} ${{matrix.os != 'ubuntu' && github.event_name != 'schedule' && '--cfg skip_ui_tests' || ''}} id: testsuite shell: bash - name: Ignore macOS linker warning run: echo RUSTFLAGS=${RUSTFLAGS}\ -Alinker_messages >> $GITHUB_ENV if: matrix.os == 'macos' - run: cargo run --manifest-path demo/Cargo.toml - run: cargo test --workspace ${{steps.testsuite.outputs.exclude}} if: contains(matrix.flags, '-fno-exceptions') == false - run: cargo check --no-default-features --features alloc env: RUSTFLAGS: --cfg compile_error_if_std ${{env.RUSTFLAGS}} - run: cargo check --no-default-features env: RUSTFLAGS: --cfg compile_error_if_alloc --cfg cxx_experimental_no_alloc ${{env.RUSTFLAGS}} - uses: actions/upload-artifact@v6 if: matrix.os == 'ubuntu' && matrix.rust == 'nightly' && matrix.cc == '' && matrix.flags == '' && always() with: name: Cargo.lock path: Cargo.lock continue-on-error: true wasi: name: WebAssembly runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly with: targets: wasm32-wasip1 components: rust-src - uses: dtolnay/install@wasmtime-cli - run: curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz --location --silent --show-error --fail --retry 2 --output ${{runner.temp}}/wasi-sdk-27.0-x86_64-linux.tar.gz - run: tar xf ${{runner.temp}}/wasi-sdk-27.0-x86_64-linux.tar.gz -C ${{runner.temp}} - run: cargo build --target=wasm32-wasip1 --manifest-path=demo/Cargo.toml --release --config='target.wasm32-wasip1.linker="${{runner.temp}}/wasi-sdk-27.0-x86_64-linux/bin/lld"' --config='target.wasm32-wasip1.rustflags=["-Clink-args=-L${{runner.temp}}/wasi-sdk-27.0-x86_64-linux/share/wasi-sysroot/lib/wasm32-wasip1", "-Clink-args=-lc++abi"]' env: CXX: ${{runner.temp}}/wasi-sdk-27.0-x86_64-linux/bin/clang++ CXXFLAGS: --sysroot=${{runner.temp}}/wasi-sdk-27.0-x86_64-linux/share/wasi-sysroot - run: wasmtime target/wasm32-wasip1/release/demo.wasm emscripten: name: Emscripten needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly with: targets: wasm32-unknown-emscripten components: rust-src - name: Disable initramfs update run: sudo sed -i 's/^update_initramfs=yes$/update_initramfs=no/' /etc/initramfs-tools/update-initramfs.conf - name: Disable man-db update run: sudo rm -f /var/lib/man-db/auto-update - name: Install emscripten run: sudo apt-get install emscripten - run: cargo build --target=wasm32-unknown-emscripten --manifest-path=demo/Cargo.toml --release -Zbuild-std env: RUSTFLAGS: -Clink-arg=--emrun ${{env.RUSTFLAGS}} - name: Create demo.html for demo.js run: echo '' > target/wasm32-unknown-emscripten/release/demo.html - name: Install firefox run: sudo snap install firefox - run: emrun target/wasm32-unknown-emscripten/release/demo.html --browser=/snap/firefox/current/usr/lib/firefox/firefox --browser_args=-headless --safe_firefox_profile --log_stdout=${{runner.temp}}/demo.log --timeout=60 --kill_exit - run: cat ${{runner.temp}}/demo.log - run: grep --silent blobid ${{runner.temp}}/demo.log reindeer: name: Reindeer runs-on: ubuntu-latest if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: rust-src - uses: dtolnay/install@reindeer - run: reindeer buckify working-directory: third-party - name: Check reindeer-generated BUCK file up to date run: git diff --exit-code bazel: name: Bazel on ${{matrix.os == 'ubuntu' && 'Linux' || matrix.os == 'macos' && 'macOS' || matrix.os == 'windows' && 'Windows' || '???'}} runs-on: ${{matrix.os}}-latest if: github.event_name != 'pull_request' strategy: fail-fast: false matrix: os: [ubuntu, macos, windows] timeout-minutes: 45 steps: - uses: actions/checkout@v6 - name: Disable initramfs update run: sudo sed -i 's/^update_initramfs=yes$/update_initramfs=no/' /etc/initramfs-tools/update-initramfs.conf if: matrix.os == 'ubuntu' - name: Disable man-db update run: sudo rm -f /var/lib/man-db/auto-update if: matrix.os == 'ubuntu' - name: Install lld run: sudo apt-get install lld if: matrix.os == 'ubuntu' - name: Set bazelrc for Windows run: echo "startup --output_user_root=D:/bzl" > user.bazelrc if: matrix.os == 'windows' - run: bazel --version - run: bazel run demo --verbose_failures --noshow_progress ${{matrix.os == 'macos' && '--xcode_version_config=tools/bazel:github_actions_xcodes' || ''}} - run: bazel test ... --verbose_failures --noshow_progress ${{matrix.os == 'macos' && '--xcode_version_config=tools/bazel:github_actions_xcodes' || ''}} - name: Check MODULE.bazel.lock up to date run: git diff --exit-code - run: bazel run //third-party:vendor if: matrix.os == 'ubuntu' || matrix.os == 'macos' - name: Check third-party/bazel up to date run: git diff --exit-code if: matrix.os == 'ubuntu' || matrix.os == 'macos' minimal: name: Minimal versions needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly - run: cargo generate-lockfile -Z minimal-versions - run: cargo check --locked --workspace doc: name: Documentation needs: pre_ci if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 env: RUSTDOCFLAGS: -Dwarnings steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly with: components: rust-src - uses: dtolnay/install@cargo-docs-rs - run: cargo docs-rs - run: cargo docs-rs -p cxx-build - run: cargo docs-rs -p cxx-gen - run: cargo docs-rs -p cxxbridge-flags - run: cargo docs-rs -p cxxbridge-macro clippy: name: Clippy runs-on: ubuntu-latest if: github.event_name != 'pull_request' timeout-minutes: 45 env: RUSTFLAGS: -Dwarnings steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly with: components: clippy, rust-src - run: cargo clippy --workspace --tests --exclude demo -- -Dclippy::all -Dclippy::pedantic - run: cargo clippy --manifest-path demo/Cargo.toml -- -Dclippy::all clang-tidy: name: Clang Tidy runs-on: ubuntu-latest if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - uses: actions/checkout@v6 - name: Disable initramfs update run: sudo sed -i 's/^update_initramfs=yes$/update_initramfs=no/' /etc/initramfs-tools/update-initramfs.conf - name: Disable man-db update run: sudo rm -f /var/lib/man-db/auto-update - name: Install clang-tidy run: sudo apt-get update && sudo apt-get install clang-tidy-20 - name: Run clang-tidy run: clang-tidy-20 src/cxx.cc --warnings-as-errors=* eslint: name: ESLint runs-on: ubuntu-latest if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - uses: actions/checkout@v6 - run: npm install working-directory: book - run: npx eslint working-directory: book outdated: name: Outdated runs-on: ubuntu-latest if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: dtolnay/install@cargo-outdated - run: cargo outdated --workspace --exit-code 1 ================================================ FILE: .github/workflows/install.yml ================================================ name: Install on: workflow_dispatch: schedule: [cron: "40 1 * * *"] push: {tags: ['*']} permissions: {} env: RUSTFLAGS: -Dwarnings jobs: install: name: Install uses: dtolnay/.github/.github/workflows/check_install.yml@master with: crate: cxxbridge-cmd ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: release: types: [released] permissions: attestations: write contents: write id-token: write jobs: upload: uses: dtolnay/.github/.github/workflows/release_tgz.yml@master publish-to-bcr: needs: upload uses: bazel-contrib/publish-to-bcr/.github/workflows/publish.yaml@92ae43f10e552721931f98b61fe5506bbdf32ce6 with: tag_name: ${{github.event.release.tag_name}} registry_fork: dtolnay-contrib/bazel-central-registry attest: false secrets: publish_token: ${{secrets.PUBLISH_TOKEN}} ================================================ FILE: .github/workflows/site.yml ================================================ name: Deploy on: push: branches: - master paths: - book/** - .github/workflows/site.yml workflow_dispatch: jobs: deploy: name: Deploy runs-on: ubuntu-latest permissions: contents: write timeout-minutes: 30 steps: - uses: actions/checkout@v6 - uses: dtolnay/install@mdbook - run: mdbook --version - name: Build run: book/build.sh - name: Push to gh-pages working-directory: book/build run: | REV=$(git rev-parse --short HEAD) git init git remote add upstream https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/dtolnay/cxx git config user.name "CXX" git config user.email "dtolnay+cxx@gmail.com" git add -A . git commit -qm "Website @ ${{github.repository}}@${REV}" git push -q upstream HEAD:refs/heads/gh-pages --force ================================================ FILE: .gitignore ================================================ /.buckconfig.d/ /.buckconfig.local /.buckd /bazel-bin /bazel-cxx /bazel-out /bazel-testlogs /user.bazelrc /buck-out /expand.cc /expand.rs /target/ /Cargo.lock ================================================ FILE: .vscode/README.md ================================================ VS Code actions and configuration. Applicable when developing CXX inside of GitHub [Codespaces]. [Codespaces]: https://github.com/features/codespaces ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "name": "Run cxx demo", "type": "lldb", "request": "launch", "cargo": { "args": ["build", "--manifest-path", "demo/Cargo.toml"], "filter": { "name": "demo", "kind": "bin" } } }, { "name": "Debug cargo tests", "type": "lldb", "request": "launch", "cargo": { "args": ["test", "--no-run"], "filter": { "name": "test", "kind": "test" } } } ] } ================================================ FILE: .vscode/settings.json ================================================ { "search.exclude": { "**/target": true } } ================================================ FILE: .vscode/tasks.json ================================================ { "version": "2.0.0", "tasks": [ { "label": "Cargo test", "type": "shell", "command": "cargo test", "group": "test" }, { "label": "Bazel test", "type": "shell", "command": "bazel test ...", "group": "test", "dependsOn": ["Vendor"] }, { "label": "Buck test", "type": "shell", "command": "buck test ...", "group": "test", "dependsOn": ["Vendor"] }, { "label": "Vendor", "type": "shell", "command": "cp third-party/Cargo.lock . && cargo vendor --versioned-dirs --locked third-party/vendor" } ] } ================================================ FILE: .watchmanconfig ================================================ { "ignore_dirs": ["buck-out"] } ================================================ FILE: BUCK ================================================ load(":Cargo.toml", cargo_toml = "value") CARGO_PKG_VERSION_PATCH = cargo_toml["package"]["version"].split(".")[2] rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), doc_deps = [ ":cxx-build", ], edition = "2021", features = [ "alloc", "std", ], visibility = ["PUBLIC"], deps = [ ":core", ":cxxbridge-macro", "//third-party:foldhash", ], ) alias( name = "codegen", actual = ":cxxbridge", visibility = ["PUBLIC"], ) rust_binary( name = "cxxbridge", srcs = glob([ "gen/cmd/src/**/*.rs", "gen/src/builtin/*.h", ]) + [ "gen/cmd/src/gen", "gen/cmd/src/syntax", ], edition = "2021", env = { "CARGO_PKG_VERSION_PATCH": CARGO_PKG_VERSION_PATCH, }, deps = [ "//third-party:clap", "//third-party:codespan-reporting", "//third-party:indexmap", "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", ], ) cxx_library( name = "core", srcs = ["src/cxx.cc"], exported_headers = { "cxx.h": "include/cxx.h", }, header_namespace = "rust", preferred_linkage = "static", visibility = ["PUBLIC"], ) rust_library( name = "cxxbridge-macro", srcs = glob(["macro/src/**/*.rs"]) + ["macro/src/syntax"], doctests = False, edition = "2021", env = { "CARGO_PKG_VERSION_PATCH": CARGO_PKG_VERSION_PATCH, }, proc_macro = True, deps = [ "//third-party:indexmap", "//third-party:proc-macro2", "//third-party:quote", "//third-party:rustversion", "//third-party:syn", ], ) rust_library( name = "cxx-build", srcs = glob([ "gen/build/src/**/*.rs", "gen/src/builtin/*.h", ]) + [ "gen/build/src/gen", "gen/build/src/syntax", ], doctests = False, edition = "2021", env = { "CARGO_PKG_VERSION_PATCH": CARGO_PKG_VERSION_PATCH, }, deps = [ "//third-party:cc", "//third-party:codespan-reporting", "//third-party:indexmap", "//third-party:proc-macro2", "//third-party:quote", "//third-party:scratch", "//third-party:syn", ], ) rust_library( name = "cxx-gen", srcs = glob([ "gen/lib/src/**/*.rs", "gen/src/builtin/*.h", ]) + [ "gen/lib/src/gen", "gen/lib/src/syntax", ], edition = "2021", env = { "CARGO_PKG_VERSION_PATCH": CARGO_PKG_VERSION_PATCH, }, visibility = ["PUBLIC"], deps = [ "//third-party:cc", "//third-party:codespan-reporting", "//third-party:indexmap", "//third-party:proc-macro2", "//third-party:quote", "//third-party:syn", ], ) ================================================ FILE: BUILD.bazel ================================================ load("@rules_cc//cc:defs.bzl", "cc_library") load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro") rust_library( name = "cxx", srcs = glob(["src/**/*.rs"]), crate_features = [ "alloc", "std", ], edition = "2021", proc_macro_deps = [ ":cxxbridge-macro", ], version = module_version(), visibility = ["//visibility:public"], deps = [ ":core-lib", "@crates.io//:foldhash", ], ) alias( name = "codegen", actual = ":cxxbridge", visibility = ["//visibility:public"], ) rust_binary( name = "cxxbridge", srcs = glob(["gen/cmd/src/**/*.rs"]), compile_data = glob(["gen/cmd/src/gen/**/*.h"]), edition = "2021", version = module_version(), deps = [ "@crates.io//:clap", "@crates.io//:codespan-reporting", "@crates.io//:indexmap", "@crates.io//:proc-macro2", "@crates.io//:quote", "@crates.io//:syn", ], ) cc_library( name = "core", hdrs = ["include/cxx.h"], include_prefix = "rust", strip_include_prefix = "include", visibility = ["//visibility:public"], ) cc_library( name = "core-lib", srcs = ["src/cxx.cc"], hdrs = ["include/cxx.h"], linkstatic = True, ) rust_proc_macro( name = "cxxbridge-macro", srcs = glob(["macro/src/**/*.rs"]), edition = "2021", proc_macro_deps = [ "@crates.io//:rustversion", ], version = module_version(), deps = [ "@crates.io//:indexmap", "@crates.io//:proc-macro2", "@crates.io//:quote", "@crates.io//:syn", ], ) rust_library( name = "cxx-build", srcs = glob(["gen/build/src/**/*.rs"]), compile_data = glob(["gen/build/src/gen/**/*.h"]), edition = "2021", version = module_version(), deps = [ "@crates.io//:cc", "@crates.io//:codespan-reporting", "@crates.io//:indexmap", "@crates.io//:proc-macro2", "@crates.io//:quote", "@crates.io//:scratch", "@crates.io//:syn", ], ) rust_library( name = "cxx-gen", srcs = glob(["gen/lib/src/**/*.rs"]), compile_data = glob(["gen/lib/src/gen/**/*.h"]), edition = "2021", version = module_version(), visibility = ["//visibility:public"], deps = [ "@crates.io//:cc", "@crates.io//:codespan-reporting", "@crates.io//:indexmap", "@crates.io//:proc-macro2", "@crates.io//:quote", "@crates.io//:syn", ], ) ================================================ FILE: Cargo.toml ================================================ [package] name = "cxx" version = "1.0.194" authors = ["David Tolnay "] categories = ["development-tools::ffi", "api-bindings", "no-std"] description = "Safe interop between Rust and C++" documentation = "https://docs.rs/cxx" edition = "2021" exclude = ["/demo", "/gen", "/syntax", "/third-party", "/tools/buck/prelude"] homepage = "https://cxx.rs" keywords = ["ffi", "c++"] license = "MIT OR Apache-2.0" links = "cxxbridge1" repository = "https://github.com/dtolnay/cxx" rust-version = "1.85" [features] default = ["std", "cxxbridge-flags/default"] # c++11 "c++14" = ["cxxbridge-flags/c++14"] "c++17" = ["cxxbridge-flags/c++17"] "c++20" = ["cxxbridge-flags/c++20"] alloc = [] std = ["alloc", "foldhash/std"] [dependencies] cxxbridge-macro = { version = "=1.0.194", path = "macro" } foldhash = { version = "0.2", default-features = false } link-cplusplus = "1.0.11" [build-dependencies] cc = "1.0.101" cxxbridge-flags = { version = "=1.0.194", path = "flags", default-features = false } [dev-dependencies] cc = "1.0.101" cxx-build = { version = "1", path = "gen/build" } cxx-gen = { version = "=0.7.194", path = "gen/lib" } cxx-test-suite = { version = "0", path = "tests/ffi" } indoc = "2" proc-macro2 = "1.0.95" quote = "1.0.40" rustversion = "1.0.13" scratch = "1" target-triple = "1" tempfile = "3.8" trybuild = { version = "1.0.108", features = ["diff"] } # Disallow incompatible version appearing in the same lockfile. [target.'cfg(any())'.build-dependencies] cxx-build = { version = "=1.0.194", path = "gen/build" } cxxbridge-cmd = { version = "=1.0.194", path = "gen/cmd" } [workspace] members = ["demo", "flags", "gen/build", "gen/cmd", "gen/lib", "macro", "tests/ffi"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--generate-macro-expansion", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", ] [package.metadata.bazel] additive_build_file_content = """ load("@rules_cc//cc:defs.bzl", "cc_library") cc_library( name = "cxx_cc", srcs = ["src/cxx.cc"], hdrs = ["include/cxx.h"], include_prefix = "rust", includes = ["include"], linkstatic = True, strip_include_prefix = "include", visibility = ["//visibility:public"], ) """ deps = [":cxx_cc"] extra_aliased_targets = { cxx_cc = "cxx_cc" } gen_build_script = false [patch.crates-io] cxx = { path = "." } cxx-build = { path = "gen/build" } ================================================ FILE: LICENSE-APACHE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ================================================ FILE: LICENSE-MIT ================================================ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MODULE.bazel ================================================ module( name = "cxx.rs", version = "0.0.0", bazel_compatibility = [">=8.0.0"], compatibility_level = 1, ) bazel_dep(name = "apple_support", version = "2.1.0") bazel_dep(name = "bazel_features", version = "1.33.0") bazel_dep(name = "bazel_skylib", version = "1.8.2") bazel_dep(name = "platforms", version = "1.0.0") bazel_dep(name = "rules_cc", version = "0.2.14") bazel_dep(name = "rules_rust", version = "0.69.0") rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") rust.toolchain(versions = ["1.94.0"]) use_repo(rust, "rust_toolchains") register_toolchains("@rust_toolchains//:all") crate_repositories = use_extension("//tools/bazel:extension.bzl", "crate_repositories") use_repo(crate_repositories, "crates.io", "vendor") ================================================ FILE: README.md ================================================ CXX — safe FFI between Rust and C++ ========================================= [github](https://github.com/dtolnay/cxx) [crates.io](https://crates.io/crates/cxx) [docs.rs](https://docs.rs/cxx) [build status](https://github.com/dtolnay/cxx/actions?query=branch%3Amaster) This library provides a **safe** mechanism for calling C++ code from Rust and Rust code from C++, not subject to the many ways that things can go wrong when using bindgen or cbindgen to generate unsafe C-style bindings. This doesn't change the fact that 100% of C++ code is unsafe. When auditing a project, you would be on the hook for auditing all the unsafe Rust code and *all* the C++ code. The core safety claim under this new model is that auditing just the C++ side would be sufficient to catch all problems, i.e. the Rust side can be 100% safe. ```toml [dependencies] cxx = "1.0" [build-dependencies] cxx-build = "1.0" ``` *Compiler support: requires rustc 1.85+ and c++11 or newer*
*[Release notes](https://github.com/dtolnay/cxx/releases)*
## Guide Please see **** for a tutorial, reference material, and example code.
## Overview The idea is that we define the signatures of both sides of our FFI boundary embedded together in one Rust module (the next section shows an example). From this, CXX receives a complete picture of the boundary to perform static analyses against the types and function signatures to uphold both Rust's and C++'s invariants and requirements. If everything checks out statically, then CXX uses a pair of code generators to emit the relevant `extern "C"` signatures on both sides together with any necessary static assertions for later in the build process to verify correctness. On the Rust side this code generator is simply an attribute procedural macro. On the C++ side it can be a small Cargo build script if your build is managed by Cargo, or for other build systems like Bazel or Buck we provide a command line tool which generates the header and source file and should be easy to integrate. The resulting FFI bridge operates at zero or negligible overhead, i.e. no copying, no serialization, no memory allocation, no runtime checks needed. The FFI signatures are able to use native types from whichever side they please, such as Rust's `String` or C++'s `std::string`, Rust's `Box` or C++'s `std::unique_ptr`, Rust's `Vec` or C++'s `std::vector`, etc in any combination. CXX guarantees an ABI-compatible signature that both sides understand, based on builtin bindings for key standard library types to expose an idiomatic API on those types to the other language. For example when manipulating a C++ string from Rust, its `len()` method becomes a call of the `size()` member function defined by C++; when manipulating a Rust string from C++, its `size()` member function calls Rust's `len()`.
## Example In this example we are writing a Rust application that wishes to take advantage of an existing C++ client for a large-file blobstore service. The blobstore supports a `put` operation for a discontiguous buffer upload. For example we might be uploading snapshots of a circular buffer which would tend to consist of 2 chunks, or fragments of a file spread across memory for some other reason. A runnable version of this example is provided under the *demo* directory of this repo. To try it out, run `cargo run` from that directory. ```rust #[cxx::bridge] mod ffi { // Any shared structs, whose fields will be visible to both languages. struct BlobMetadata { size: usize, tags: Vec, } extern "Rust" { // Zero or more opaque types which both languages can pass around but // only Rust can see the fields. type MultiBuf; // Functions implemented in Rust. fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } unsafe extern "C++" { // One or more headers with the matching C++ declarations. Our code // generators don't read it but it gets #include'd and used in static // assertions to ensure our picture of the FFI boundary is accurate. include!("demo/include/blobstore.h"); // Zero or more opaque types which both languages can pass around but // only C++ can see the fields. type BlobstoreClient; // Functions implemented in C++. fn new_blobstore_client() -> UniquePtr; fn put(&self, parts: &mut MultiBuf) -> u64; fn tag(&self, blobid: u64, tag: &str); fn metadata(&self, blobid: u64) -> BlobMetadata; } } ``` Now we simply provide Rust definitions of all the things in the `extern "Rust"` block and C++ definitions of all the things in the `extern "C++"` block, and get to call back and forth safely. Here are links to the complete set of source files involved in the demo: - [demo/src/main.rs](demo/src/main.rs) - [demo/build.rs](demo/build.rs) - [demo/include/blobstore.h](demo/include/blobstore.h) - [demo/src/blobstore.cc](demo/src/blobstore.cc) To look at the code generated in both languages for the example by the CXX code generators: ```console # run Rust code generator and print to stdout # (requires https://github.com/dtolnay/cargo-expand) $ cargo expand --manifest-path demo/Cargo.toml # run C++ code generator and print to stdout $ cargo run --manifest-path gen/cmd/Cargo.toml -- demo/src/main.rs ```
## Details As seen in the example, the language of the FFI boundary involves 3 kinds of items: - **Shared structs** — their fields are made visible to both languages. The definition written within cxx::bridge is the single source of truth. - **Opaque types** — their fields are secret from the other language. These cannot be passed across the FFI by value but only behind an indirection, such as a reference `&`, a Rust `Box`, or a `UniquePtr`. Can be a type alias for an arbitrarily complicated generic language-specific type depending on your use case. - **Functions** — implemented in either language, callable from the other language. Within the `extern "Rust"` part of the CXX bridge we list the types and functions for which Rust is the source of truth. These all implicitly refer to the `super` module, the parent module of the CXX bridge. You can think of the two items listed in the example above as being like `use super::MultiBuf` and `use super::next_chunk` except re-exported to C++. The parent module will either contain the definitions directly for simple things, or contain the relevant `use` statements to bring them into scope from elsewhere. Within the `extern "C++"` part, we list types and functions for which C++ is the source of truth, as well as the header(s) that declare those APIs. In the future it's possible that this section could be generated bindgen-style from the headers but for now we need the signatures written out; static assertions will verify that they are accurate. Your function implementations themselves, whether in C++ or Rust, *do not* need to be defined as `extern "C"` ABI or no\_mangle. CXX will put in the right shims where necessary to make it all work.
## Comparison vs bindgen and cbindgen Notice that with CXX there is repetition of all the function signatures: they are typed out once where the implementation is defined (in C++ or Rust) and again inside the cxx::bridge module, though compile-time assertions guarantee these are kept in sync. This is different from [bindgen] and [cbindgen] where function signatures are typed by a human once and the tool consumes them in one language and emits them in the other language. [bindgen]: https://github.com/rust-lang/rust-bindgen [cbindgen]: https://github.com/eqrion/cbindgen/ This is because CXX fills a somewhat different role. It is a lower level tool than bindgen or cbindgen in a sense; you can think of it as being a replacement for the concept of `extern "C"` signatures as we know them, rather than a replacement for a bindgen. It would be reasonable to build a higher level bindgen-like tool on top of CXX which consumes a C++ header and/or Rust module (and/or IDL like Thrift) as source of truth and generates the cxx::bridge, eliminating the repetition while leveraging the static analysis safety guarantees of CXX. But note in other ways CXX is higher level than the bindgens, with rich support for common standard library types. Frequently with bindgen when we are dealing with an idiomatic C++ API we would end up manually wrapping that API in C-style raw pointer functions, applying bindgen to get unsafe raw pointer Rust functions, and replicating the API again to expose those idiomatically in Rust. That's a much worse form of repetition because it is unsafe all the way through. By using a CXX bridge as the shared understanding between the languages, rather than `extern "C"` C-style signatures as the shared understanding, common FFI use cases become expressible using 100% safe code. It would also be reasonable to mix and match, using CXX bridge for the 95% of your FFI that is straightforward and doing the remaining few oddball signatures the old fashioned way with bindgen and cbindgen, if for some reason CXX's static restrictions get in the way. Please file an issue if you end up taking this approach so that we know what ways it would be worthwhile to make the tool more expressive.
## Cargo-based setup For builds that are orchestrated by Cargo, you will use a build script that runs CXX's C++ code generator and compiles the resulting C++ code along with any other C++ code for your crate. The canonical build script is as follows. The indicated line returns a [`cc::Build`] instance (from the usual widely used `cc` crate) on which you can set up any additional source files and compiler flags as normal. [`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html ```toml # Cargo.toml [build-dependencies] cxx-build = "1.0" ``` ```rust // build.rs fn main() { cxx_build::bridge("src/main.rs") // returns a cc::Build .file("src/demo.cc") .std("c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/demo.cc"); println!("cargo:rerun-if-changed=include/demo.h"); } ```
## Non-Cargo setup For use in non-Cargo builds like Bazel or Buck, CXX provides an alternate way of invoking the C++ code generator as a standalone command line tool. The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be built from the *gen/cmd* directory of this repo. ```bash $ cargo install cxxbridge-cmd $ cxxbridge src/main.rs --header > path/to/mybridge.h $ cxxbridge src/main.rs > path/to/mybridge.cc ```
## Safety Be aware that the design of this library is intentionally restrictive and opinionated! It isn't a goal to be powerful enough to handle arbitrary signatures in either language. Instead this project is about carving out a reasonably expressive set of functionality about which we can make useful safety guarantees today and maybe extend over time. You may find that it takes some practice to use CXX bridge effectively as it won't work in all the ways that you are used to. Some of the considerations that go into ensuring safety are: - By design, our paired code generators work together to control both sides of the FFI boundary. Ordinarily in Rust writing your own `extern "C"` blocks is unsafe because the Rust compiler has no way to know whether the signatures you've written actually match the signatures implemented in the other language. With CXX we achieve that visibility and know what's on the other side. - Our static analysis detects and prevents passing types by value that shouldn't be passed by value from C++ to Rust, for example because they may contain internal pointers that would be screwed up by Rust's move behavior. - To many people's surprise, it is possible to have a struct in Rust and a struct in C++ with exactly the same layout / fields / alignment / everything, and still not the same ABI when passed by value. This is a longstanding bindgen bug that leads to segfaults in absolutely correct-looking code ([rust-lang/rust-bindgen#778]). CXX knows about this and can insert the necessary zero-cost workaround transparently where needed, so go ahead and pass your structs by value without worries. This is made possible by owning both sides of the boundary rather than just one. - Template instantiations: for example in order to expose a UniquePtr\ type in Rust backed by a real C++ unique\_ptr, we have a way of using a Rust trait to connect the behavior back to the template instantiations performed by the other language. [rust-lang/rust-bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778
## Builtin types In addition to all the primitive types (i32 <=> int32_t), the following common types may be used in the fields of shared structs and the arguments and returns of functions.
name in Rustname in C++restrictions
Stringrust::String
&strrust::Str
&[T]rust::Slice<const T>cannot hold opaque C++ type
&mut [T]rust::Slice<T>cannot hold opaque C++ type
CxxStringstd::stringcannot be passed by value
Box<T>rust::Box<T>cannot hold opaque C++ type
UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
SharedPtr<T>std::shared_ptr<T>cannot hold opaque Rust type
[T; N]std::array<T, N>cannot hold opaque C++ type
Vec<T>rust::Vec<T>cannot hold opaque C++ type
CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type
*mut T, *const TT*, const T*fn with a raw pointer argument must be declared unsafe to call
fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
Result<T>throw/catchallowed as return type only
The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in this repo. You will need to include this header in your C++ code when working with those types. The following types are intended to be supported "soon" but are just not implemented yet. I don't expect any of these to be hard to make work but it's a matter of designing a nice API for each in its non-native language.
name in Rustname in C++
BTreeMap<K, V>tbd
HashMap<K, V>tbd
Arc<T>tbd
Option<T>tbd
tbdstd::map<K, V>
tbdstd::unordered_map<K, V>

## Remaining work This is still early days for CXX; I am releasing it as a minimum viable product to collect feedback on the direction and invite collaborators. Please check the open issues. Especially please report issues if you run into trouble building or linking any of this stuff. I'm sure there are ways to make the build aspects friendlier or more robust. Finally, I know more about Rust library design than C++ library design so I would appreciate help making the C++ APIs in this project more idiomatic where anyone has suggestions.
#### License Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. ================================================ FILE: book/.gitignore ================================================ /build/ /mdbook /node_modules/ ================================================ FILE: book/README.md ================================================ Published automatically to https://cxx.rs from master branch. To build and view locally: - Install [mdBook]: `cargo install mdbook`. - Run `mdbook build` in this directory. - Open the generated *build/index.html*. [mdBook]: https://github.com/rust-lang/mdBook ================================================ FILE: book/book.toml ================================================ [book] #title = "Rust ♡ C++" authors = ["David Tolnay"] description = "CXX — safe interop between Rust and C++ by David Tolnay. This library provides a safe mechanism for calling C++ code from Rust and Rust code from C++." [rust] edition = "2021" [build] build-dir = "build" create-missing = false [output.html] additional-css = ["css/cxx.css"] cname = "cxx.rs" git-repository-url = "https://github.com/dtolnay/cxx" playground = { copyable = false } print = { enable = false } [output.html.redirect] "binding/index.html" = "../bindings.html" "build/index.html" = "../building.html" ================================================ FILE: book/build.js ================================================ #!/usr/bin/env node const fs = require('fs'); const cheerio = require('cheerio'); const entities = require('html-entities'); const hljs = require('./build/highlight.js'); const githublink = `\
  • \ \ \ https://github.com/dtolnay/cxx\ \
  • `; const opengraph = `\ \ \ \ \ \ \ `; const themejs = `\ var theme; try { theme = localStorage.getItem('mdbook-theme'); } catch(e) {} if (theme === null || theme === undefined) { theme = default_theme; } const html = document.documentElement; html.classList.remove('light') html.classList.add(theme); html.classList.add("js");`; const themejsReplacement = `\ const html = document.documentElement; html.classList.add('js');`; const dirs = ['build']; while (dirs.length) { const dir = dirs.pop(); fs.readdirSync(dir).forEach((entry) => { const path = dir + '/' + entry; const stat = fs.statSync(path); if (stat.isDirectory()) { dirs.push(path); return; } if (!path.endsWith('.html')) { return; } const index = fs.readFileSync(path, 'utf8'); const $ = cheerio.load(index, { decodeEntities: false, xml: { xmlMode: false }, }); $('head').append(opengraph); $('nav#sidebar ol.chapter').append(githublink); $('head link[href="tomorrow-night.css"]').attr('disabled', true); $('head link[href="ayu-highlight.css"]').attr('disabled', true); $('button#theme-toggle').attr('style', 'display:none'); $('pre code').each(function () { const node = $(this); const langClass = node.attr('class').split(' ', 2)[0]; if (!langClass.startsWith('language-')) { return; } const lang = langClass.replace('language-', ''); const originalLines = node.html().split('\n'); const boring = originalLines.map((line) => line.includes(''), ); const ellipsis = originalLines.map((line) => line.includes('// ...')); const target = entities.decode(node.text()); const highlightedLines = hljs.highlight(lang, target).value.split('\n'); const result = highlightedLines .map(function (line, i) { if (boring[i]) { line = '' + line; } else if (ellipsis[i]) { line = '' + line; } if (i > 0 && (boring[i - 1] || ellipsis[i - 1])) { line = '' + line; } if (i + 1 === highlightedLines.length && (boring[i] || ellipsis[i])) { line = line + ''; } return line; }) .join('\n'); node.text(result); node.removeClass(langClass); if (!node.hasClass('focuscomment')) { node.addClass('hidelines'); node.addClass('hide-boring'); } }); $('code').each(function () { $(this).addClass('hljs'); }); var foundScript = false; $('body script').each(function () { const node = $(this); if (node.text().replace(/\s/g, '') === themejs.replace(/\s/g, '')) { node.text(themejsReplacement); foundScript = true; } }); const pathsWithoutScript = [ 'build/toc.html', 'build/build/index.html', 'build/binding/index.html', ]; if (!foundScript && !pathsWithoutScript.includes(path)) { throw new Error(`theme script not found in ${path}`); } const out = $.html(); fs.writeFileSync(path, out); }); } fs.copyFileSync('build/highlight.css', 'build/tomorrow-night.css'); fs.copyFileSync('build/highlight.css', 'build/ayu-highlight.css'); var bookjs = fs.readFileSync('build/book.js', 'utf8'); bookjs = bookjs .replace('set_theme(theme, false);', '') .replace( 'document.querySelectorAll("code.hljs")', 'document.querySelectorAll("code.hidelines")', ); fs.writeFileSync('build/book.js', bookjs); ================================================ FILE: book/build.sh ================================================ #!/bin/bash set -e cd "$(dirname "$0")" if [ -f ./mdbook ]; then ./mdbook build else mdbook build fi if [ ! -d node_modules ]; then npm install fi ./build.js ================================================ FILE: book/css/cxx.css ================================================ :root { --sidebar-width: 310px; } .badges img { margin: 0 7px 7px 0; } .badges { margin: 16px 0 120px; } .boring { opacity: 0.5; } .no-js code:not(.focuscomment) .boring { display: none; } .js code:not(.hide-boring) .ellipsis { display: none; } .focuscomment .hljs-comment { font-weight: bold; color: black; } .focuscomment .boring { opacity: 0.5; } nav.sidebar li.part-title i.fa-github { font-size: 20px; padding-right: 5px; padding-top: 12px; position: relative; top: 1px; } .sidebar .sidebar-scrollbox { padding: 10px 0 10px 10px; } pre > .buttons { visibility: visible; opacity: 0.3; } ================================================ FILE: book/diagram/.gitignore ================================================ /*.aux /*.fdb_latexmk /*.fls /*.log /*.pdf /*.png /*.svg ================================================ FILE: book/diagram/Makefile ================================================ overview.svg: overview.pdf pdf2svg $< $@ overview.pdf: overview.tex latexmk $< overview.png: overview.svg svgexport $< $@ 3x ================================================ FILE: book/diagram/overview.tex ================================================ \documentclass{standalone} \usepackage{makecell} \usepackage{pgfplots} \usepackage{sansmath} \usetikzlibrary{arrows.meta} \pgfplotsset{compat=1.16} \begin{document} \pagecolor{white} \begin{tikzpicture}[ x=1cm, y=-.6cm, every node/.append style={ line width=1.5pt, font=\Large\sansmath\sffamily, }, every path/.append style={ >={Latex[length=10pt,width=8pt]}, line width=1.5pt, }, execute at end node={\vphantom{bg}}, ] \node[draw, rounded corners=5, inner xsep=30pt, inner ysep=2pt] (bridge) at (0, .25) {\makecell{\texttt{\#\hspace{-1pt}[}cxx::bridge\texttt{]} mod\\[-4pt]description of boundary}}; \node[draw, rounded corners, inner xsep=10pt, inner ysep=6pt, text depth=1pt] (rust-bindings) at (-3.5, 6.5) {Rust bindings}; \node[draw, rounded corners, inner xsep=10pt, inner ysep=6pt, text depth=1pt] (cpp-bindings) at (3.5, 6.5) {C\texttt{++} bindings}; \node[inner xsep=4pt, inner ysep=-0pt] (rust-code) at (-9, 6.5) {\makecell[r]{\\[-8pt]Rust\\[-4pt]code}}; \node[inner xsep=4pt, inner ysep=-0pt] (cpp-code) at (9, 6.5) {\makecell[l]{\\[-8pt]C\texttt{++}\\[-4pt]code}}; \draw (bridge) -- (0, 4); \draw[<->] (rust-bindings) |- (0, 4) -| (cpp-bindings); \draw[<->] (rust-code) -- (rust-bindings); \draw[<->, dash pattern=on 8pt off 6pt] (rust-bindings) -- (cpp-bindings); \draw[<->] (cpp-bindings) -- (cpp-code); \draw (-.75, 4) node[anchor=south east] {Macro expansion}; \draw (.75, 4) node[anchor=south west] {Code generation}; \draw (0, 6.5) node[anchor=south, inner ysep=4pt] {Hidden C ABI}; \draw (-6.75, 6.5) node[anchor=south, inner ysep=1pt] {\makecell{Safe\\[-4pt]straightforward\\[-4pt]Rust APIs}}; \draw (6.75, 6.5) node[anchor=south, inner ysep=1pt] {\makecell{Straightforward\\[-4pt]C\texttt{++} APIs}}; \pgfresetboundingbox\path (-9.5, 0) -- (rust-bindings.south)+(0, .3) -- (9.5, 0) -- (bridge.north); \end{tikzpicture} \end{document} ================================================ FILE: book/eslint.config.mjs ================================================ import pluginJs from '@eslint/js'; /** @type {import('eslint').Linter.Config[]} */ export default [ { ignores: ['build/*'] }, { files: ['**/*.js'], languageOptions: { sourceType: 'commonjs' } }, pluginJs.configs.recommended, ]; ================================================ FILE: book/package.json ================================================ { "name": "cxx-book-build", "version": "0.0.0", "main": "build.js", "dependencies": { "cheerio": "^1.0.0", "html-entities": "^2.5.2" }, "devDependencies": { "@eslint/js": "^9.19.0", "eslint": "^9.19.0" }, "prettier": { "singleQuote": true } } ================================================ FILE: book/src/404.md ================================================ ### Whoops, this page doesn’t exist :-(
    ferris ================================================ FILE: book/src/SUMMARY.md ================================================ # Summary - [Rust ❤️ C++](index.md) - [Core concepts](concepts.md) - [Tutorial](tutorial.md) - [Other Rust–C++ interop tools](context.md) - [Multi-language build system options](building.md) - [Cargo](build/cargo.md) - [Bazel or Buck2](build/bazel.md) - [CMake](build/cmake.md) - [More...](build/other.md) - [Reference: the bridge module](reference.md) - [extern "Rust"](extern-rust.md) - [extern "C++"](extern-c++.md) - [Shared types](shared.md) - [Attributes](attributes.md) - [Async functions](async.md) - [Error handling](binding/result.md) - [Reference: built-in bindings](bindings.md) - [String — rust::String](binding/string.md) - [&str — rust::Str](binding/str.md) - [&[T], &mut [T] — rust::Slice\](binding/slice.md) - [CxxString — std::string](binding/cxxstring.md) - [Box\ — rust::Box\](binding/box.md) - [UniquePtr\ — std::unique\_ptr\](binding/uniqueptr.md) - [SharedPtr\ — std::shared\_ptr\](binding/sharedptr.md) - [Vec\ — rust::Vec\](binding/vec.md) - [CxxVector\ — std::vector\](binding/cxxvector.md) - [*mut T, *const T raw pointers](binding/rawptr.md) - [Function pointers](binding/fn.md) - [Result\](binding/result.md) ================================================ FILE: book/src/async.md ================================================ {{#title Async functions — Rust ♡ C++}} # Async functions Direct FFI of async functions is absolutely in scope for CXX (on C++20 and up) but is not implemented yet in the current release. We are aiming for an implementation that is as easy as: ```rust,noplayground #[cxx::bridge] mod ffi { unsafe extern "C++" { async fn doThing(arg: Arg) -> Ret; } } ``` ```cpp rust::Future doThing(Arg arg) { auto v1 = co_await f(); auto v2 = co_await g(arg); co_return v1 + v2; } ``` ## Workaround For now the recommended approach is to handle the return codepath over a oneshot channel (such as [`futures::channel::oneshot`]) represented in an opaque Rust type on the FFI. [`futures::channel::oneshot`]: https://docs.rs/futures/0.3.31/futures/channel/oneshot/index.html ```rust,noplayground // bridge.rs use futures::channel::oneshot; #[cxx::bridge] mod ffi { extern "Rust" { type DoThingContext; } unsafe extern "C++" { include!("path/to/bridge_shim.h"); fn shim_doThing( arg: Arg, done: fn(Box, ret: Ret), ctx: Box, ); } } struct DoThingContext(oneshot::Sender); pub async fn do_thing(arg: Arg) -> Ret { let (tx, rx) = oneshot::channel(); let context = Box::new(DoThingContext(tx)); ffi::shim_doThing( arg, |context, ret| { let _ = context.0.send(ret); }, context, ); rx.await.unwrap() } ``` ```cpp // bridge_shim.cc #include "path/to/bridge.rs.h" #include "rust/cxx.h" void shim_doThing( Arg arg, rust::Fn ctx, Ret ret)> done, rust::Box ctx) noexcept { doThing(arg) .then([done, ctx(std::move(ctx))](auto &&res) mutable { (*done)(std::move(ctx), std::move(res)); }); } ``` ## Streams Through a multishot channel such as [`futures::channel::mpsc::unbounded`] in place of the `futures::channel::oneshot` from above, C++ can send a stream of values that become a `futures::Stream` in Rust. [`futures::channel::mpsc::unbounded`]: https://docs.rs/futures/0.3.31/futures/channel/mpsc/fn.unbounded.html In this case the callback function will take the channel sender by reference, not as a Box. `rust::Fn` ================================================ FILE: book/src/attributes.md ================================================ {{#title Attributes — Rust ♡ C++}} # Attributes ## namespace The top-level cxx::bridge attribute macro takes an optional `namespace` argument to control the C++ namespace into which to emit extern Rust items and the namespace in which to expect to find the extern C++ items. ```rust,noplayground #[cxx::bridge(namespace = "path::of::my::company")] mod ffi { extern "Rust" { type MyType; // emitted to path::of::my::company::MyType } extern "C++" { type TheirType; // refers to path::of::my::company::TheirType } } ``` Additionally, a `#[namespace = "..."]` attribute may be used inside the bridge module on any extern block or individual item. An item will inherit the namespace specified on its surrounding extern block if any, otherwise the namespace specified with the top level cxx::bridge attribute if any, otherwise the global namespace. ```rust,noplayground #[cxx::bridge(namespace = "third_priority")] mod ffi { #[namespace = "second_priority"] extern "Rust" { fn f(); #[namespace = "first_priority"] fn g(); } extern "Rust" { fn h(); } } ``` The above would result in functions `::second_priority::f`, `::first_priority::g`, `::third_priority::h`. ## rust\_name, cxx\_name Sometimes you want the Rust name of a function or type to differ from its C++ name. Importantly, this enables binding multiple overloads of the same C++ function name using distinct Rust names. ```rust,noplayground #[cxx::bridge] mod ffi { unsafe extern "C++" { #[rust_name = "i32_overloaded_function"] fn cOverloadedFunction(x: i32) -> String; #[rust_name = "str_overloaded_function"] fn cOverloadedFunction(x: &str) -> String; } } ``` The `#[rust_name = "..."]` attribute replaces the name that Rust should use for this function, and an analogous `#[cxx_name = "..."]` attribute replaces the name that C++ should use. Either of the two attributes may be used on extern "Rust" as well as extern "C++" functions, according to which one you find clearer in context. The same attribute works for renaming functions, opaque types, shared structs and enums, and enum variants. ## Self Indicates the name of the type in which to place a [Rust associated function] or [C++ static member function]. [Rust associated function]: extern-rust.md#associated-functions [C++ static member function]: extern-c++.md#functions-and-member-functions ```rust,noplayground #[cxx::bridge] mod ffi { extern "Rust" { type RustType; #[Self = "RustType"] fn member(); // callable from C++ as `RustType::member()` } unsafe extern "C++" { type CppType; #[Self = "CppType"] fn member(); // callable from Rust as `CppType::member()` } } ``` ================================================ FILE: book/src/binding/box.md ================================================ {{#title rust::Box — Rust ♡ C++}} # rust::Box\ ### Public API: ```cpp,hidelines=... // rust/cxx.h ... ...#include ... ...namespace rust { template class Box final { public: using element_type = T; using const_pointer = typename std::add_pointer::type>::type; using pointer = typename std::add_pointer::type; Box(Box &&) noexcept; ~Box() noexcept; explicit Box(const T &); explicit Box(T &&); Box &operator=(Box &&) & noexcept; const T *operator->() const noexcept; const T &operator*() const noexcept; T *operator->() noexcept; T &operator*() noexcept; template static Box in_place(Fields &&...); void swap(Box &) noexcept; // Important: requires that `raw` came from an into_raw call. Do not // pass a pointer from `new` or any other source. static Box from_raw(T *) noexcept; T *into_raw() noexcept; }; ... ...} // namespace rust ``` ### Restrictions: Box\ does not support T being an opaque C++ type. You should use [UniquePtr\](uniqueptr.md) or [SharedPtr\](sharedptr.md) instead for transferring ownership of opaque C++ types on the language boundary. If T is an opaque Rust type, the Rust type is required to be [Sized] i.e. size known at compile time. In the future we may introduce support for dynamically sized opaque Rust types. [Sized]: https://doc.rust-lang.org/std/marker/trait.Sized.html ## Example This program uses a Box to pass ownership of some opaque piece of Rust state over to C++ and then back to a Rust callback, which is a useful pattern for implementing [async functions over FFI](../async.md). ```rust,noplayground // src/main.rs use std::io::Write; #[cxx::bridge] mod ffi { extern "Rust" { type File; } unsafe extern "C++" { include!("example/include/example.h"); fn f( callback: fn(Box, fst: &str, snd: &str), out: Box, ); } } pub struct File(std::fs::File); fn main() { let out = std::fs::File::create("example.log").unwrap(); ffi::f( |mut out, fst, snd| { let _ = write!(out.0, "{}{}\n", fst, snd); }, Box::new(File(out)), ); } ``` ```cpp // include/example.h #pragma once #include "example/src/main.rs.h" #include "rust/cxx.h" void f(rust::Fn, rust::Str, rust::Str)> callback, rust::Box out); ``` ```cpp // include/example.cc #include "example/include/example.h" void f(rust::Fn, rust::Str, rust::Str)> callback, rust::Box out) { callback(std::move(out), "fearless", "concurrency"); } ``` ================================================ FILE: book/src/binding/cxxstring.md ================================================ {{#title std::string — Rust ♡ C++}} # std::string The Rust binding of std::string is called **[`CxxString`]**. See the link for documentation of the Rust API. [`CxxString`]: https://docs.rs/cxx/*/cxx/struct.CxxString.html ### Restrictions: Rust code can never obtain a CxxString by value. C++'s string requires a move constructor and may hold internal pointers, which is not compatible with Rust's move behavior. Instead in Rust code we will only ever look at a CxxString through a reference or smart pointer, as in &CxxString or Pin\<&mut CxxString\> or UniquePtr\. In order to construct a CxxString on the stack from Rust, you must use the [`let_cxx_string!`] macro which will pin the string properly. The code below uses this in one place, and the link covers the syntax. [`let_cxx_string!`]: https://docs.rs/cxx/*/cxx/macro.let_cxx_string.html ## Example This example uses C++17's std::variant to build a toy JSON type. JSON can hold various types including strings, and JSON's object type is a map with string keys. The example demonstrates Rust indexing into one of those maps. ```rust,noplayground // src/main.rs use cxx::let_cxx_string; #[cxx::bridge] mod ffi { unsafe extern "C++" { include!("example/include/json.h"); #[cxx_name = "json"] type Json; #[cxx_name = "object"] type Object; fn isNull(self: &Json) -> bool; fn isNumber(self: &Json) -> bool; fn isString(self: &Json) -> bool; fn isArray(self: &Json) -> bool; fn isObject(self: &Json) -> bool; fn getNumber(self: &Json) -> f64; fn getString(self: &Json) -> &CxxString; fn getArray(self: &Json) -> &CxxVector; fn getObject(self: &Json) -> &Object; #[cxx_name = "at"] fn get<'a>(self: &'a Object, key: &CxxString) -> &'a Json; fn load_config() -> UniquePtr; } } fn main() { let config = ffi::load_config(); let_cxx_string!(key = "name"); println!("{}", config.getObject().get(&key).getString()); } ``` ```cpp // include/json.h #pragma once #include #include #include #include #include class json final { public: static const json null; using number = double; using string = std::string; using array = std::vector; using object = std::map; json() noexcept = default; json(const json &) = default; json(json &&) = default; template json(T &&...value) : value(std::forward(value)...) {} bool isNull() const; bool isNumber() const; bool isString() const; bool isArray() const; bool isObject() const; number getNumber() const; const string &getString() const; const array &getArray() const; const object &getObject() const; private: std::variant value; }; using object = json::object; std::unique_ptr load_config(); ``` ```cpp // include/json.cc #include "example/include/json.h" #include #include const json json::null{}; bool json::isNull() const { return std::holds_alternative(value); } bool json::isNumber() const { return std::holds_alternative(value); } bool json::isString() const { return std::holds_alternative(value); } bool json::isArray() const { return std::holds_alternative(value); } bool json::isObject() const { return std::holds_alternative(value); } json::number json::getNumber() const { return std::get(value); } const json::string &json::getString() const { return std::get(value); } const json::array &json::getArray() const { return std::get(value); } const json::object &json::getObject() const { return std::get(value); } std::unique_ptr load_config() { return std::make_unique( std::in_place_type, std::initializer_list>{ {"name", "cxx-example"}, {"edition", 2021.}, {"repository", json::null}}); } ``` ================================================ FILE: book/src/binding/cxxvector.md ================================================ {{#title std::vector — Rust ♡ C++}} # std::vector\ The Rust binding of std::vector\ is called **[`CxxVector`]**. See the link for documentation of the Rust API. [`CxxVector`]: https://docs.rs/cxx/*/cxx/struct.CxxVector.html ### Restrictions: Rust code can never obtain a CxxVector by value. Instead in Rust code we will only ever look at a vector behind a reference or smart pointer, as in &CxxVector\ or UniquePtr\\>. CxxVector\ does not support T being an opaque Rust type. You should use a Vec\ (C++ rust::Vec\) instead for collections of opaque Rust types on the language boundary. ## Example This program involves Rust code converting a `CxxVector` (i.e. `std::vector`) into a Rust `Vec`. ```rust,noplayground // src/main.rs #![no_main] // main defined in C++ by main.cc use cxx::{CxxString, CxxVector}; #[cxx::bridge] mod ffi { extern "Rust" { fn f(vec: &CxxVector); } } fn f(vec: &CxxVector) { let vec: Vec = vec .iter() .map(|s| s.to_string_lossy().into_owned()) .collect(); g(&vec); } fn g(vec: &[String]) { println!("{:?}", vec); } ``` ```cpp // src/main.cc #include "example/src/main.rs.h" #include #include int main() { std::vector vec{"fearless", "concurrency"}; f(vec); } ``` ================================================ FILE: book/src/binding/fn.md ================================================ {{#title Function pointers — Rust ♡ C++}} # Function pointers ### Public API: ```cpp,hidelines=... // rust/cxx.h ... ...namespace rust { template class Fn; template class Fn final { public: Ret operator()(Args... args) const noexcept; Fn operator*() const noexcept; }; ... ...} // namespace rust ``` ### Restrictions: Function pointers with a Result return type are not implemented yet. Passing a function pointer from C++ to Rust is not implemented yet, only from Rust to an `extern "C++"` function is implemented. ## Example Function pointers are commonly useful for implementing [async functions over FFI](../async.md). See the example code on that page. ================================================ FILE: book/src/binding/rawptr.md ================================================ {{#title *mut T, *const T — Rust ♡ C++}} # *mut T, *const T Generally you should use references (`&mut T`, `&T`) or [std::unique_ptr\] where possible over raw pointers, but raw pointers are available too as an unsafe fallback option. [std::unique_ptr\]: uniqueptr.md ### Restrictions: Extern functions and function pointers taking a raw pointer as an argument must be declared `unsafe fn` i.e. unsafe to call. The same does not apply to functions which only *return* a raw pointer, though presumably doing anything useful with the returned pointer is going to involve unsafe code elsewhere anyway. ## Example This example illustrates making a Rust call to a canonical C-style `main` signature involving `char *argv[]`. ```cpp // include/args.h #pragma once void parseArgs(int argc, char *argv[]); ``` ```cpp // src/args.cc #include "example/include/args.h" #include void parseArgs(int argc, char *argv[]) { std::cout << argc << std::endl; for (int i = 0; i < argc; i++) { std::cout << '"' << argv[i] << '"' << std::endl; } } ``` ```rust,noplayground // src/main.rs use std::env; use std::ffi::CString; use std::os::raw::c_char; use std::os::unix::ffi::OsStrExt; use std::ptr; #[cxx::bridge] mod ffi { extern "C++" { include!("example/include/args.h"); unsafe fn parseArgs(argc: i32, argv: *mut *mut c_char); } } fn main() { // Convert from OsString to nul-terminated CString, truncating each argument // at the first inner nul byte if present. let args: Vec = env::args_os() .map(|os_str| { let bytes = os_str.as_bytes(); CString::new(bytes).unwrap_or_else(|nul_error| { let nul_position = nul_error.nul_position(); let mut bytes = nul_error.into_vec(); bytes.truncate(nul_position); CString::new(bytes).unwrap() }) }) .collect(); // Convert from Vec of owned strings to Vec<*mut c_char> of // borrowed string pointers. // // Once extern type stabilizes (https://github.com/rust-lang/rust/issues/43467) // and https://internals.rust-lang.org/t/pre-rfc-make-cstr-a-thin-pointer/6258 // is implemented, and CStr pointers become thin, we can sidestep this step // by accumulating the args as Vec> up front, then simply casting // from *mut [Box] to *mut [*mut CStr] to *mut *mut c_char. let argc = args.len(); let mut argv: Vec<*mut c_char> = Vec::with_capacity(argc + 1); for arg in &args { argv.push(arg.as_ptr() as *mut c_char); } argv.push(ptr::null_mut()); // Nul terminator. unsafe { ffi::parseArgs(argc as i32, argv.as_mut_ptr()); } // The CStrings go out of scope here. C function must not have held on to // the pointers beyond this point. } ``` ================================================ FILE: book/src/binding/result.md ================================================ {{#title Result — Rust ♡ C++}} # Result\ Result\ is allowed as the return type of an extern function in either direction. Its behavior is to translate to/from C++ exceptions. If your codebase does not use C++ exceptions, or prefers to represent fallibility using something like outcome\, leaf::result\, StatusOr\, etc then you'll need to handle the translation of those to Rust Result\ using your own shims for now. Better support for this is planned. If an exception is thrown from an `extern "C++"` function that is *not* declared by the CXX bridge to return Result, the program calls C++'s `std::terminate`. The behavior is equivalent to the same exception being thrown through a `noexcept` C++ function. If a panic occurs in *any* `extern "Rust"` function, regardless of whether it is declared by the CXX bridge to return Result, a message is logged and the program calls Rust's `std::process::abort`. ## Returning Result from Rust to C++ An `extern "Rust"` function returning a Result turns into a `throw` in C++ if the Rust side produces an error. Note that the return type written inside of cxx::bridge must be written without a second type parameter. Only the Ok type is specified for the purpose of the FFI. The Rust *implementation* (outside of the bridge module) may pick any error type as long as it has a std::fmt::Display impl. ```rust,noplayground # use std::io; # #[cxx::bridge] mod ffi { extern "Rust" { fn fallible1(depth: usize) -> Result; fn fallible2() -> Result<()>; } } fn fallible1(depth: usize) -> anyhow::Result { if depth == 0 { return Err(anyhow::Error::msg("fallible1 requires depth > 0")); } ... } fn fallible2() -> Result<(), io::Error> { ... Ok(()) } ``` The exception that gets thrown by CXX on the C++ side is always of type `rust::Error` and has the following C++ public API. The `what()` member function gives the error message according to the Rust error's std::fmt::Display impl. ```cpp,hidelines=... // rust/cxx.h ... ...namespace rust { class Error final : public std::exception { public: Error(const Error &); Error(Error &&) noexcept; ~Error() noexcept; Error &operator=(const Error &) &; Error &operator=(Error &&) & noexcept; const char *what() const noexcept override; }; ... ...} // namespace rust ``` ## Returning Result from C++ to Rust An `extern "C++"` function returning a Result turns into a `catch` in C++ that converts the exception into an Err for Rust. Note that the return type written inside of cxx::bridge must be written without a second type parameter. Only the Ok type is specified for the purpose of the FFI. The resulting error type created by CXX when an `extern "C++"` function throws will always be of type **[`cxx::Exception`]**. [`cxx::Exception`]: https://docs.rs/cxx/*/cxx/struct.Exception.html ```rust,noplayground # use std::process; # #[cxx::bridge] mod ffi { unsafe extern "C++" { include!("example/include/example.h"); fn fallible1(depth: usize) -> Result; fn fallible2() -> Result<()>; } } fn main() { if let Err(err) = ffi::fallible1(99) { eprintln!("Error: {}", err); process::exit(1); } } ``` The specific set of caught exceptions and the conversion to error message are both customizable. The way you do this is by defining a template function `rust::behavior::trycatch` with a suitable signature inside any one of the headers `include!`'d by your cxx::bridge. The template signature is required to be: ```cpp namespace rust { namespace behavior { template static void trycatch(Try &&func, Fail &&fail) noexcept; } // namespace behavior } // namespace rust ``` The default `trycatch` used by CXX if you have not provided your own is the following. You must follow the same pattern: invoke `func` with no arguments, catch whatever exception(s) you want, and invoke `fail` with the error message you'd like for the Rust error to have. ```cpp,hidelines=... ...#include ... ...namespace rust { ...namespace behavior { ... template static void trycatch(Try &&func, Fail &&fail) noexcept try { func(); } catch (const std::exception &e) { fail(e.what()); } ... ...} // namespace behavior ...} // namespace rust ``` ================================================ FILE: book/src/binding/sharedptr.md ================================================ {{#title std::shared_ptr — Rust ♡ C++}} # std::shared\_ptr\ The Rust binding of std::shared\_ptr\ is called **[`SharedPtr`]**. See the link for documentation of the Rust API. [`SharedPtr`]: https://docs.rs/cxx/*/cxx/struct.SharedPtr.html ### Restrictions: SharedPtr\ does not support T being an opaque Rust type. You should use a Box\ (C++ [rust::Box\](box.md)) instead for transferring ownership of opaque Rust types on the language boundary. ## Example ```rust,noplayground // src/main.rs use std::ops::Deref; use std::ptr; #[cxx::bridge] mod ffi { unsafe extern "C++" { include!("example/include/example.h"); type Object; fn create_shared_ptr() -> SharedPtr; } } fn main() { let ptr1 = ffi::create_shared_ptr(); { // Create a second shared_ptr holding shared ownership of the same // object. There is still only one Object but two SharedPtr. // Both pointers point to the same object on the heap. let ptr2 = ptr1.clone(); assert!(ptr::eq(ptr1.deref(), ptr2.deref())); // ptr2 goes out of scope, but Object is not destroyed yet. } println!("say goodbye to Object"); // ptr1 goes out of scope and Object is destroyed. } ``` ```cpp // include/example.h #pragma once #include class Object { public: Object(); ~Object(); }; std::shared_ptr create_shared_ptr(); ``` ```cpp // src/example.cc #include "example/include/example.h" #include Object::Object() { std::cout << "construct Object" << std::endl; } Object::~Object() { std::cout << "~Object" << std::endl; } std::shared_ptr create_shared_ptr() { return std::make_shared(); } ``` ================================================ FILE: book/src/binding/slice.md ================================================ {{#title rust::Slice — Rust ♡ C++}} # rust::Slice\, rust::Slice\ - Rust `&[T]` is written `rust::Slice` in C++ - Rust `&mut [T]` is written `rust::Slice` in C++ ### Public API: ```cpp,hidelines=... // rust/cxx.h ... ...#include ...#include ... ...namespace rust { template class Slice final { public: using value_type = T; Slice() noexcept; Slice(const Slice &) noexcept; Slice(T *, size_t count) noexcept; template explicit Slice(C &c) : Slice(c.data(), c.size()); Slice &operator=(Slice &&) & noexcept; Slice &operator=(const Slice &) & noexcept requires std::is_const_v; T *data() const noexcept; size_t size() const noexcept; bool empty() const noexcept; T &operator[](size_t n) const noexcept; T &at(size_t n) const; T &front() const noexcept; T &back() const noexcept; class iterator; iterator begin() const noexcept; iterator end() const noexcept; void swap(Slice &) noexcept; }; ... ...template ...class Slice::iterator final { ...public: ...#if __cplusplus >= 202002L ... using iterator_category = std::contiguous_iterator_tag; ...#else ... using iterator_category = std::random_access_iterator_tag; ...#endif ... using value_type = T; ... using pointer = T *; ... using reference = T &; ... ... T &operator*() const noexcept; ... T *operator->() const noexcept; ... T &operator[](ptrdiff_t) const noexcept; ... ... iterator &operator++() noexcept; ... iterator operator++(int) noexcept; ... iterator &operator--() noexcept; ... iterator operator--(int) noexcept; ... ... iterator &operator+=(ptrdiff_t) noexcept; ... iterator &operator-=(ptrdiff_t) noexcept; ... iterator operator+(ptrdiff_t) const noexcept; ... iterator operator-(ptrdiff_t) const noexcept; ... ptrdiff_t operator-(const iterator &) const noexcept; ... ... bool operator==(const iterator &) const noexcept; ... bool operator!=(const iterator &) const noexcept; ... bool operator<(const iterator &) const noexcept; ... bool operator>(const iterator &) const noexcept; ... bool operator<=(const iterator &) const noexcept; ... bool operator>=(const iterator &) const noexcept; ...}; ... ...} // namespace rust ``` ### Restrictions: T must not be an opaque Rust type or opaque C++ type. Support for opaque Rust types in slices is coming. Allowed as function argument or return value. Not supported in shared structs. Only rust::Slice\ is copy-assignable, not rust::Slice\. (Both are move-assignable.) You'll need to write std::move occasionally as a reminder that accidentally exposing overlapping &mut \[T\] to Rust is UB. ## Example This example is a C++ program that constructs a slice containing JSON data (by reading from stdin, but it could be from anywhere), then calls into Rust to pretty-print that JSON data into a std::string via the [serde_json] and [serde_transcode] crates. [serde_json]: https://github.com/serde-rs/json [serde_transcode]: https://github.com/sfackler/serde-transcode ```rust,noplayground // src/main.rs #![no_main] // main defined in C++ by main.cc use cxx::CxxString; use std::io::{self, Write}; use std::pin::Pin; #[cxx::bridge] mod ffi { extern "Rust" { fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> Result<()>; } } struct WriteToCxxString<'a>(Pin<&'a mut CxxString>); impl<'a> Write for WriteToCxxString<'a> { fn write(&mut self, buf: &[u8]) -> io::Result { self.0.as_mut().push_bytes(buf); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } fn prettify_json(input: &[u8], output: Pin<&mut CxxString>) -> serde_json::Result<()> { let writer = WriteToCxxString(output); let mut deserializer = serde_json::Deserializer::from_slice(input); let mut serializer = serde_json::Serializer::pretty(writer); serde_transcode::transcode(&mut deserializer, &mut serializer) } ``` ```cpp // src/main.cc #include "example/src/main.rs.h" #include #include #include #include int main() { // Read json from stdin. std::istreambuf_iterator begin{std::cin}, end; std::vector input{begin, end}; rust::Slice slice{input.data(), input.size()}; // Prettify using serde_json and serde_transcode. std::string output; prettify_json(slice, output); // Write to stdout. std::cout << output << std::endl; } ``` Testing the example: ```console $ echo '{"fearless":"concurrency"}' | cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/example` { "fearless": "concurrency" } ``` ================================================ FILE: book/src/binding/str.md ================================================ {{#title rust::Str — Rust ♡ C++}} # rust::Str ### Public API: ```cpp,hidelines=... // rust/cxx.h ... ...#include ...#include ... ...namespace rust { class Str final { public: Str() noexcept; Str(const Str &) noexcept; Str(const String &) noexcept; // Throws std::invalid_argument if not utf-8. Str(const std::string &); Str(const char *); Str(const char *, size_t); Str &operator=(const Str &) & noexcept; explicit operator std::string() const; #if __cplusplus >= 201703L explicit operator std::string_view() const; #endif // Note: no null terminator. const char *data() const noexcept; // Length in bytes. size_t size() const noexcept; // Length in bytes, same as size(). size_t length() const noexcept; bool empty() const noexcept; using iterator = const char *; using const_iterator = const char *; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; bool operator==(const Str &) const noexcept; bool operator!=(const Str &) const noexcept; bool operator<(const Str &) const noexcept; bool operator<=(const Str &) const noexcept; bool operator>(const Str &) const noexcept; bool operator>=(const Str &) const noexcept; void swap(Str &) noexcept; }; std::ostream &operator<<(std::ostream &, const Str &); ... ...} // namespace rust ``` ### Notes: **Be aware that rust::Str behaves like &str i.e. it is a borrow!** C++ needs to be mindful of the lifetimes at play. Just to reiterate: &str is rust::Str. Do not try to write &str as `const rust::Str &`. A language-level C++ reference is not able to capture the fat pointer nature of &str. ### Restrictions: Allowed as function argument or return value. Not supported in shared structs yet. `&mut str` is not supported yet, but is also extremely obscure so this is fine. ## Example ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { extern "Rust" { fn r(greeting: &str); } unsafe extern "C++" { include!("example/include/greeting.h"); fn c(greeting: &str); } } fn r(greeting: &str) { println!("{}", greeting); } fn main() { ffi::c("hello from Rust"); } ``` ```cpp // include/greeting.h #pragma once #include "example/src/main.rs.h" #include "rust/cxx.h" void c(rust::Str greeting); ``` ```cpp // src/greeting.cc #include "example/include/greeting.h" #include void c(rust::Str greeting) { std::cout << greeting << std::endl; r("hello from C++"); } ``` ================================================ FILE: book/src/binding/string.md ================================================ {{#title rust::String — Rust ♡ C++}} # rust::String ### Public API: ```cpp,hidelines=... // rust/cxx.h ... ...#include ...#include ... ...namespace rust { class String final { public: String() noexcept; String(const String &) noexcept; String(String &&) noexcept; ~String() noexcept; // Throws std::invalid_argument if not UTF-8. String(const std::string &); String(const char *); String(const char *, size_t); String(const char8_t *); String(const char8_t *, size_t); // Replaces invalid UTF-8 data with the replacement character (U+FFFD). static String lossy(const std::string &) noexcept; static String lossy(const char *) noexcept; static String lossy(const char *, size_t) noexcept; // Throws std::invalid_argument if not UTF-16. String(const char16_t *); String(const char16_t *, size_t); // Replaces invalid UTF-16 data with the replacement character (U+FFFD). static String lossy(const char16_t *) noexcept; static String lossy(const char16_t *, size_t) noexcept; String &operator=(const String &) & noexcept; String &operator=(String &&) & noexcept; explicit operator std::string() const; // Note: no null terminator. const char *data() const noexcept; // Length in bytes. size_t size() const noexcept; // Length in bytes, same as size(). size_t length() const noexcept; bool empty() const noexcept; const char *c_str() noexcept; size_t capacity() const noexcept; void reserve(size_t new_cap) noexcept; using iterator = char *; iterator begin() noexcept; iterator end() noexcept; using const_iterator = const char *; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; bool operator==(const String &) const noexcept; bool operator!=(const String &) const noexcept; bool operator<(const String &) const noexcept; bool operator<=(const String &) const noexcept; bool operator>(const String &) const noexcept; bool operator>=(const String &) const noexcept; void swap(String &) noexcept; }; std::ostream &operator<<(std::ostream &, const String &); ... ...} // namespace rust ``` ### Restrictions: None. Strings may be used as function arguments and function return values, by value or by reference, as well as fields of shared structs. ## Example ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { struct ConcatRequest { fst: String, snd: String, } unsafe extern "C++" { include!("example/include/concat.h"); fn concat(r: ConcatRequest) -> String; } } fn main() { let concatenated = ffi::concat(ffi::ConcatRequest { fst: "fearless".to_owned(), snd: "concurrency".to_owned(), }); println!("concatenated: {:?}", concatenated); } ``` ```cpp // include/concat.h #pragma once #include "example/src/main.rs.h" #include "rust/cxx.h" rust::String concat(ConcatRequest r); ``` ```cpp // src/concat.cc #include "example/include/concat.h" rust::String concat(ConcatRequest r) { // The full suite of operator overloads hasn't been added // yet on rust::String, but we can get it done like this: return std::string(r.fst) + std::string(r.snd); } ``` ================================================ FILE: book/src/binding/uniqueptr.md ================================================ {{#title std::unique_ptr — Rust ♡ C++}} # std::unique\_ptr\ The Rust binding of std::unique\_ptr\ is called **[`UniquePtr`]**. See the link for documentation of the Rust API. [`UniquePtr`]: https://docs.rs/cxx/*/cxx/struct.UniquePtr.html ### Restrictions: Only `std::unique_ptr>` is currently supported. Custom deleters may be supported in the future. UniquePtr\ does not support T being an opaque Rust type. You should use a Box\ (C++ [rust::Box\](box.md)) instead for transferring ownership of opaque Rust types on the language boundary. ## Example UniquePtr is commonly useful for returning opaque C++ objects to Rust. This use case was featured in the [*blobstore tutorial*](../tutorial.md). ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { unsafe extern "C++" { include!("example/include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr; // ... } } fn main() { let client = ffi::new_blobstore_client(); // ... } ``` ```cpp // include/blobstore.h #pragma once #include class BlobstoreClient; std::unique_ptr new_blobstore_client(); ``` ```cpp // src/blobstore.cc #include "example/include/blobstore.h" std::unique_ptr new_blobstore_client() { return std::make_unique(); } ``` ================================================ FILE: book/src/binding/vec.md ================================================ {{#title rust::Vec — Rust ♡ C++}} # rust::Vec\ ### Public API: ```cpp,hidelines=... // rust/cxx.h ... ...#include ...#include ...#include ... ...namespace rust { template class Vec final { public: using value_type = T; Vec() noexcept; Vec(std::initializer_list); Vec(const Vec &); Vec(Vec &&) noexcept; ~Vec() noexcept; Vec &operator=(Vec &&) & noexcept; Vec &operator=(const Vec &) &; size_t size() const noexcept; bool empty() const noexcept; const T *data() const noexcept; T *data() noexcept; size_t capacity() const noexcept; const T &operator[](size_t n) const noexcept; const T &at(size_t n) const; const T &front() const; const T &back() const; T &operator[](size_t n) noexcept; T &at(size_t n); T &front(); T &back(); void reserve(size_t new_cap); void push_back(const T &value); void push_back(T &&value); template void emplace_back(Args &&...args); void truncate(size_t len); void clear(); class iterator; iterator begin() noexcept; iterator end() noexcept; class const_iterator; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; void swap(Vec &) noexcept; }; ... ...template ...class Vec::iterator final { ...public: ...#if __cplusplus >= 202002L ... using iterator_category = std::contiguous_iterator_tag; ...#else ... using iterator_category = std::random_access_iterator_tag; ...#endif ... using value_type = T; ... using pointer = T *; ... using reference = T &; ... ... T &operator*() const noexcept; ... T *operator->() const noexcept; ... T &operator[](ptrdiff_t) const noexcept; ... ... iterator &operator++() noexcept; ... iterator operator++(int) noexcept; ... iterator &operator--() noexcept; ... iterator operator--(int) noexcept; ... ... iterator &operator+=(ptrdiff_t) noexcept; ... iterator &operator-=(ptrdiff_t) noexcept; ... iterator operator+(ptrdiff_t) const noexcept; ... iterator operator-(ptrdiff_t) const noexcept; ... ptrdiff_t operator-(const iterator &) const noexcept; ... ... bool operator==(const iterator &) const noexcept; ... bool operator!=(const iterator &) const noexcept; ... bool operator<(const iterator &) const noexcept; ... bool operator<=(const iterator &) const noexcept; ... bool operator>(const iterator &) const noexcept; ... bool operator>=(const iterator &) const noexcept; ...}; ... ...template ...class Vec::const_iterator final { ...public: ...#if __cplusplus >= 202002L ... using iterator_category = std::contiguous_iterator_tag; ...#else ... using iterator_category = std::random_access_iterator_tag; ...#endif ... using value_type = const T; ... using pointer = const T *; ... using reference = const T &; ... ... const T &operator*() const noexcept; ... const T *operator->() const noexcept; ... const T &operator[](ptrdiff_t) const noexcept; ... ... const_iterator &operator++() noexcept; ... const_iterator operator++(int) noexcept; ... const_iterator &operator--() noexcept; ... const_iterator operator--(int) noexcept; ... ... const_iterator &operator+=(ptrdiff_t) noexcept; ... const_iterator &operator-=(ptrdiff_t) noexcept; ... const_iterator operator+(ptrdiff_t) const noexcept; ... const_iterator operator-(ptrdiff_t) const noexcept; ... ptrdiff_t operator-(const const_iterator &) const noexcept; ... ... bool operator==(const const_iterator &) const noexcept; ... bool operator!=(const const_iterator &) const noexcept; ... bool operator<(const const_iterator &) const noexcept; ... bool operator<=(const const_iterator &) const noexcept; ... bool operator>(const const_iterator &) const noexcept; ... bool operator>=(const const_iterator &) const noexcept; ...}; ... ...} // namespace rust ``` ### Restrictions: Vec\ does not support T being an opaque C++ type. You should use CxxVector\ (C++ std::vector\) instead for collections of opaque C++ types on the language boundary. ## Example ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { struct Shared { v: u32, } unsafe extern "C++" { include!("example/include/example.h"); fn f(elements: Vec); } } fn main() { let shared = |v| ffi::Shared { v }; let elements = vec![shared(3), shared(2), shared(1)]; ffi::f(elements); } ``` ```cpp // include/example.h #pragma once #include "example/src/main.rs.h" #include "rust/cxx.h" void f(rust::Vec elements); ``` ```cpp // src/example.cc #include "example/include/example.h" #include #include #include #include #include void f(rust::Vec v) { for (auto shared : v) { std::cout << shared.v << std::endl; } // Copy the elements to a C++ std::vector using STL algorithm. std::vector stdv; std::copy(v.begin(), v.end(), std::back_inserter(stdv)); assert(v.size() == stdv.size()); } ``` ================================================ FILE: book/src/bindings.md ================================================ {{#title Built-in bindings — Rust ♡ C++}} # Built-in bindings reference In addition to all the primitive types (i32 <=> int32_t), the following common types may be used in the fields of shared structs and the arguments and returns of extern functions.
    name in Rustname in C++restrictions
    Stringrust::String
    &strrust::Str
    &[T]rust::Slice<const T>cannot hold opaque C++ type
    &mut [T]rust::Slice<T>cannot hold opaque C++ type
    CxxStringstd::stringcannot be passed by value
    Box<T>rust::Box<T>cannot hold opaque C++ type
    UniquePtr<T>std::unique_ptr<T>cannot hold opaque Rust type
    SharedPtr<T>std::shared_ptr<T>cannot hold opaque Rust type
    [T; N]std::array<T, N>cannot hold opaque C++ type
    Vec<T>rust::Vec<T>cannot hold opaque C++ type
    CxxVector<T>std::vector<T>cannot be passed by value, cannot hold opaque Rust type
    *mut T, *const TT*, const T*fn with a raw pointer argument must be declared unsafe to call
    fn(T, U) -> Vrust::Fn<V(T, U)>only passing from Rust to C++ is implemented so far
    Result<T>throw/catchallowed as return type only

    The C++ API of the `rust` namespace is defined by the *include/cxx.h* file in the CXX GitHub repo. You will need to include this header in your C++ code when working with those types. **When using Cargo and the cxx-build crate, the header is made available to you at `#include "rust/cxx.h"`.** The `rust` namespace additionally provides lowercase type aliases of all the types mentioned in the table, for use in codebases preferring that style. For example `rust::String`, `rust::Vec` may alternatively be written `rust::string`, `rust::vec` etc. ## Pending bindings The following types are intended to be supported "soon" but are just not implemented yet. I don't expect any of these to be hard to make work but it's a matter of designing a nice API for each in its non-native language.
    name in Rustname in C++
    BTreeMap<K, V>tbd
    HashMap<K, V>tbd
    Arc<T>tbd
    Option<T>tbd
    tbdstd::map<K, V>
    tbdstd::unordered_map<K, V>
    ================================================ FILE: book/src/build/bazel.md ================================================ {{#title Bazel, Buck2 — Rust ♡ C++}} ## Bazel, Buck2, potentially other similar environments Starlark-based build systems with the ability to compile a code generator and invoke it as a `genrule` will run CXX's C++ code generator via its `cxxbridge` command line interface. The tool is packaged as the `cxxbridge-cmd` crate on crates.io or can be built from the *gen/cmd/* directory of the CXX GitHub repo. ```console $ cargo install cxxbridge-cmd $ cxxbridge src/bridge.rs --header > path/to/bridge.rs.h $ cxxbridge src/bridge.rs > path/to/bridge.rs.cc ```
    **Important:** The version number of `cxxbridge-cmd` used for the C++ side of the binding must always be identical to the version number of `cxx` used for the Rust side. You must use some form of lockfile or version pinning to ensure that this is the case.
    The CXX repo maintains working [Bazel] `BUILD.bazel` and [Buck2] `BUCK` targets for the complete blobstore tutorial (chapter 3) for your reference, tested in CI. These aren't meant to be directly what you use in your codebase, but serve as an illustration of one possible working pattern. [Bazel]: https://bazel.build [Buck2]: https://buck2.build ```python # tools/bazel/rust_cxx_bridge.bzl load("@bazel_skylib//rules:run_binary.bzl", "run_binary") load("@rules_cc//cc:defs.bzl", "cc_library") def rust_cxx_bridge(name, src, deps = []): native.alias( name = "%s/header" % name, actual = src + ".h", ) native.alias( name = "%s/source" % name, actual = src + ".cc", ) run_binary( name = "%s/generated" % name, srcs = [src], outs = [ src + ".h", src + ".cc", ], args = [ "$(location %s)" % src, "-o", "$(location %s.h)" % src, "-o", "$(location %s.cc)" % src, ], tool = "//:codegen", ) cc_library( name = name, srcs = [src + ".cc"], deps = deps + [":%s/include" % name], ) cc_library( name = "%s/include" % name, hdrs = [src + ".h"], ) ``` ```python # demo/BUILD.bazel load("@rules_cc//cc:defs.bzl", "cc_library") load("@rules_rust//rust:defs.bzl", "rust_binary") load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), deps = [ ":blobstore-sys", ":bridge", "//:cxx", ], ) rust_cxx_bridge( name = "bridge", src = "src/main.rs", deps = [":blobstore-include"], ) cc_library( name = "blobstore-sys", srcs = ["src/blobstore.cc"], deps = [ ":blobstore-include", ":bridge/include", ], ) cc_library( name = "blobstore-include", hdrs = ["include/blobstore.h"], deps = ["//:core"], ) ``` ================================================ FILE: book/src/build/cargo.md ================================================ {{#title Cargo-based setup — Rust ♡ C++}} # Cargo-based builds As one aspect of delivering a good Rust–C++ interop experience, CXX turns Cargo into a quite usable build system for C++ projects published as a collection of crates.io packages, including a consistent and frictionless experience `#include`-ing C++ headers across dependencies. ## Canonical setup CXX's integration with Cargo is handled through the [cxx-build] crate. [cxx-build]: https://docs.rs/cxx-build ```toml,hidelines=... # Cargo.toml ...[package] ...name = "..." ...version = "..." ...edition = "2021" [dependencies] cxx = "1.0" [build-dependencies] cxx-build = "1.0" ``` The canonical build script is as follows. The indicated line returns a [`cc::Build`] instance (from the usual widely used `cc` crate) on which you can set up any additional source files and compiler flags as normal. [`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html ```rust,noplayground // build.rs fn main() { cxx_build::bridge("src/main.rs") // returns a cc::Build .file("src/demo.cc") .std("c++11") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/demo.cc"); println!("cargo:rerun-if-changed=include/demo.h"); } ``` The `rerun-if-changed` lines are optional but make it so that Cargo does not spend time recompiling your C++ code when only non-C++ code has changed since the previous Cargo build. By default without any `rerun-if-changed`, Cargo will re-execute the build script after *any* file changed in the project. If stuck, try comparing what you have against the *demo/* directory of the CXX GitHub repo, which maintains a working Cargo-based setup for the blobstore tutorial (chapter 3). ## Header include paths With cxx-build, by default your include paths always start with the crate name. This applies to both `#include` within your C++ code, and `include!` in the `extern "C++"` section of your Rust cxx::bridge. Your crate name is determined by the `name` entry in Cargo.toml. For example if your crate is named `yourcratename` and contains a C++ header file `path/to/header.h` relative to Cargo.toml, that file will be includable as: ```cpp #include "yourcratename/path/to/header.h" ``` A crate can choose a prefix for its headers that is different from the crate name by modifying **[`CFG.include_prefix`][CFG]** from build.rs: [CFG]: https://docs.rs/cxx-build/*/cxx_build/static.CFG.html ```rust,noplayground // build.rs use cxx_build::CFG; fn main() { CFG.include_prefix = "my/project"; cxx_build::bridge(...)... } ``` Subsequently the header located at `path/to/header.h` would now be includable as: ```cpp #include "my/project/path/to/header.h" ``` The empty string `""` is a valid include prefix and will make it possible to have `#include "path/to/header.h"`. However, if your crate is a library, be considerate of possible name collisions that may occur in downstream crates. If using an empty include prefix, you'll want to make sure your headers' local path within the crate is sufficiently namespaced or unique. ## Including generated code If your `#[cxx::bridge]` module contains an `extern "Rust"` block i.e. types or functions exposed from Rust to C++, or any shared data structures, the CXX-generated C++ header declaring those things is available using a `.rs.h` extension on the Rust source file's name. ```cpp // the header generated from path/to/lib.rs #include "yourcratename/path/to/lib.rs.h" ``` For giggles, it's also available using just a plain `.rs` extension as if you were including the Rust file directly. Use whichever you find more palatable. ```cpp #include "yourcratename/path/to/lib.rs" ``` ## Including headers from dependencies You get to include headers from your dependencies, both handwritten ones contained as `.h` files in their Cargo package, as well as CXX-generated ones. It works the same as an include of a local header: use the crate name (or their include\_prefix if their crate changed it) followed by the relative path of the header within the crate. ```cpp #include "dependencycratename/path/to/their/header.h` ``` Note that cross-crate imports are only made available between **direct dependencies**. You must directly depend on the other crate in order to #include its headers; a transitive dependency is not sufficient. Additionally, headers from a direct dependency are only importable if the dependency's Cargo.toml manifest contains a `links` key. If not, its headers will not be importable from outside of the same crate. See *[the `links` manifest key][links]* in the Cargo reference. [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key


    # Advanced features The following CFG settings are only relevant to you if you are writing a library that needs to support downstream crates `#include`-ing its C++ public headers. ## Publicly exporting header directories **[`CFG.exported_header_dirs`][CFG]** (vector of absolute paths) defines a set of additional directories from which the current crate, directly dependent crates, and further crates to which this crate's headers are exported (more below) will be able to `#include` headers. Adding a directory to `exported_header_dirs` is similar to adding it to the current build via the `cc` crate's [`Build::include`], but *also* makes the directory available to downstream crates that want to `#include` one of the headers from your crate. If the dir were added only using `Build::include`, the downstream crate including your header would need to manually add the same directory to their own build as well. [`Build::include`]: https://docs.rs/cc/1/cc/struct.Build.html#method.include When using `exported_header_dirs`, your crate must also set a `links` key for itself in Cargo.toml. See [*the `links` manifest key*][links]. The reason is that Cargo imposes no ordering on the execution of build scripts without a `links` key, which means the downstream crate's build script might otherwise execute before yours decides what to put into `exported_header_dirs`. ### Example One of your crate's headers wants to include a system library, such as `#include "Python.h"`. ```rust,noplayground // build.rs use cxx_build::CFG; use std::path::PathBuf; fn main() { let python3 = pkg_config::probe_library("python3").unwrap(); let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path); CFG.exported_header_dirs.extend(python_include_paths); cxx_build::bridge("src/bridge.rs").compile("demo"); } ``` ### Example Your crate wants to rearrange the headers that it exports vs how they're laid out locally inside the crate's source directory. Suppose the crate as published contains a file at `./include/myheader.h` but wants it available to downstream crates as `#include "foo/v1/public.h"`. ```rust,noplayground // build.rs use cxx_build::CFG; use std::path::Path; use std::{env, fs}; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let headers = Path::new(&out_dir).join("headers"); CFG.exported_header_dirs.push(&headers); // We contain `include/myheader.h` locally, but // downstream will use `#include "foo/v1/public.h"` let foo = headers.join("foo").join("v1"); fs::create_dir_all(&foo).unwrap(); fs::copy("include/myheader.h", foo.join("public.h")).unwrap(); cxx_build::bridge("src/bridge.rs").compile("demo"); } ``` ## Publicly exporting dependencies **[`CFG.exported_header_prefixes`][CFG]** (vector of strings) each refer to the `include_prefix` of one of your direct dependencies, or a prefix thereof. They describe which of your dependencies participate in your crate's C++ public API, as opposed to private use by your crate's implementation. As a general rule, if one of your headers `#include`s something from one of your dependencies, you need to put that dependency's `include_prefix` into `CFG.exported_header_prefixes` (*or* their `links` key into `CFG.exported_header_links`; see below). On the other hand if only your C++ implementation files and *not* your headers are importing from the dependency, you do not export that dependency. The significance of exported headers is that if downstream code (crate **𝒜**) contains an `#include` of a header from your crate (**ℬ**) and your header contains an `#include` of something from your dependency (**𝒞**), the exported dependency **𝒞** becomes available during the downstream crate **𝒜**'s build. Otherwise the downstream crate **𝒜** doesn't know about **𝒞** and wouldn't be able to find what header your header is referring to, and would fail to build. When using `exported_header_prefixes`, your crate must also set a `links` key for itself in Cargo.toml. ### Example Suppose you have a crate with 5 direct dependencies and the `include_prefix` for each one are: - "crate0" - "group/api/crate1" - "group/api/crate2" - "group/api/contrib/crate3" - "detail/crate4" Your header involves types from the first four so we re-export those as part of your public API, while crate4 is only used internally by your cc file not your header, so we do not export: ```rust,noplayground // build.rs use cxx_build::CFG; fn main() { CFG.exported_header_prefixes = vec!["crate0", "group/api"]; cxx_build::bridge("src/bridge.rs") .file("src/impl.cc") .compile("demo"); } ```
    For more fine grained control, there is **[`CFG.exported_header_links`][CFG]** (vector of strings) which each refer to the `links` attribute ([*the `links` manifest key*][links]) of one of your crate's direct dependencies. This achieves an equivalent result to `CFG.exported_header_prefixes` by re-exporting a C++ dependency as part of your crate's public API, except with finer control for cases when multiple crates might be sharing the same `include_prefix` and you'd like to export some but not others. Links attributes are guaranteed to be unique identifiers by Cargo. When using `exported_header_links`, your crate must also set a `links` key for itself in Cargo.toml. ### Example ```rust,noplayground // build.rs use cxx_build::CFG; fn main() { CFG.exported_header_links.push("git2"); cxx_build::bridge("src/bridge.rs").compile("demo"); } ``` ================================================ FILE: book/src/build/cmake.md ================================================ {{#title CMake — Rust ♡ C++}} # CMake There is not an officially endorsed CMake setup for CXX, but a few developers have shared one that they got working. You can try one of these as a starting point. If you feel that you have arrived at a CMake setup that is superior to what is available in these links, feel free to make a PR adding it to this list.
    --- - **** - Supports cross-language link time optimization (LTO) --- - **** - Includes a cbindgen component - Tested on Windows 10 with MSVC, and on Linux --- - **** - Alias target that can be linked into a C++ project - Tested on Windows 10 with GNU target, and on Linux --- - **** - Improved rusty_cmake CMake file to use modern C++ - Rich examples of using different primitive types and Rust's Result return to C++ - MacOS and Linux only --- - **** - Same blobstore example as the official demo, but inverted languages - Minimal CMake configuration - Tested on Linux, macOS, and Windows --- ================================================ FILE: book/src/build/other.md ================================================ {{#title Other build systems — Rust ♡ C++}} # Some other build system You will need to achieve at least these three things: - Produce the CXX-generated C++ bindings code. - Compile the generated C++ code. - Link the resulting objects together with your other C++ and Rust objects. *Not all build systems are created equal. If you're hoping to use a build system from the '90s, especially if you're hoping to overlay the limitations of 2 or more build systems (like automake+cargo) and expect to solve them simultaneously, then be mindful that your expectations are set accordingly and seek sympathy from those who have imposed the same approach on themselves.* ### Producing the generated code CXX's Rust code generation automatically happens when the `#[cxx::bridge]` procedural macro is expanded during the normal Rust compilation process, so no special build steps are required there. But the C++ side of the bindings needs to be generated. Your options are: - Use the `cxxbridge` command, which is a standalone command line interface to the CXX C++ code generator. Wire up your build system to compile and invoke this tool. ```console $ cxxbridge src/bridge.rs --header > path/to/bridge.rs.h $ cxxbridge src/bridge.rs > path/to/bridge.rs.cc ``` It's packaged as the `cxxbridge-cmd` crate on crates.io or can be built from the *gen/cmd/* directory of the CXX GitHub repo. - Or, build your own code generator frontend on top of the [cxx-gen] crate. This is currently unofficial and unsupported.
    **Important:** The Rust side and C++ side of a binding must always be created using the same release of CXX. If using `cxxbridge-cmd` for the C++ side, the version number of `cxxbridge-cmd` must be identical to the version number of `cxx` used for the Rust side. If using `cxx-gen` for the C++ side, its patch number must be identical to the patch number of `cxx`.
    [cxx-gen]: https://docs.rs/cxx-gen ### Compiling C++ However you like. We can provide no guidance. ### Linking the C++ and Rust together When linking a binary which contains mixed Rust and C++ code, you will have to choose between using the Rust toolchain (`rustc`) or the C++ toolchain which you may already have extensively tuned. The generated C++ code and the Rust code generated by the procedural macro both depend on each other. Simple examples may only require one or the other, but in general your linking will need to handle both directions. For some linkers, such as LLD, this is not a problem at all. For others, such as GNU ld, flags like `--start-lib`/`--end-lib` may help. Rust does not generate simple standalone `.o` files, so you can't just throw the Rust-generated code into your existing C++ toolchain linker. Instead you need to choose one of these options: * Use `rustc` as the final linker. Pass any non-Rust libraries using `-L ` and `-l` rustc arguments, and/or `#[link]` directives in your Rust code. If you need to link against C/C++ `.o` files you can use `-Clink-arg=file.o`. * Use your C++ linker. In this case, you first need to use `rustc` and/or `cargo` to generate a _single_ Rust `staticlib` target and pass that into your foreign linker invocation. * If you need to link multiple Rust subsystems, you will need to generate a _single_ `staticlib` perhaps using lots of `extern crate` statements to include multiple Rust `rlib`s. Multiple Rust `staticlib` files are likely to conflict. Passing Rust `rlib`s directly into your non-Rust linker is not supported (but apparently sometimes works). See the [Rust reference's *Linkage*][linkage] page for some general information here. [linkage]: https://doc.rust-lang.org/reference/linkage.html The following open rust-lang issues might hold more recent guidance or inspiration: [rust-lang/rust#73632], [rust-lang/rust#73295]. [rust-lang/rust#73632]: https://github.com/rust-lang/rust/issues/73632 [rust-lang/rust#73295]: https://github.com/rust-lang/rust/issues/73295 ================================================ FILE: book/src/building.md ================================================ {{#title Multi-language build system options — Rust ♡ C++}} # Multi-language build system options CXX is designed to be convenient to integrate into a variety of build systems. If you are working in a project that does not already have a preferred build system for its C++ code *or* which will be relying heavily on open source libraries from the Rust package registry, you're likely to have the easiest experience with Cargo which is the build system commonly used by open source Rust projects. Refer to the ***[Cargo](build/cargo.md)*** chapter about CXX's Cargo support. Among build systems designed for first class multi-language support, Bazel is a solid choice. Refer to the ***[Bazel](build/bazel.md)*** chapter. If your codebase is already invested in CMake, refer to the ***[CMake](build/cmake.md)*** chapter. If you have some other build system that you'd like to try to make work with CXX, see [this page](build/other.md) for notes. ================================================ FILE: book/src/concepts.md ================================================ {{#title Core concepts — Rust ♡ C++}} # Core concepts This page is a brief overview of the major concepts of CXX, enough so that you recognize the shape of things as you read the tutorial and following chapters. In CXX, the language of the FFI boundary involves 3 kinds of items: - **Shared structs** — data structures whose fields are made visible to both languages. The definition written within cxx::bridge in Rust is usually the single source of truth, though there are ways to do sharing based on a bindgen-generated definition with C++ as source of truth. - **Opaque types** — their fields are secret from the other language. These cannot be passed across the FFI by value but only behind an indirection, such as a reference `&`, a Rust `Box`, or a C++ `unique_ptr`. Can be a type alias for an arbitrarily complicated generic language-specific type depending on your use case. - **Functions** — implemented in either language, callable from the other language. ```rust,noplayground,focuscomment # #[cxx::bridge] # mod ffi { // Any shared structs, whose fields will be visible to both languages. # struct BlobMetadata { # size: usize, # tags: Vec, # } # # extern "Rust" { // Zero or more opaque types which both languages can pass around // but only Rust can see the fields. # type MultiBuf; # // Functions implemented in Rust. # fn next_chunk(buf: &mut MultiBuf) -> &[u8]; # } # # unsafe extern "C++" { // One or more headers with the matching C++ declarations for the // enclosing extern "C++" block. Our code generators don't read it // but it gets #include'd and used in static assertions to ensure // our picture of the FFI boundary is accurate. # include!("demo/include/blobstore.h"); # // Zero or more opaque types which both languages can pass around // but only C++ can see the fields. # type BlobstoreClient; # // Functions implemented in C++. # fn new_blobstore_client() -> UniquePtr; # fn put(&self, parts: &mut MultiBuf) -> u64; # fn tag(&self, blobid: u64, tag: &str); # fn metadata(&self, blobid: u64) -> BlobMetadata; # } # } ``` Within the `extern "Rust"` part of the CXX bridge we list the types and functions for which Rust is the source of truth. These all implicitly refer to the `super` module, the parent module of the CXX bridge. You can think of the two items listed in the example above as being like `use super::MultiBuf` and `use super::next_chunk` except re-exported to C++. The parent module will either contain the definitions directly for simple things, or contain the relevant `use` statements to bring them into scope from elsewhere. Within the `extern "C++"` part, we list types and functions for which C++ is the source of truth, as well as the header(s) that declare those APIs. In the future it's possible that this section could be generated bindgen-style from the headers but for now we need the signatures written out; static assertions verify that they are accurate.

    Be aware that the design of this library is intentionally restrictive and opinionated! It isn't a goal to be flexible enough to handle an arbitrary signature in either language. Instead this project is about carving out a highly expressive set of functionality about which we can make powerful safety guarantees today and extend over time. You may find that it takes some practice to use CXX bridge effectively as it won't work in all the ways that you may be used to.
    ================================================ FILE: book/src/context.md ================================================ {{#title Other Rust–C++ interop tools — Rust ♡ C++}} # Context: other Rust–C++ interop tools When it comes to interacting with an idiomatic Rust API or idiomatic C++ API from the other language, the generally applicable approaches outside of the CXX crate are: - Build a C-compatible wrapper around the code (expressed using `extern "C"` signatures, primitives, C-compatible structs, raw pointers). Translate that manually to equivalent `extern "C"` declarations in the other language and keep them in sync. Preferably, build a safe/idiomatic wrapper around the translated `extern "C"` signatures for callers to use. - Build a C wrapper around the C++ code and use **[bindgen]** to translate that programmatically to `extern "C"` Rust signatures. Preferably, build a safe/idiomatic Rust wrapper on top. - Build a C-compatible Rust wrapper around the Rust code and use **[cbindgen]** to translate that programmatically to an `extern "C"` C++ header. Preferably, build an idiomatic C++ wrapper. **If the code you are binding is already *"effectively C"*, the above has you covered.** You should use bindgen or cbindgen, or manually translated C signatures if there aren't too many and they seldom change. [bindgen]: https://github.com/rust-lang/rust-bindgen [cbindgen]: https://github.com/eqrion/cbindgen ## C++ vs C Bindgen has some basic support for C++. It can reason about classes, member functions, and the layout of templated types. However, everything it does related to C++ is best-effort only. Bindgen starts from a point of wanting to generate declarations for everything, so any C++ detail that it hasn't implemented will cause a crash if you are lucky ([bindgen#388]) or more likely silently emit an incompatible signature ([bindgen#380], [bindgen#607], [bindgen#652], [bindgen#778], [bindgen#1194]) which will do arbitrary memory-unsafe things at runtime whenever called. [bindgen#388]: https://github.com/rust-lang/rust-bindgen/issues/388 [bindgen#380]: https://github.com/rust-lang/rust-bindgen/issues/380 [bindgen#607]: https://github.com/rust-lang/rust-bindgen/issues/607 [bindgen#652]: https://github.com/rust-lang/rust-bindgen/issues/652 [bindgen#778]: https://github.com/rust-lang/rust-bindgen/issues/778 [bindgen#1194]: https://github.com/rust-lang/rust-bindgen/issues/1194 Thus using bindgen correctly requires not just juggling all your pointers correctly at the language boundary, but also understanding ABI details and their workarounds and reliably applying them. For example, the programmer will discover that their program sometimes segfaults if they call a function that returns std::unique\_ptr\ through bindgen. Why? Because unique\_ptr, despite being "just a pointer", has a different ABI than a pointer or a C struct containing a pointer ([bindgen#778]) and is not directly expressible in Rust. Bindgen emitted something that *looks* reasonable and you will have a hell of a time in gdb working out what went wrong. Eventually people learn to avoid anything involving a non-trivial copy constructor, destructor, or inheritance, and instead stick to raw pointers and primitives and trivial structs only — in other words C. ## Geometric intuition for why there is so much opportunity for improvement The CXX project attempts a different approach to C++ FFI. Imagine Rust and C and C++ as three vertices of a scalene triangle, with length of the edges being related to similarity of the languages when it comes to library design. The most similar pair (the shortest edge) is Rust–C++. These languages have largely compatible concepts of things like ownership, vectors, strings, fallibility, etc that translate clearly from signatures in either language to signatures in the other language. When we make a binding for an idiomatic C++ API using bindgen, and we fall down to raw pointers and primitives and trivial structs as described above, what we are really doing is coding the two longest edges of the triangle: getting from C++ down to C, and C back up to Rust. The Rust–C edge always involves a great deal of `unsafe` code, and the C++–C edge similarly requires care just for basic memory safety. Something as basic as "how do I pass ownership of a string to the other language?" becomes a strap-yourself-in moment, particularly for someone not already an expert in one or both sides. You should think of the `cxx` crate as being the midpoint of the Rust–C++ edge. Rather than coding the two long edges, you will code half the short edge in Rust and half the short edge in C++, in both cases with the library playing to the strengths of the Rust type system *and* the C++ type system to help assure correctness. If you've already been through the tutorial in the previous chapter, take a moment to appreciate that the C++ side *really* looks like we are just writing C++ and the Rust side *really* looks like we are just writing Rust. Anything you could do wrong in Rust, and almost anything you could reasonably do wrong in C++, will be caught by the compiler. This highlights that we are on the "short edge of the triangle". But it all still boils down to the same things: it's still FFI from one piece of native code to another, nothing is getting serialized or allocated or runtime-checked in between. ## Role of CXX The role of CXX is to capture the language boundary with more fidelity than what `extern "C"` is able to represent. You can think of CXX as being a replacement for `extern "C"` in a sense. From this perspective, CXX is a lower level tool than the bindgens. Just as bindgen and cbindgen are built on top of `extern "C"`, it makes sense to think about higher level tools built on top of CXX. Such a tool might consume a C++ header and/or Rust module (and/or IDL like Thrift) and emit the corresponding safe cxx::bridge language boundary, leveraging CXX's static analysis and underlying implementation of that boundary. We are beginning to see this space explored by the [autocxx] tool, though nothing yet ready for broad use in the way that CXX on its own is. [autocxx]: https://github.com/google/autocxx But note in other ways CXX is higher level than the bindgens, with rich support for common standard library types. CXX's types serve as an intuitive vocabulary for designing a good boundary between components in different languages. ================================================ FILE: book/src/extern-c++.md ================================================ {{#title extern "C++" — Rust ♡ C++}} # extern "C++" ```rust,noplayground #[cxx::bridge] mod ffi { extern "C++" { include!("path/to/header.h"); include!("path/to/another.h"); ... } } ``` The `extern "C++"` section of a CXX bridge declares C++ types and signatures to be made available to Rust, and gives the paths of the header(s) which contain the corresponding C++ declarations. A bridge module may contain zero or more extern "C++" blocks. ## Opaque C++ types Type defined in C++ that are made available to Rust, but only behind an indirection. ```rust,noplayground # #[cxx::bridge] # mod ffi { extern "C++" { # include!("path/to/header.h"); # type MyType; type MyOtherType; } # } ``` For example in the ***[Tutorial](tutorial.md)*** we saw `BlobstoreClient` implemented as an opaque C++ type. The blobstore client was created in C++ and returned to Rust by way of a UniquePtr. **Mutability:** Unlike extern Rust types and shared types, an extern C++ type is not permitted to be passed by plain mutable reference `&mut MyType` across the FFI bridge. For mutation support, the bridge is required to use `Pin<&mut MyType>`. This is to safeguard against things like mem::swap-ing the contents of two mutable references, given that Rust doesn't have information about the size of the underlying object and couldn't invoke an appropriate C++ move constructor anyway. **Thread safety:** Be aware that CXX does not assume anything about the thread safety of your extern C++ types. In other words the `MyType` etc bindings which CXX produces for you in Rust *do not* come with `Send` and `Sync` impls. If you are sure that your C++ type satisfies the requirements of `Send` and/or `Sync` and need to leverage that fact from Rust, you must provide your own unsafe marker trait impls. ```rust,noplayground # #[cxx::bridge] # mod ffi { # extern "C++" { # include!("path/to/header.h"); # # type MyType; # } # } # /// The C++ implementation of MyType is thread safe. unsafe impl Send for ffi::MyType {} unsafe impl Sync for ffi::MyType {} ``` Take care in doing this because thread safety in C++ can be extremely tricky to assess if you are coming from a Rust background. For example the `BlobstoreClient` type in the tutorial is *not thread safe* despite doing only completely innocuous things in its implementation. Concurrent calls to the `tag` member function trigger a data race on the `blobs` map. ## Functions and member functions This largely follows the same principles as ***[extern "Rust"](extern-rust.md)*** functions and methods. In particular, any signature with a `self` parameter is interpreted as a C++ non-static member function and exposed to Rust as a method; any signature with a `#[Self = "…"]` attribute is interpreted as a C++ static member function and exposed to Rust as an associated function. The programmer **does not** need to promise that the signatures they have typed in are accurate; that would be unreasonable. CXX performs static assertions that the signatures exactly correspond with what is declared in C++. Rather, the programmer is only on the hook for things that C++'s static information is not precise enough to capture, i.e. things that would only be represented at most by comments in the C++ code unintelligible to a static assertion: namely whether the C++ function is safe or unsafe to be called from Rust. **Safety:** the extern "C++" block is responsible for deciding whether to expose each signature inside as safe-to-call or unsafe-to-call. If an extern block contains at least one safe-to-call signature, it must be written as an `unsafe extern` block, which serves as an item level unsafe block to indicate that an unchecked safety claim is being made about the contents of the block. ```rust,noplayground #[cxx::bridge] mod ffi { unsafe extern "C++" { # include!("path/to/header.h"); # fn f(); // safe to call } extern "C++" { unsafe fn g(); // unsafe to call } } ``` ## Lifetimes C++ types holding borrowed data may be described naturally in Rust by an extern type with a generic lifetime parameter. For example in the case of the following pair of types: ```cpp // header.h class Resource; class TypeContainingBorrow { TypeContainingBorrow(const Resource &res) : res(res) {} const Resource &res; }; std::shared_ptr create(const Resource &res); ``` we'd want to expose this to Rust as: ```rust,noplayground #[cxx::bridge] mod ffi { unsafe extern "C++" { # include!("path/to/header.h"); # type Resource; type TypeContainingBorrow<'a>; fn create<'a>(res: &'a Resource) -> SharedPtr>; // or with lifetime elision: fn create(res: &Resource) -> SharedPtr; } } ``` ## Reusing existing binding types Extern C++ types support a syntax for declaring that a Rust binding of the correct C++ type already exists outside of the current bridge module. This avoids generating a fresh new binding which Rust's type system would consider non-interchangeable with the first. ```rust,noplayground #[cxx::bridge(namespace = "path::to")] mod ffi { extern "C++" { type MyType = crate::existing::MyType; } extern "Rust" { fn f(x: &MyType) -> usize; } } ``` In this case rather than producing a unique new Rust type `ffi::MyType` for the Rust binding of C++'s `::path::to::MyType`, CXX will reuse the already existing binding at `crate::existing::MyType` in expressing the signature of `f` and any other uses of `MyType` within the bridge module. CXX safely validates that `crate::existing::MyType` is in fact a binding for the right C++ type `::path::to::MyType` by generating a static assertion based on `crate::existing::MyType`'s implementation of [`ExternType`], which is a trait automatically implemented by CXX for bindings that it generates but can also be manually implemented as described below. [`ExternType`]: https://docs.rs/cxx/*/cxx/trait.ExternType.html `ExternType` serves the following two related use cases. #### Safely unifying occurrences of an extern type across bridges In the following snippet, two #\[cxx::bridge\] invocations in different files (possibly different crates) both contain function signatures involving the same C++ type `example::Demo`. If both were written just containing `type Demo;`, then both macro expansions would produce their own separate Rust type called `Demo` and thus the compiler wouldn't allow us to take the `Demo` returned by `file1::ffi::create_demo` and pass it as the `Demo` argument accepted by `file2::ffi::take_ref_demo`. Instead, one of the two `Demo`s has been defined as an extern type alias of the other, making them the same type in Rust. ```rust,noplayground // file1.rs #[cxx::bridge(namespace = "example")] pub mod ffi { unsafe extern "C++" { type Demo; fn create_demo() -> UniquePtr; } } ``` ```rust,noplayground // file2.rs #[cxx::bridge(namespace = "example")] pub mod ffi { unsafe extern "C++" { type Demo = crate::file1::ffi::Demo; fn take_ref_demo(demo: &Demo); } } ``` #### Integrating with bindgen-generated or handwritten unsafe bindings Handwritten `ExternType` impls make it possible to plug in a data structure emitted by bindgen as the definition of a C++ type emitted by CXX. By writing the unsafe `ExternType` impl, the programmer asserts that the C++ namespace and type name given in the type id refers to a C++ type that is equivalent to Rust type that is the `Self` type of the impl. ```rust,noplayground mod folly_sys; // the bindgen-generated bindings use cxx::{type_id, ExternType}; unsafe impl ExternType for folly_sys::StringPiece { type Id = type_id!("folly::StringPiece"); type Kind = cxx::kind::Opaque; } #[cxx::bridge(namespace = "folly")] pub mod ffi { unsafe extern "C++" { include!("rust_cxx_bindings.h"); type StringPiece = crate::folly_sys::StringPiece; fn print_string_piece(s: &StringPiece); } } // Now if we construct a StringPiece or obtain one through one // of the bindgen-generated signatures, we are able to pass it // along to ffi::print_string_piece. ``` The `ExternType::Id` associated type encodes a type-level representation of the type's C++ namespace and type name. It will always be defined using the `type_id!` macro exposed in the cxx crate. The `ExternType::Kind` associated type will always be either [`cxx::kind::Opaque`] or [`cxx::kind::Trivial`] identifying whether a C++ type is soundly relocatable by Rust's move semantics. A C++ type is only okay to hold and pass around by value in Rust if its [move constructor is trivial] and it has no destructor. In CXX, these are called Trivial extern C++ types, while types with nontrivial move behavior or a destructor must be considered Opaque and handled by Rust only behind an indirection, such as a reference or UniquePtr. [`cxx::kind::Opaque`]: https://docs.rs/cxx/*/cxx/kind/enum.Opaque.html [`cxx::kind::Trivial`]: https://docs.rs/cxx/*/cxx/kind/enum.Trivial.html [move constructor is trivial]: https://en.cppreference.com/w/cpp/types/is_move_constructible If you believe your C++ type reflected by the ExternType impl is indeed fine to hold by value and move in Rust, you can specify: ```rust,noplayground # unsafe impl cxx::ExternType for TypeName { # type Id = cxx::type_id!("name::space::of::TypeName"); type Kind = cxx::kind::Trivial; # } ``` which will enable you to pass it into C++ functions by value, return it by value, and include it in `struct`s that you have declared to `cxx::bridge`. Your claim about the triviality of the C++ type will be checked by a `static_assert` in the generated C++ side of the binding. ## Explicit shim trait impls This is a somewhat niche feature, but important when you need it. CXX's support for C++'s std::unique\_ptr and std::vector is built on a set of internal trait impls connecting the Rust API of UniquePtr and CxxVector to underlying template instantiations performed by the C++ compiler. When reusing a binding type across multiple bridge modules as described in the previous section, you may find that your code needs some trait impls which CXX hasn't decided to generate. ```rust,noplayground #[cxx::bridge] mod ffi1 { extern "C++" { include!("path/to/header.h"); type A; type B; // Okay: CXX sees UniquePtr using a type B defined within the same // bridge, and automatically emits the right template instantiations // corresponding to std::unique_ptr. fn get_b() -> UniquePtr; } } #[cxx::bridge] mod ffi2 { extern "C++" { type A = crate::ffi1::A; // Rust trait error: CXX processing this module has no visibility into // whether template instantiations corresponding to std::unique_ptr // have already been emitted by the upstream library, so it does not // emit them here. If the upstream library does not have any signatures // involving UniquePtr, an explicit instantiation of the template // needs to be requested in one module or the other. fn get_a() -> UniquePtr; } } ``` You can request a specific template instantiation at a particular location in the Rust crate hierarchy by writing `impl UniquePtr {}` inside of the bridge module which defines `A` but does not otherwise contain any use of `UniquePtr`. ```rust,noplayground #[cxx::bridge] mod ffi1 { extern "C++" { include!("path/to/header.h"); type A; type B; fn get_b() -> UniquePtr; } impl UniquePtr {} // explicit instantiation } ``` ================================================ FILE: book/src/extern-rust.md ================================================ {{#title extern "Rust" — Rust ♡ C++}} # extern "Rust" ```rust,noplayground #[cxx::bridge] mod ffi { extern "Rust" { } } ``` The `extern "Rust"` section of a CXX bridge declares Rust types and signatures to be made available to C++. The CXX code generator uses your extern "Rust" section(s) to produce a C++ header file containing the corresponding C++ declarations. The generated header has the same path as the Rust source file containing the bridge, except with a `.rs.h` file extension. A bridge module may contain zero or more extern "Rust" blocks. ## Opaque Rust types Types defined in Rust that are made available to C++, but only behind an indirection. ```rust,noplayground # #[cxx::bridge] # mod ffi { extern "Rust" { type MyType; type MyOtherType; type OneMoreType<'a>; } # } ``` For example in the ***[Tutorial](tutorial.md)*** we saw `MultiBuf` used in this way. Rust code created the `MultiBuf`, passed a `&mut MultiBuf` to C++, and C++ later passed a `&mut MultiBuf` back across the bridge to Rust. Another example is the one on the ***[Box\](binding/box.md)*** page, which exposes the Rust standard library's `std::fs::File` to C++ as an opaque type in a similar way but with Box as the indirection rather than &mut. The types named as opaque types (`MyType` etc) refer to types in the `super` module, the parent module of the CXX bridge. You can think of an opaque type `T` as being like a re-export `use super::T` made available to C++ via the generated header. Opaque types are currently required to be [`Sized`] and [`Unpin`]. In particular, a trait object `dyn MyTrait` or slice `[T]` may not be used for an opaque Rust type. These restrictions may be lifted in the future. [`Sized`]: https://doc.rust-lang.org/std/marker/trait.Sized.html [`Unpin`]: https://doc.rust-lang.org/std/marker/trait.Unpin.html For now, types used as extern Rust types are required to be defined by the same crate that contains the bridge using them. This restriction may be lifted in the future. The bridge's parent module will contain the appropriate imports or definitions for these types. ```rust,noplayground use path::to::MyType; pub struct MyOtherType { ... } # # #[cxx::bridge] # mod ffi { # extern "Rust" { # type MyType; # type MyOtherType; # } # } ``` ## Functions Rust functions made callable to C++. Just like for opaque types, these functions refer implicitly to something in scope in the `super` module, whether defined there or imported by some `use` statement. ```rust,noplayground #[cxx::bridge] mod ffi { extern "Rust" { type MyType; fn f() -> Box; } } struct MyType(i32); fn f() -> Box { return Box::new(MyType(1)); } ``` Extern Rust function signature may consist of types defined in the bridge, primitives, and [any of these additional bindings](bindings.md). ## Methods Any signature with a `self` parameter is interpreted as a Rust method and exposed to C++ as a non-static member function. ```rust,noplayground # #[cxx::bridge] # mod ffi { extern "Rust" { type MyType; fn f(&self) -> usize; } # } ``` The `self` parameter may be a shared reference `&self`, an exclusive reference `&mut self`, or a pinned reference `self: Pin<&mut Self>`. A by-value `self` is not currently supported. If the surrounding `extern "Rust"` block contains exactly one extern type, that type is implicitly the receiver for a `&self` or `&mut self` method. If the surrounding block contains *more than one* extern type, a receiver type must be provided explicitly for the self parameter, or you can consider splitting into multiple extern blocks. ```rust,noplayground # #[cxx::bridge] # mod ffi { extern "Rust" { type First; type Second; fn bar(self: &First); fn foo(self: &mut Second); } # } ``` ## Associated functions A function with a `Self` attribute is interpreted as a Rust associated function and exposed to C++ as a static member function. These must not have a `self` argument. In the following example, the `builder` associated function is callable as `MyType::builder()` from both Rust and C++. ```rust,noplayground #[cxx::bridge] mod ffi { extern "Rust" { type MyType; type MyTypeBuilder; #[Self = "MyType"] fn builder() -> Box; } } pub struct MyType; pub struct MyTypeBuilder; impl MyType { pub fn builder() -> Box { ... } } ``` ## Functions with explicit lifetimes An extern Rust function signature is allowed to contain explicit lifetimes but in this case the function must be declared unsafe-to-call. This is pretty meaningless given we're talking about calls from C++, but at least it draws some extra attention from the caller that they may be responsible for upholding some atypical lifetime relationship. ```rust,noplayground #[cxx::bridge] mod ffi { extern "Rust" { type MyType; unsafe fn f<'a>(&'a self, s: &str) -> &'a str; } } ``` Bounds on a lifetime (like `<'a, 'b: 'a>`) are not currently supported. Nor are type parameters or where-clauses. ================================================ FILE: book/src/index.md ================================================ # CXX — safe interop between Rust and C++ This library provides a safe mechanism for calling C++ code from Rust and Rust code from C++. It carves out a regime of commonality where Rust and C++ are semantically very similar and guides the programmer to express their language boundary effectively within this regime. CXX fills in the low level stuff so that you get a safe binding, preventing the pitfalls of doing a foreign function interface over unsafe C-style signatures.
    From a high level description of the language boundary, CXX uses static analysis of the types and function signatures to protect both Rust's and C++'s invariants. Then it uses a pair of code generators to implement the boundary efficiently on both sides together with any necessary static assertions for later in the build process to verify correctness. The resulting FFI bridge operates at zero or negligible overhead, i.e. no copying, no serialization, no memory allocation, no runtime checks needed. The FFI signatures are able to use native data structures from whichever side they please. In addition, CXX provides builtin bindings for key standard library types like strings, vectors, Box, unique\_ptr, etc to expose an idiomatic API on those types to the other language. ## Example In this example we are writing a Rust application that calls a C++ client of a large-file blobstore service. The blobstore supports a `put` operation for a discontiguous buffer upload. For example we might be uploading snapshots of a circular buffer which would tend to consist of 2 pieces, or fragments of a file spread across memory for some other reason (like a rope data structure). ```rust,noplayground #[cxx::bridge] mod ffi { extern "Rust" { type MultiBuf; fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } unsafe extern "C++" { include!("example/include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr; fn put(self: &BlobstoreClient, buf: &mut MultiBuf) -> Result; } } ``` Now we simply provide Rust definitions of all the things in the `extern "Rust"` block and C++ definitions of all the things in the `extern "C++"` block, and get to call back and forth safely. The [***Tutorial***](tutorial.md) chapter walks through a fleshed out version of this blobstore example in full detail, including all of the Rust code and all of the C++ code. The code is also provided in runnable form in the *demo* directory of . To try it out, run `cargo run` from that directory. - [demo/src/main.rs](https://github.com/dtolnay/cxx/blob/master/demo/src/main.rs) - [demo/include/blobstore.h](https://github.com/dtolnay/cxx/blob/master/demo/include/blobstore.h) - [demo/src/blobstore.cc](https://github.com/dtolnay/cxx/blob/master/demo/src/blobstore.cc) The key takeaway, which is enabled by the CXX library, is that the Rust code in main.rs is 100% ordinary safe Rust code working idiomatically with Rust types while the C++ code in blobstore.cc is 100% ordinary C++ code working idiomatically with C++ types. The Rust code feels like Rust and the C++ code feels like C++, not like C-style "FFI glue".
    ***Chapter outline:** See the hamburger menu in the top left if you are on a small screen and it didn't open with a sidebar by default.* ================================================ FILE: book/src/reference.md ================================================ {{#title The bridge module — Rust ♡ C++}} # The bridge module reference The ***[Core concepts](concepts.md)*** in chapter 2 covered the high level model that CXX uses to represent a language boundary. This chapter builds on that one to document an exhaustive reference on the syntax and functionality of \#\[cxx::bridge\]. - ***[extern "Rust"](extern-rust.md)*** — exposing opaque Rust types, Rust functions, Rust methods to C++; functions with lifetimes. - ***[extern "C++"](extern-c++.md)*** — binding opaque C++ types, C++ functions, C++ member functions; sharing an opaque type definition across multiple bridge modules or different crates; using bindgen-generated data structures across a CXX bridge; Rust orphan-rule-compatible way to request that particular glue code be emitted in a specific bridge module. - ***[Shared types](shared.md)*** — shared structs; shared enums; using Rust as source of truth vs C++ as source of truth. - ***[Attributes](attributes.md)*** — working with namespaces; giving functions a different name in their non-native language. - ***[Async functions](async.md)*** — integrating async C++ with async Rust. - ***[Error handling](binding/result.md)*** — representing fallibility on the language boundary; accessing a Rust error message from C++; customizing the set of caught exceptions and their conversion to a Rust error message. ================================================ FILE: book/src/shared.md ================================================ {{#title Shared types — Rust ♡ C++}} # Shared types Shared types enable *both* languages to have visibility into the internals of a type. This is in contrast to opaque Rust types and opaque C++ types, for which only one side gets to manipulate the internals. Unlike opaque types, the FFI bridge is allowed to pass and return shared types by value. The order in which shared types are written is not important. C++ is order sensitive but CXX will topologically sort and forward-declare your types as necessary. ## Shared structs and enums For enums, only C-like a.k.a. unit variants are currently supported. ```rust,noplayground #[cxx::bridge] mod ffi { struct PlayingCard { suit: Suit, value: u8, // A=1, J=11, Q=12, K=13 } enum Suit { Clubs, Diamonds, Hearts, Spades, } unsafe extern "C++" { fn deck() -> Vec; fn sort(cards: &mut Vec); } } ``` ## The generated data structures Shared structs compile to an aggregate-initialization compatible C++ struct. Shared enums compile to a C++ `enum class` with a sufficiently sized integral base type decided by CXX. ```cpp // generated header struct PlayingCard final { Suit suit; uint8_t value; }; enum class Suit : uint8_t { Clubs = 0, Diamonds = 1, Hearts = 2, Spades = 3, }; ``` Because it is not UB in C++ for an `enum class` to hold a value different from all of the listed variants, we use a Rust representation for shared enums that is compatible with this. The API you'll get is something like: ```rust,noplayground #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] pub struct Suit { pub repr: u8, } #[allow(non_upper_case_globals)] impl Suit { pub const Clubs: Self = Suit { repr: 0 }; pub const Diamonds: Self = Suit { repr: 1 }; pub const Hearts: Self = Suit { repr: 2 }; pub const Spades: Self = Suit { repr: 3 }; } ``` Notice you're free to treat the enum as an integer in Rust code via the public `repr` field. Pattern matching with `match` still works but will require you to write wildcard arms to handle the situation of an enum value that is not one of the listed variants. ```rust,noplayground fn main() { let suit: Suit = /*...*/; match suit { Suit::Clubs => ..., Suit::Diamonds => ..., Suit::Hearts => ..., Suit::Spades => ..., _ => ..., // fallback arm } } ``` If a shared struct has generic lifetime parameters, the lifetimes are simply not represented on the C++ side. C++ code will need care when working with borrowed data (as usual in C++). ```rust,noplayground #[cxx::bridge] mod ffi { struct Borrowed<'a> { flags: &'a [&'a str], } } ``` ```cpp // generated header struct Borrowed final { rust::Slice flags; }; ``` ## Enum discriminants You may provide explicit discriminants for some or all of the enum variants, in which case those numbers will be propagated into the generated C++ `enum class`. ```rust,noplayground #[cxx::bridge] mod ffi { enum SmallPrime { Two = 2, Three = 3, Five = 5, Seven = 7, } } ``` Variants without an explicit discriminant are assigned the previous discriminant plus 1. If the first variant has not been given an explicit discriminant, it is assigned discriminant 0. By default CXX represents your enum using the smallest integer type capable of fitting all the discriminants (whether explicit or implicit). If you need a different representation for reasons, provide a `repr` attribute. ```rust,noplayground #[cxx::bridge] mod ffi { #[repr(i32)] enum Enum { Zero, One, Five = 5, Six, } } ``` ```cpp // generated header enum class Enum : int32_t { Zero = 0, One = 1, Five = 5, Six = 6, }; ``` ## Extern enums If you need to interoperate with an already existing enum for which an existing C++ definition is the source of truth, make sure that definition is provided by some header in the bridge and then declare your enum *additionally* as an extern C++ type. ```rust,noplayground #[cxx::bridge] mod ffi { enum Enum { Yes, No, } extern "C++" { include!("path/to/the/header.h"); type Enum; } } ``` CXX will recognize this pattern and, instead of generating a C++ definition of the enum, will instead generate C++ static assertions asserting that the variants and discriminant values and integer representation written in Rust all correctly match the existing C++ enum definition. Extern enums support all the same features as ordinary shared enums (explicit discriminants, repr). Again, CXX will static assert that all of those things you wrote are correct. ## Derives The following standard traits are supported in `derive(...)` within the CXX bridge module. - `Clone` - `Copy` - `Debug` - `Default` - `Eq` - `Hash` - `Ord` - `PartialEq` - `PartialOrd` - `BitAnd` (enums only) - `BitOr` (enums only) - `BitXor` (enums only) Note that shared enums automatically always come with impls of `Copy`, `Clone`, `Eq`, and `PartialEq`, so you're free to omit those derives on an enum. ```rust,noplayground #[cxx::bridge] mod ffi { #[derive(Clone, Debug, Hash)] struct ExampleStruct { x: u32, s: String, } #[derive(Hash, Ord, PartialOrd)] enum ExampleEnum { Yes, No, } } ``` The derives naturally apply to *both* the Rust data type *and* the corresponding C++ data type: - `Hash` gives you a specialization of [`template <> struct std::hash`][hash] in C++ - `PartialEq` produces `operator==` and `operator!=` - `PartialOrd` produces `operator<`, `operator<=`, `operator>`, `operator>=` - `BitAnd` produces `operator&` - `BitOr` produces `operator|` - `BitXor` produces `operator^` [hash]: https://en.cppreference.com/w/cpp/utility/hash ## Alignment The attribute `repr(align(…))` sets a minimum required alignment for a shared struct. The alignment value must be a power of two in the range 20 to 213. This turns into an [`alignas`] specifier in C++. [`alignas`]: https://en.cppreference.com/w/cpp/language/alignas.html ```rust,noplayground #[cxx::bridge] mod ffi { #[repr(align(4))] struct ExampleStruct { b: [u8; 4], } } ``` ================================================ FILE: book/src/tutorial.md ================================================ {{#title Tutorial — Rust ♡ C++}} # Tutorial: CXX blobstore client This example walks through a Rust application that calls into a C++ client of a blobstore service. In fact we'll see calls going in both directions: Rust to C++ as well as C++ to Rust. For your own use case it may be that you need just one of these directions. All of the code involved in the example is shown on this page, but it's also provided in runnable form in the *demo* directory of . To try it out directly, run `cargo run` from that directory. This tutorial assumes you've read briefly about **shared structs**, **opaque types**, and **functions** in the [*Core concepts*](concepts.md) page. ## Creating the project We'll use Cargo, which is the build system commonly used by open source Rust projects. (CXX works with other build systems too; refer to chapter 5.) Create a blank Cargo project: `mkdir cxx-demo`; `cd cxx-demo`; `cargo init`. Edit the Cargo.toml to add a dependency on the `cxx` crate: ```toml,hidelines=... # Cargo.toml ...[package] ...name = "cxx-demo" ...version = "0.1.0" ...edition = "2021" [dependencies] cxx = "1.0" ``` We'll revisit this Cargo.toml later when we get to compiling some C++ code. ## Defining the language boundary CXX relies on a description of the function signatures that will be exposed from each language to the other. You provide this description using `extern` blocks in a Rust module annotated with the `#[cxx::bridge]` attribute macro. We'll open with just the following at the top of src/main.rs and walk through each item in detail. ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { } # # fn main() {} ``` The contents of this module will be everything that needs to be agreed upon by both sides of the FFI boundary. ## Calling a C++ function from Rust Let's obtain an instance of the C++ blobstore client, a class `BlobstoreClient` defined in C++. We'll treat `BlobstoreClient` as an *opaque type* in CXX's classification so that Rust does not need to assume anything about its implementation, not even its size or alignment. In general, a C++ type might have a move-constructor which is incompatible with Rust's move semantics, or may hold internal references which cannot be modeled by Rust's borrowing system. Though there are alternatives, the easiest way to not care about any such thing on an FFI boundary is to require no knowledge about a type by treating it as opaque. Opaque types may only be manipulated behind an indirection such as a reference `&`, a Rust `Box`, or a `UniquePtr` (Rust binding of `std::unique_ptr`). We'll add a function through which C++ can return a `std::unique_ptr` to Rust. ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { unsafe extern "C++" { include!("cxx-demo/include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr; } } fn main() { let client = ffi::new_blobstore_client(); } ``` The nature of `unsafe` extern blocks is clarified in more detail in the [*extern "C++"*](extern-c++.md) chapter. In brief: the programmer is **not** promising that the signatures they have typed in are accurate; that would be unreasonable. CXX performs static assertions that the signatures exactly match what is declared in C++. Rather, the programmer is only on the hook for things that C++'s semantics are not precise enough to capture, i.e. things that would only be represented at most by comments in the C++ code. In this case, it's whether `new_blobstore_client` is safe or unsafe to call. If that function said something like "must be called at most once or we'll stomp yer memery", Rust would instead want to expose it as `unsafe fn new_blobstore_client`, this time inside a safe `extern "C++"` block because the programmer is no longer on the hook for any safety claim about the signature. If you build this file right now with `cargo build`, it won't build because we haven't written a C++ implementation of `new_blobstore_client` nor instructed Cargo about how to link it into the resulting binary. You'll see an error from the linker like this: ```console error: linking with `cc` failed: exit code: 1 | = /bin/ld: target/debug/deps/cxx-demo-7cb7fddf3d67d880.rcgu.o: in function `cxx_demo::ffi::new_blobstore_client': src/main.rs:1: undefined reference to `cxxbridge1$new_blobstore_client' collect2: error: ld returned 1 exit status ``` ## Adding in the C++ code In CXX's integration with Cargo, all #include paths begin with a crate name by default (when not explicitly selected otherwise by a crate; see `CFG.include_prefix` in chapter 5). That's why we see `include!("cxx-demo/include/blobstore.h")` above — we'll be putting the C++ header at relative path `include/blobstore.h` within the Rust crate. If your crate is named something other than `cxx-demo` according to the `name` field in Cargo.toml, you will need to use that name everywhere in place of `cxx-demo` throughout this tutorial. ```cpp // include/blobstore.h #pragma once #include class BlobstoreClient { public: BlobstoreClient(); }; std::unique_ptr new_blobstore_client(); ``` ```cpp // src/blobstore.cc #include "cxx-demo/include/blobstore.h" BlobstoreClient::BlobstoreClient() {} std::unique_ptr new_blobstore_client() { return std::unique_ptr(new BlobstoreClient()); } ``` Using `std::make_unique` would work too, as long as you pass `std("c++14")` to the C++ compiler as described later on. The placement in *include/* and *src/* is not significant; you can place C++ code anywhere else in the crate as long as you use the right paths throughout the tutorial. Be aware that *CXX does not look at any of these files.* You're free to put arbitrary C++ code in here, #include your own libraries, etc. All we do is emit static assertions against what you provide in the headers. ## Compiling the C++ code with Cargo Cargo has a [build scripts] feature suitable for compiling non-Rust code. We need to introduce a new build-time dependency on CXX's C++ code generator in Cargo.toml: ```toml,hidelines=... # Cargo.toml ...[package] ...name = "cxx-demo" ...version = "0.1.0" ...edition = "2021" [dependencies] cxx = "1.0" [build-dependencies] cxx-build = "1.0" ``` Then add a build.rs build script adjacent to Cargo.toml to run the cxx-build code generator and C++ compiler. The relevant arguments are the path to the Rust source file containing the cxx::bridge language boundary definition, and the paths to any additional C++ source files to be compiled during the Rust crate's build. ```rust,noplayground // build.rs fn main() { cxx_build::bridge("src/main.rs") .file("src/blobstore.cc") .compile("cxx-demo"); println!("cargo:rerun-if-changed=src/blobstore.cc"); println!("cargo:rerun-if-changed=include/blobstore.h"); } ``` This build.rs would also be where you set up C++ compiler flags, for example if you'd like to have access to `std::make_unique` from C++14. See the page on ***[Cargo-based builds](build/cargo.md)*** for more details about CXX's Cargo integration. ```rust,noplayground # // build.rs # # fn main() { cxx_build::bridge("src/main.rs") .file("src/blobstore.cc") .std("c++14") .compile("cxx-demo"); # } ``` [build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html The project should now build and run successfully, though not do anything useful yet. ```console cxx-demo$ cargo run Compiling cxx-demo v0.1.0 Finished dev [unoptimized + debuginfo] target(s) in 0.34s Running `target/debug/cxx-demo` cxx-demo$ ``` ## Calling a Rust function from C++ Our C++ blobstore supports a `put` operation for a discontiguous buffer upload. For example we might be uploading snapshots of a circular buffer which would tend to consist of 2 pieces, or fragments of a file spread across memory for some other reason (like a rope data structure). We'll express this by handing off an iterator over contiguous borrowed chunks. This loosely resembles the API of the widely used `bytes` crate's `Buf` trait. During a `put`, we'll make C++ call back into Rust to obtain contiguous chunks of the upload (all with no copying or allocation on the language boundary). In reality the C++ client might contain some sophisticated batching of chunks and/or parallel uploading that all of this ties into. ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { extern "Rust" { type MultiBuf; fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } unsafe extern "C++" { include!("cxx-demo/include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr; fn put(&self, parts: &mut MultiBuf) -> u64; } } # # fn main() { # let client = ffi::new_blobstore_client(); # } ``` Any signature having a `self` parameter (the Rust name for C++'s `this`) is considered a method / non-static member function. If there is only one `type` in the surrounding extern block, it'll be a method of that type. If there is more than one `type`, you can disambiguate which one a method belongs to by writing `self: &BlobstoreClient` in the argument list. As usual, now we need to provide Rust definitions of everything declared by the `extern "Rust"` block and a C++ definition of the new signature declared by the `extern "C++"` block. ```rust,noplayground // src/main.rs # # #[cxx::bridge] # mod ffi { # extern "Rust" { # type MultiBuf; # # fn next_chunk(buf: &mut MultiBuf) -> &[u8]; # } # # unsafe extern "C++" { # include!("cxx-demo/include/blobstore.h"); # # type BlobstoreClient; # # fn new_blobstore_client() -> UniquePtr; # fn put(&self, parts: &mut MultiBuf) -> u64; # } # } // An iterator over contiguous chunks of a discontiguous file object. Toy // implementation uses a Vec> but in reality this might be iterating // over some more complex Rust data structure like a rope, or maybe loading // chunks lazily from somewhere. pub struct MultiBuf { chunks: Vec>, pos: usize, } pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] { let next = buf.chunks.get(buf.pos); buf.pos += 1; next.map_or(&[], Vec::as_slice) } # # fn main() { # let client = ffi::new_blobstore_client(); # } ``` ```cpp,hidelines=... // include/blobstore.h ...#pragma once ...#include ... struct MultiBuf; class BlobstoreClient { public: BlobstoreClient(); uint64_t put(MultiBuf &buf) const; }; ... ...std::unique_ptr new_blobstore_client(); ``` In blobstore.cc we're able to call the Rust `next_chunk` function, exposed to C++ by a header `main.rs.h` generated by the CXX code generator. In CXX's Cargo integration this generated header has a path containing the crate name, the relative path of the Rust source file within the crate, and a `.rs.h` extension. ```cpp,hidelines=... // src/blobstore.cc #include "cxx-demo/include/blobstore.h" #include "cxx-demo/src/main.rs.h" #include #include ... ...BlobstoreClient::BlobstoreClient() {} ... ...std::unique_ptr new_blobstore_client() { ... return std::make_unique(); ...} // Upload a new blob and return a blobid that serves as a handle to the blob. uint64_t BlobstoreClient::put(MultiBuf &buf) const { // Traverse the caller's chunk iterator. std::string contents; while (true) { auto chunk = next_chunk(buf); if (chunk.size() == 0) { break; } contents.append(reinterpret_cast(chunk.data()), chunk.size()); } // Pretend we did something useful to persist the data. auto blobid = std::hash{}(contents); return blobid; } ``` This is now ready to use. :) ```rust,noplayground // src/main.rs # # #[cxx::bridge] # mod ffi { # extern "Rust" { # type MultiBuf; # # fn next_chunk(buf: &mut MultiBuf) -> &[u8]; # } # # unsafe extern "C++" { # include!("cxx-demo/include/blobstore.h"); # # type BlobstoreClient; # # fn new_blobstore_client() -> UniquePtr; # fn put(&self, parts: &mut MultiBuf) -> u64; # } # } # # pub struct MultiBuf { # chunks: Vec>, # pos: usize, # } # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] { # let next = buf.chunks.get(buf.pos); # buf.pos += 1; # next.map_or(&[], Vec::as_slice) # } fn main() { let client = ffi::new_blobstore_client(); // Upload a blob. let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; let mut buf = MultiBuf { chunks, pos: 0 }; let blobid = client.put(&mut buf); println!("blobid = {blobid}"); } ``` ```console cxx-demo$ cargo run Compiling cxx-demo v0.1.0 Finished dev [unoptimized + debuginfo] target(s) in 0.41s Running `target/debug/cxx-demo` blobid = 9851996977040795552 ``` ## Interlude: What gets generated? For the curious, it's easy to look behind the scenes at what CXX has done to make these function calls work. You shouldn't need to do this during normal usage of CXX, but for the purpose of this tutorial it can be educative. CXX comprises *two* code generators: a Rust one (which is the cxx::bridge attribute procedural macro) and a C++ one. ### Rust generated code It's easiest to view the output of the procedural macro by installing [cargo-expand]. Then run `cargo expand ::ffi` to macro-expand the `mod ffi` module. [cargo-expand]: https://github.com/dtolnay/cargo-expand ```console cxx-demo$ cargo install cargo-expand cxx-demo$ cargo expand ::ffi ``` You'll see some deeply unpleasant code involving `#[repr(C)]`, `#[link_name]`, and `#[export_name]`. ### C++ generated code For debugging convenience, `cxx_build` links all generated C++ code into Cargo's target directory under *target/cxxbridge/*. ```console cxx-demo$ exa -T target/cxxbridge/ target/cxxbridge ├── cxx-demo │ └── src │ ├── main.rs.cc -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/sources/cxx-demo/src/main.rs.cc │ └── main.rs.h -> ../../../debug/build/cxx-demo-11c6f678ce5c3437/out/cxxbridge/include/cxx-demo/src/main.rs.h └── rust └── cxx.h -> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cxx-1.0.0/include/cxx.h ``` In those files you'll see declarations or templates of any CXX Rust types present in your language boundary (like `rust::Slice` for `&[T]`) and `extern "C"` signatures corresponding to your extern functions. If it fits your workflow better, the CXX C++ code generator is also available as a standalone executable which outputs generated code to stdout. ```console cxx-demo$ cargo install cxxbridge-cmd cxx-demo$ cxxbridge src/main.rs ``` ## Shared data structures So far the calls in both directions above only used **opaque types**, not **shared structs**. Shared structs are data structures whose complete definition is visible to both languages, making it possible to pass them by value across the language boundary. Shared structs translate to a C++ aggregate-initialization compatible struct exactly matching the layout of the Rust one. As the last step of this demo, we'll use a shared struct `BlobMetadata` to pass metadata about blobs between our Rust application and C++ blobstore client. ```rust,noplayground // src/main.rs #[cxx::bridge] mod ffi { struct BlobMetadata { size: usize, tags: Vec, } extern "Rust" { // ... # type MultiBuf; # # fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } unsafe extern "C++" { // ... # include!("cxx-demo/include/blobstore.h"); # # type BlobstoreClient; # # fn new_blobstore_client() -> UniquePtr; # fn put(&self, parts: &mut MultiBuf) -> u64; fn tag(&self, blobid: u64, tag: &str); fn metadata(&self, blobid: u64) -> BlobMetadata; } } # # pub struct MultiBuf { # chunks: Vec>, # pos: usize, # } # pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] { # let next = buf.chunks.get(buf.pos); # buf.pos += 1; # next.map_or(&[], Vec::as_slice) # } fn main() { let client = ffi::new_blobstore_client(); // Upload a blob. let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; let mut buf = MultiBuf { chunks, pos: 0 }; let blobid = client.put(&mut buf); println!("blobid = {blobid}"); // Add a tag. client.tag(blobid, "rust"); // Read back the tags. let metadata = client.metadata(blobid); println!("tags = {:?}", metadata.tags); } ``` ```cpp,hidelines=... // include/blobstore.h #pragma once #include "rust/cxx.h" ...#include struct MultiBuf; struct BlobMetadata; class BlobstoreClient { public: BlobstoreClient(); uint64_t put(MultiBuf &buf) const; void tag(uint64_t blobid, rust::Str tag) const; BlobMetadata metadata(uint64_t blobid) const; private: class impl; std::shared_ptr impl; }; ... ...std::unique_ptr new_blobstore_client(); ``` ```cpp,hidelines=... // src/blobstore.cc #include "cxx-demo/include/blobstore.h" #include "cxx-demo/src/main.rs.h" #include #include #include #include #include // Toy implementation of an in-memory blobstore. // // In reality the implementation of BlobstoreClient could be a large // complex C++ library. class BlobstoreClient::impl { friend BlobstoreClient; using Blob = struct { std::string data; std::set tags; }; std::unordered_map blobs; }; BlobstoreClient::BlobstoreClient() : impl(new class BlobstoreClient::impl) {} ... ...// Upload a new blob and return a blobid that serves as a handle to the blob. ...uint64_t BlobstoreClient::put(MultiBuf &buf) const { ... // Traverse the caller's chunk iterator. ... std::string contents; ... while (true) { ... auto chunk = next_chunk(buf); ... if (chunk.size() == 0) { ... break; ... } ... contents.append(reinterpret_cast(chunk.data()), chunk.size()); ... } ... ... // Insert into map and provide caller the handle. ... auto blobid = std::hash{}(contents); ... impl->blobs[blobid] = {std::move(contents), {}}; ... return blobid; ...} // Add tag to an existing blob. void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const { impl->blobs[blobid].tags.emplace(tag); } // Retrieve metadata about a blob. BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const { BlobMetadata metadata{}; auto blob = impl->blobs.find(blobid); if (blob != impl->blobs.end()) { metadata.size = blob->second.data.size(); std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(), [&](auto &t) { metadata.tags.emplace_back(t); }); } return metadata; } ... ...std::unique_ptr new_blobstore_client() { ... return std::make_unique(); ...} ``` ```console cxx-demo$ cargo run Running `target/debug/cxx-demo` blobid = 9851996977040795552 tags = ["rust"] ``` *You've now seen all the code involved in the tutorial. It's available all together in runnable form in the* demo *directory of . You can run it directly without stepping through the steps above by running `cargo run` from that directory.*
    # Takeaways The key contribution of CXX is it gives you Rust–C++ interop in which *all* of the Rust side of the code you write *really* looks like you are just writing normal Rust, and the C++ side *really* looks like you are just writing normal C++. You've seen in this tutorial that none of the code involved feels like C or like the usual perilous "FFI glue" prone to leaks or memory safety flaws. An expressive system of opaque types, shared types, and key standard library type bindings enables API design on the language boundary that captures the proper ownership and borrowing contracts of the interface. CXX plays to the strengths of the Rust type system *and* C++ type system *and* the programmer's intuitions. An individual working on the C++ side without a Rust background, or the Rust side without a C++ background, will be able to apply all their usual intuitions and best practices about development in their language to maintain a correct FFI.

    ================================================ FILE: book/theme/head.hbs ================================================ ================================================ FILE: build.rs ================================================ #![expect(unexpected_cfgs)] use std::env; use std::path::{Path, PathBuf}; use std::process::Command; fn main() { let manifest_dir_opt = env::var_os("CARGO_MANIFEST_DIR").map(PathBuf::from); let manifest_dir = manifest_dir_opt.as_deref().unwrap_or(Path::new("")); cc::Build::new() .file(manifest_dir.join("src/cxx.cc")) .cpp(true) .cpp_link_stdlib(None) // linked via link-cplusplus crate .std(cxxbridge_flags::STD) .warnings_into_errors(cfg!(deny_warnings)) .compile("cxxbridge1"); println!("cargo:rerun-if-changed=src/cxx.cc"); println!("cargo:rerun-if-changed=include/cxx.h"); println!("cargo:rustc-cfg=built_with_cargo"); if let Some(manifest_dir) = &manifest_dir_opt { let cxx_h = manifest_dir.join("include").join("cxx.h"); println!("cargo:HEADER={}", cxx_h.to_string_lossy()); } println!("cargo:rustc-check-cfg=cfg(built_with_cargo)"); println!("cargo:rustc-check-cfg=cfg(compile_error_if_alloc)"); println!("cargo:rustc-check-cfg=cfg(compile_error_if_std)"); println!("cargo:rustc-check-cfg=cfg(cxx_experimental_no_alloc)"); println!("cargo:rustc-check-cfg=cfg(skip_ui_tests)"); if let Some(rustc) = rustc_version() { if rustc.minor < 85 { println!("cargo:warning=The cxx crate requires a rustc version 1.85.0 or newer."); println!( "cargo:warning=You appear to be building with: {}", rustc.version, ); } } if let (Some(manifest_links), Some(pkg_version_major)) = ( env::var_os("CARGO_MANIFEST_LINKS"), env::var_os("CARGO_PKG_VERSION_MAJOR"), ) { assert_eq!( manifest_links, *format!("cxxbridge{}", pkg_version_major.to_str().unwrap()), ); } } struct RustVersion { version: String, minor: u32, } fn rustc_version() -> Option { let rustc = env::var_os("RUSTC")?; let output = Command::new(rustc).arg("--version").output().ok()?; let version = String::from_utf8(output.stdout).ok()?; let mut pieces = version.split('.'); if pieces.next() != Some("rustc 1") { return None; } let minor = pieces.next()?.parse().ok()?; Some(RustVersion { version, minor }) } ================================================ FILE: compile_flags.txt ================================================ -std=c++20 ================================================ FILE: demo/BUCK ================================================ load("//tools/buck:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), edition = "2021", deps = [ ":blobstore-sys", ":bridge", "//:cxx", ], ) rust_cxx_bridge( name = "bridge", src = "src/main.rs", deps = [":blobstore-include"], ) cxx_library( name = "blobstore-sys", srcs = ["src/blobstore.cc"], preferred_linkage = "static", deps = [ ":blobstore-include", ":bridge/include", ], ) cxx_library( name = "blobstore-include", exported_deps = ["//:core"], exported_headers = ["include/blobstore.h"], ) ================================================ FILE: demo/BUILD.bazel ================================================ load("@rules_cc//cc:defs.bzl", "cc_library") load("@rules_rust//rust:defs.bzl", "rust_binary") load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") rust_binary( name = "demo", srcs = glob(["src/**/*.rs"]), edition = "2021", deps = [ ":blobstore-sys", ":bridge", "//:cxx", ], ) rust_cxx_bridge( name = "bridge", src = "src/main.rs", deps = [":blobstore-include"], ) cc_library( name = "blobstore-sys", srcs = ["src/blobstore.cc"], linkstatic = True, deps = [ ":blobstore-include", ":bridge/include", ], ) cc_library( name = "blobstore-include", hdrs = ["include/blobstore.h"], deps = ["//:core"], ) ================================================ FILE: demo/Cargo.toml ================================================ [package] name = "demo" version = "0.0.0" authors = ["David Tolnay "] description = "Toy project from https://github.com/dtolnay/cxx" edition = "2021" license = "MIT OR Apache-2.0" publish = false repository = "https://github.com/dtolnay/cxx" [dependencies] cxx = "1.0" [build-dependencies] cxx-build = "1.0" ================================================ FILE: demo/build.rs ================================================ fn main() { cxx_build::bridge("src/main.rs") .file("src/blobstore.cc") .std("c++14") .compile("cxxbridge-demo"); println!("cargo:rerun-if-changed=src/blobstore.cc"); println!("cargo:rerun-if-changed=include/blobstore.h"); } ================================================ FILE: demo/include/blobstore.h ================================================ #pragma once #include "rust/cxx.h" #include namespace org { namespace blobstore { struct MultiBuf; struct BlobMetadata; class BlobstoreClient { public: BlobstoreClient(); uint64_t put(MultiBuf &buf) const; void tag(uint64_t blobid, rust::Str tag) const; BlobMetadata metadata(uint64_t blobid) const; private: class impl; std::shared_ptr impl; }; std::unique_ptr new_blobstore_client(); } // namespace blobstore } // namespace org ================================================ FILE: demo/src/blobstore.cc ================================================ #include "demo/include/blobstore.h" #include "demo/src/main.rs.h" #include #include #include #include #include namespace org { namespace blobstore { // Toy implementation of an in-memory blobstore. // // In reality the implementation of BlobstoreClient could be a large complex C++ // library. class BlobstoreClient::impl { friend BlobstoreClient; using Blob = struct { std::string data; std::set tags; }; std::unordered_map blobs; }; BlobstoreClient::BlobstoreClient() : impl(new class BlobstoreClient::impl) {} // Upload a new blob and return a blobid that serves as a handle to the blob. uint64_t BlobstoreClient::put(MultiBuf &buf) const { std::string contents; // Traverse the caller's chunk iterator. // // In reality there might be sophisticated batching of chunks and/or parallel // upload implemented by the blobstore's C++ client. while (true) { auto chunk = next_chunk(buf); if (chunk.size() == 0) { break; } contents.append(reinterpret_cast(chunk.data()), chunk.size()); } // Insert into map and provide caller the handle. auto blobid = std::hash{}(contents); impl->blobs[blobid] = {std::move(contents), {}}; return blobid; } // Add tag to an existing blob. void BlobstoreClient::tag(uint64_t blobid, rust::Str tag) const { impl->blobs[blobid].tags.emplace(tag); } // Retrieve metadata about a blob. BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const { BlobMetadata metadata{}; auto blob = impl->blobs.find(blobid); if (blob != impl->blobs.end()) { metadata.size = blob->second.data.size(); std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(), [&](auto &t) { metadata.tags.emplace_back(t); }); } return metadata; } std::unique_ptr new_blobstore_client() { return std::make_unique(); } } // namespace blobstore } // namespace org ================================================ FILE: demo/src/main.rs ================================================ #[cxx::bridge(namespace = "org::blobstore")] mod ffi { // Shared structs with fields visible to both languages. struct BlobMetadata { size: usize, tags: Vec, } // Rust types and signatures exposed to C++. extern "Rust" { type MultiBuf; fn next_chunk(buf: &mut MultiBuf) -> &[u8]; } // C++ types and signatures exposed to Rust. unsafe extern "C++" { include!("demo/include/blobstore.h"); type BlobstoreClient; fn new_blobstore_client() -> UniquePtr; fn put(&self, parts: &mut MultiBuf) -> u64; fn tag(&self, blobid: u64, tag: &str); fn metadata(&self, blobid: u64) -> BlobMetadata; } } // An iterator over contiguous chunks of a discontiguous file object. // // Toy implementation uses a Vec> but in reality this might be iterating // over some more complex Rust data structure like a rope, or maybe loading // chunks lazily from somewhere. pub struct MultiBuf { chunks: Vec>, pos: usize, } pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] { let next = buf.chunks.get(buf.pos); buf.pos += 1; next.map_or(&[], Vec::as_slice) } fn main() { let client = ffi::new_blobstore_client(); // Upload a blob. let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; let mut buf = MultiBuf { chunks, pos: 0 }; let blobid = client.put(&mut buf); println!("blobid = {blobid}"); // Add a tag. client.tag(blobid, "rust"); // Read back the tags. let metadata = client.metadata(blobid); println!("tags = {:?}", metadata.tags); } ================================================ FILE: flags/Cargo.toml ================================================ [package] name = "cxxbridge-flags" version = "1.0.194" authors = ["David Tolnay "] categories = ["development-tools::ffi", "compilers"] description = "Compiler configuration of the `cxx` crate (implementation detail)" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/cxx" rust-version = "1.85" [features] default = [] # c++11 "c++14" = [] "c++17" = [] "c++20" = [] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--generate-macro-expansion", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", ] ================================================ FILE: flags/src/impl.rs ================================================ #[allow(unused_assignments, unused_mut, unused_variables)] pub const STD: &str = { let mut flag = "c++11"; #[cfg(feature = "c++14")] (flag = "c++14"); #[cfg(feature = "c++17")] (flag = "c++17"); #[cfg(feature = "c++20")] (flag = "c++20"); flag }; ================================================ FILE: flags/src/lib.rs ================================================ //! This crate is an implementation detail of the `cxx` and `cxx-build` crates, //! and does not expose any public API. mod r#impl; #[doc(hidden)] pub use r#impl::*; ================================================ FILE: gen/README.md ================================================ This directory contains CXX's C++ code generator. This code generator has two public frontends, one a command-line application (binary) in the *cmd* directory and the other a library intended to be used from a build.rs in the *build* directory. There's also a 'lib' frontend which is intended to allow higher level code generators to embed cxx. This is not yet recommended for general use. ================================================ FILE: gen/build/Cargo.toml ================================================ [package] name = "cxx-build" version = "1.0.194" authors = ["David Tolnay "] categories = ["development-tools::build-utils", "development-tools::ffi"] description = "C++ code generator for integrating `cxx` crate into a Cargo build." documentation = "https://docs.rs/cxx-build" edition = "2021" exclude = ["build.rs"] homepage = "https://cxx.rs" keywords = ["ffi", "build-dependencies"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/cxx" rust-version = "1.85" [features] parallel = ["cc/parallel"] [dependencies] cc = "1.0.101" codespan-reporting = "0.13.1" indexmap = "2.9.0" proc-macro2 = { version = "1.0.74", default-features = false, features = ["span-locations"] } quote = { version = "1.0.35", default-features = false } scratch = "1.0.5" syn = { version = "2.0.46", default-features = false, features = ["clone-impls", "full", "parsing", "printing"] } [dev-dependencies] cxx = { version = "1.0", path = "../.." } cxx-gen = { version = "0.7", path = "../lib" } pkg-config = "0.3.27" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--generate-macro-expansion", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", ] ================================================ FILE: gen/build/build.rs ================================================ include!("../../tools/cargo/build.rs"); ================================================ FILE: gen/build/src/cargo.rs ================================================ use crate::gen::{CfgEvaluator, CfgResult}; use std::borrow::Borrow; use std::cmp::Ordering; use std::collections::{BTreeMap as Map, BTreeSet as Set}; use std::env; use std::ptr; use std::sync::OnceLock; static ENV: OnceLock = OnceLock::new(); struct CargoEnv { features: Set, cfgs: Map, } pub(super) struct CargoEnvCfgEvaluator; impl CfgEvaluator for CargoEnvCfgEvaluator { fn eval(&self, name: &str, query_value: Option<&str>) -> CfgResult { let env = ENV.get_or_init(CargoEnv::load); if name == "feature" { return if let Some(query_value) = query_value { CfgResult::from(env.features.contains(Lookup::new(query_value))) } else { let msg = "expected `feature = \"...\"`".to_owned(); CfgResult::Undetermined { msg } }; } if name == "test" && query_value.is_none() { let msg = "cfg(test) is not supported because Cargo runs your build script only once across the lib and test build of the same crate".to_owned(); return CfgResult::Undetermined { msg }; } if let Some(cargo_value) = env.cfgs.get(Lookup::new(name)) { return if let Some(query_value) = query_value { CfgResult::from(cargo_value.split(',').any(|value| value == query_value)) } else { CfgResult::True }; } if name == "debug_assertions" && query_value.is_none() { return CfgResult::from(cfg!(debug_assertions)); } CfgResult::False } } impl CargoEnv { fn load() -> Self { const CARGO_FEATURE_PREFIX: &str = "CARGO_FEATURE_"; const CARGO_CFG_PREFIX: &str = "CARGO_CFG_"; let mut features = Set::new(); let mut cfgs = Map::new(); for (k, v) in env::vars_os() { let Some(k) = k.to_str() else { continue; }; let Ok(v) = v.into_string() else { continue; }; if let Some(feature_name) = k.strip_prefix(CARGO_FEATURE_PREFIX) { let feature_name = Name(feature_name.to_owned()); features.insert(feature_name); } else if let Some(cfg_name) = k.strip_prefix(CARGO_CFG_PREFIX) { let cfg_name = Name(cfg_name.to_owned()); cfgs.insert(cfg_name, v); } } CargoEnv { features, cfgs } } } struct Name(String); impl Ord for Name { fn cmp(&self, rhs: &Self) -> Ordering { Lookup::new(&self.0).cmp(Lookup::new(&rhs.0)) } } impl PartialOrd for Name { fn partial_cmp(&self, rhs: &Self) -> Option { Some(self.cmp(rhs)) } } impl Eq for Name {} impl PartialEq for Name { fn eq(&self, rhs: &Self) -> bool { Lookup::new(&self.0).eq(Lookup::new(&rhs.0)) } } #[repr(transparent)] struct Lookup(str); impl Lookup { fn new(name: &str) -> &Self { unsafe { &*(ptr::from_ref::(name) as *const Self) } } } impl Borrow for Name { fn borrow(&self) -> &Lookup { Lookup::new(&self.0) } } impl Ord for Lookup { fn cmp(&self, rhs: &Self) -> Ordering { self.0 .bytes() .map(CaseAgnosticByte) .cmp(rhs.0.bytes().map(CaseAgnosticByte)) } } impl PartialOrd for Lookup { fn partial_cmp(&self, rhs: &Self) -> Option { Some(self.cmp(rhs)) } } impl Eq for Lookup {} impl PartialEq for Lookup { fn eq(&self, rhs: &Self) -> bool { self.0 .bytes() .map(CaseAgnosticByte) .eq(rhs.0.bytes().map(CaseAgnosticByte)) } } struct CaseAgnosticByte(u8); impl Ord for CaseAgnosticByte { fn cmp(&self, rhs: &Self) -> Ordering { self.0.to_ascii_lowercase().cmp(&rhs.0.to_ascii_lowercase()) } } impl PartialOrd for CaseAgnosticByte { fn partial_cmp(&self, rhs: &Self) -> Option { Some(self.cmp(rhs)) } } impl Eq for CaseAgnosticByte {} impl PartialEq for CaseAgnosticByte { fn eq(&self, rhs: &Self) -> bool { self.cmp(rhs) == Ordering::Equal } } ================================================ FILE: gen/build/src/cfg.rs ================================================ use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::path::Path; /// Build configuration. See [CFG]. pub struct Cfg<'a> { /// See [`CFG.include_prefix`][CFG#cfginclude_prefix]. pub include_prefix: &'a str, /// See [`CFG.exported_header_dirs`][CFG#cfgexported_header_dirs]. pub exported_header_dirs: Vec<&'a Path>, /// See [`CFG.exported_header_prefixes`][CFG#cfgexported_header_prefixes]. pub exported_header_prefixes: Vec<&'a str>, /// See [`CFG.exported_header_links`][CFG#cfgexported_header_links]. pub exported_header_links: Vec<&'a str>, /// See [`CFG.doxygen`][CFG#cfgdoxygen]. pub doxygen: bool, marker: PhantomData<*const ()>, // !Send + !Sync } /// Global configuration of the current build. /// ///
    /// ///
    &str
    /// /// ## **`CFG.include_prefix`** /// /// The prefix at which C++ code from your crate as well as directly dependent /// crates can access the code generated during this build. /// /// By default, the `include_prefix` is equal to the name of the current crate. /// That means if your crate is called `demo` and has Rust source files in a /// *src/* directory and maybe some handwritten C++ header files in an /// *include/* directory, then the current crate as well as downstream crates /// might include them as follows: /// /// ``` /// # const _: &str = stringify! { /// // include one of the handwritten headers: /// #include "demo/include/wow.h" /// /// // include a header generated from Rust cxx::bridge: /// #include "demo/src/lib.rs.h" /// # }; /// ``` /// /// By modifying `CFG.include_prefix` we can substitute a prefix that is /// different from the crate name if desired. Here we'll change it to /// `"path/to"` which will make import paths take the form /// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`. /// /// ```no_run /// // build.rs /// /// use cxx_build::CFG; /// /// fn main() { /// CFG.include_prefix = "path/to"; /// /// cxx_build::bridge("src/lib.rs") /// .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"` /// /* ... */ /// .compile("demo"); /// } /// ``` /// /// Note that cross-crate imports are only made available between **direct /// dependencies**. Another crate must directly depend on your crate in order to /// #include its headers; a transitive dependency is not sufficient. /// Additionally, headers from a direct dependency are only importable if the /// dependency's Cargo.toml manifest contains a `links` key. If not, its headers /// will not be importable from outside of the same crate. /// ///
    /// ///
    Vec<&Path>
    /// /// ## **`CFG.exported_header_dirs`** /// /// A vector of absolute paths. The current crate, directly dependent crates, /// and further crates to which this crate's headers are exported (see below) /// will be able to `#include` headers from these directories. /// /// Adding a directory to `exported_header_dirs` is similar to adding it to the /// current build via the `cc` crate's [`Build::include`][cc::Build::include], /// but *also* makes the directory available to downstream crates that want to /// `#include` one of the headers from your crate. If the dir were added only /// using `Build::include`, the downstream crate including your header would /// need to manually add the same directory to their own build as well. /// /// When using `exported_header_dirs`, your crate must also set a `links` key /// for itself in Cargo.toml. See [*the `links` manifest key*][links]. The /// reason is that Cargo imposes no ordering on the execution of build scripts /// without a `links` key, which means the downstream crate's build script might /// execute before yours decides what to put into `exported_header_dirs`. /// /// [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key /// /// ### Example /// /// One of your crate's headers wants to include a system library, such as /// `#include "Python.h"`. /// /// ```no_run /// // build.rs /// /// use cxx_build::CFG; /// use std::path::PathBuf; /// /// fn main() { /// let python3 = pkg_config::probe_library("python3").unwrap(); /// let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path); /// CFG.exported_header_dirs.extend(python_include_paths); /// /// cxx_build::bridge("src/bridge.rs").compile("demo"); /// } /// ``` /// /// ### Example /// /// Your crate wants to rearrange the headers that it exports vs how they're /// laid out locally inside the crate's source directory. /// /// Suppose the crate as published contains a file at `./include/myheader.h` but /// wants it available to downstream crates as `#include "foo/v1/public.h"`. /// /// ```no_run /// // build.rs /// /// use cxx_build::CFG; /// use std::path::Path; /// use std::{env, fs}; /// /// fn main() { /// let out_dir = env::var_os("OUT_DIR").unwrap(); /// let headers = Path::new(&out_dir).join("headers"); /// CFG.exported_header_dirs.push(&headers); /// /// // We contain `include/myheader.h` locally, but /// // downstream will use `#include "foo/v1/public.h"` /// let foo = headers.join("foo").join("v1"); /// fs::create_dir_all(&foo).unwrap(); /// fs::copy("include/myheader.h", foo.join("public.h")).unwrap(); /// /// cxx_build::bridge("src/bridge.rs").compile("demo"); /// } /// ``` /// ///



    /// ///
    Vec<&str>
    /// /// ## **`CFG.exported_header_prefixes`** /// /// Vector of strings. These each refer to the `include_prefix` of one of your /// direct dependencies, or a prefix thereof. They describe which of your /// dependencies participate in your crate's C++ public API, as opposed to /// private use by your crate's implementation. /// /// As a general rule, if one of your headers `#include`s something from one of /// your dependencies, you need to put that dependency's `include_prefix` into /// `CFG.exported_header_prefixes` (*or* their `links` key into /// `CFG.exported_header_links`; see below). On the other hand if only your C++ /// implementation files and *not* your headers are importing from the /// dependency, you do not export that dependency. /// /// The significance of exported headers is that if downstream code (crate 𝒜) /// contains an `#include` of a header from your crate (ℬ) and your header /// contains an `#include` of something from your dependency (𝒞), the exported /// dependency 𝒞 becomes available during the downstream crate 𝒜's build. /// Otherwise the downstream crate 𝒜 doesn't know about 𝒞 and wouldn't be able /// to find what header your header is referring to, and would fail to build. /// /// When using `exported_header_prefixes`, your crate must also set a `links` /// key for itself in Cargo.toml. /// /// ### Example /// /// Suppose you have a crate with 5 direct dependencies and the `include_prefix` /// for each one are: /// /// - "crate0" /// - "group/api/crate1" /// - "group/api/crate2" /// - "group/api/contrib/crate3" /// - "detail/crate4" /// /// Your header involves types from the first four so we re-export those as part /// of your public API, while crate4 is only used internally by your cc file not /// your header, so we do not export: /// /// ```no_run /// // build.rs /// /// use cxx_build::CFG; /// /// fn main() { /// CFG.exported_header_prefixes = vec!["crate0", "group/api"]; /// /// cxx_build::bridge("src/bridge.rs") /// .file("src/impl.cc") /// .compile("demo"); /// } /// ``` /// ///



    /// ///
    Vec<&str>
    /// /// ## **`CFG.exported_header_links`** /// /// Vector of strings. These each refer to the `links` attribute ([*the `links` /// manifest key*][links]) of one of your crate's direct dependencies. /// /// This achieves an equivalent result to `CFG.exported_header_prefixes` by /// re-exporting a dependency as part of your crate's public API, except with /// finer grained control for cases when multiple crates might be sharing the /// same `include_prefix` and you'd like to export some but not others. Links /// attributes are guaranteed to be unique identifiers by Cargo. /// /// When using `exported_header_links`, your crate must also set a `links` key /// for itself in Cargo.toml. /// /// ### Example /// /// ```no_run /// // build.rs /// /// use cxx_build::CFG; /// /// fn main() { /// CFG.exported_header_links.push("git2"); /// /// cxx_build::bridge("src/bridge.rs").compile("demo"); /// } /// ``` /// ///



    /// ///
    bool
    /// /// ## **`CFG.doxygen`** /// /// Boolean. Whether to propagate Rust documentation from inside the cxx::bridge /// module as Doxygen-style comments in the generated C++ header. /// /// Documentation on the following are supported: /// /// - shared structs, and fields of shared structs /// - shared enums, and their variants /// - extern "Rust" opaque types /// - extern "Rust" functions, including methods/member functions /// /// ### Example /// /// ```no_run /// // build.rs /// /// use cxx_build::CFG; /// /// fn main() { /// CFG.doxygen = true; /// /// cxx_build::bridge("src/bridge.rs").compile("demo"); /// } /// ``` /// /// ```rust /// // src/bridge.rs /// /// #[cxx::bridge] /// mod ffi { /// /// documentation of MyStruct /// pub struct MyStruct { /// /// documentation of the struct field /// lol: String, /// } /// /// extern "Rust" { /// /// documentation of MyType /// type MyType; /// /// /// function documentation /// fn asdf() -> bool; /// } /// } /// # /// # pub struct MyType; /// # fn asdf() -> bool { true } /// # fn main() {} /// ``` /// /// With `CFG.doxygen` enabled, the generated C++ header through which /// downstream C++ code will be able to access these shared structs and extern /// "Rust" signatures will have the Rust documentation comments propagated as /// Doxygen-style comments: /// /// ```cpp /// /// documentation of MyStruct /// struct MyStruct final { /// /// documentation of the struct field /// ::rust::String lol; /// … /// }; /// ``` /// /// Otherwise by default (without `CFG.doxygen`) they'll just be `//` comments. #[cfg(doc)] pub static mut CFG: Cfg = Cfg { include_prefix: "", exported_header_dirs: Vec::new(), exported_header_prefixes: Vec::new(), exported_header_links: Vec::new(), doxygen: false, marker: PhantomData, }; impl<'a> Debug for Cfg<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let Self { include_prefix, exported_header_dirs, exported_header_prefixes, exported_header_links, doxygen, marker: _, } = self; formatter .debug_struct("Cfg") .field("include_prefix", include_prefix) .field("exported_header_dirs", exported_header_dirs) .field("exported_header_prefixes", exported_header_prefixes) .field("exported_header_links", exported_header_links) .field("doxygen", doxygen) .finish() } } #[cfg(not(doc))] pub use self::r#impl::Cfg::CFG; #[cfg(not(doc))] mod r#impl { use crate::intern::{intern, InternedString}; use crate::syntax::map::UnorderedMap as Map; use crate::vec::{self, InternedVec as _}; use std::cell::RefCell; use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::sync::{OnceLock, PoisonError, RwLock}; struct CurrentCfg { include_prefix: InternedString, exported_header_dirs: Vec, exported_header_prefixes: Vec, exported_header_links: Vec, doxygen: bool, } impl CurrentCfg { fn default() -> Self { let include_prefix = crate::env_os("CARGO_PKG_NAME") .map(|pkg| intern(&pkg.to_string_lossy())) .unwrap_or_default(); let exported_header_dirs = Vec::new(); let exported_header_prefixes = Vec::new(); let exported_header_links = Vec::new(); let doxygen = false; CurrentCfg { include_prefix, exported_header_dirs, exported_header_prefixes, exported_header_links, doxygen, } } } fn current() -> &'static RwLock { static CURRENT: OnceLock> = OnceLock::new(); CURRENT.get_or_init(|| RwLock::new(CurrentCfg::default())) } thread_local! { // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved, // we can delete this thread local side table and instead make each CFG // instance directly own the associated super::Cfg. // // #[allow(const_item_mutation)] // pub const CFG: Cfg = Cfg { // cfg: AtomicPtr::new(ptr::null_mut()), // }; // pub struct Cfg { // cfg: AtomicPtr, // } // static CONST_DEREFS: RefCell>>> = RefCell::default(); } #[derive(Eq, PartialEq, Hash)] struct Handle(*const Cfg<'static>); impl<'a> Cfg<'a> { fn current() -> super::Cfg<'a> { let current = current().read().unwrap_or_else(PoisonError::into_inner); let include_prefix = current.include_prefix.str(); let exported_header_dirs = current.exported_header_dirs.vec(); let exported_header_prefixes = current.exported_header_prefixes.vec(); let exported_header_links = current.exported_header_links.vec(); let doxygen = current.doxygen; super::Cfg { include_prefix, exported_header_dirs, exported_header_prefixes, exported_header_links, doxygen, marker: PhantomData, } } const fn handle(self: &Cfg<'a>) -> Handle { Handle(<*const Cfg>::cast(self)) } } // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will // drop on the same thread where they were created. pub enum Cfg<'a> { Mut(super::Cfg<'a>), CFG, } impl<'a> Debug for Cfg<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { if let Cfg::Mut(cfg) = self { Debug::fmt(cfg, formatter) } else { Debug::fmt(&Cfg::current(), formatter) } } } impl<'a> Deref for Cfg<'a> { type Target = super::Cfg<'a>; fn deref(&self) -> &Self::Target { if let Cfg::Mut(cfg) = self { cfg } else { let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg { &raw mut **derefs .borrow_mut() .entry(self.handle()) .or_insert_with(|| Box::new(Cfg::current())) }); unsafe { &mut *cfg } } } } impl<'a> DerefMut for Cfg<'a> { fn deref_mut(&mut self) -> &mut Self::Target { if let Cfg::CFG = self { CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle())); *self = Cfg::Mut(Cfg::current()); } match self { Cfg::Mut(cfg) => cfg, Cfg::CFG => unreachable!(), } } } impl<'a> Drop for Cfg<'a> { fn drop(&mut self) { if let Cfg::Mut(cfg) = self { let super::Cfg { include_prefix, exported_header_dirs, exported_header_prefixes, exported_header_links, doxygen, marker: _, } = cfg; let mut current = current().write().unwrap_or_else(PoisonError::into_inner); current.include_prefix = intern(include_prefix); current.exported_header_dirs = vec::intern(exported_header_dirs); current.exported_header_prefixes = vec::intern(exported_header_prefixes); current.exported_header_links = vec::intern(exported_header_links); current.doxygen = *doxygen; } else { CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle())); } } } } ================================================ FILE: gen/build/src/deps.rs ================================================ use std::collections::BTreeMap; use std::env; use std::ffi::OsString; use std::path::PathBuf; #[derive(Default)] pub(crate) struct Crate { pub include_prefix: Option, pub links: Option, pub header_dirs: Vec, } pub(crate) struct HeaderDir { pub exported: bool, pub path: PathBuf, } impl Crate { pub(crate) fn print_to_cargo(&self) { if let Some(include_prefix) = &self.include_prefix { println!( "cargo:CXXBRIDGE_PREFIX={}", include_prefix.to_string_lossy(), ); } if let Some(links) = &self.links { println!("cargo:CXXBRIDGE_LINKS={}", links.to_string_lossy()); } for (i, header_dir) in self.header_dirs.iter().enumerate() { if header_dir.exported { println!( "cargo:CXXBRIDGE_DIR{}={}", i, header_dir.path.to_string_lossy(), ); } } } } pub(crate) fn direct_dependencies() -> Vec { let mut crates: BTreeMap = BTreeMap::new(); let mut exported_header_dirs: BTreeMap> = BTreeMap::new(); // Only variables set from a build script of direct dependencies are // observable. That's exactly what we want! Your crate needs to declare a // direct dependency on the other crate in order to be able to #include its // headers. // // Also, they're only observable if the dependency's manifest contains a // `links` key. This is important because Cargo imposes no ordering on the // execution of build scripts without a `links` key. When exposing a // generated header for the current crate to #include, we need to be sure // the dependency's build script has already executed and emitted that // generated header. // // References: // - https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key // - https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate for (k, v) in env::vars_os() { let mut k = k.to_string_lossy().into_owned(); if !k.starts_with("DEP_") { continue; } if k.ends_with("_CXXBRIDGE_PREFIX") { k.truncate(k.len() - "_CXXBRIDGE_PREFIX".len()); crates.entry(k).or_default().include_prefix = Some(PathBuf::from(v)); continue; } if k.ends_with("_CXXBRIDGE_LINKS") { k.truncate(k.len() - "_CXXBRIDGE_LINKS".len()); crates.entry(k).or_default().links = Some(v); continue; } let without_counter = k.trim_end_matches(|ch: char| ch.is_ascii_digit()); let counter_len = k.len() - without_counter.len(); if counter_len == 0 || !without_counter.ends_with("_CXXBRIDGE_DIR") { continue; } let sort_key = k[k.len() - counter_len..] .parse::() .unwrap_or(usize::MAX); k.truncate(k.len() - counter_len - "_CXXBRIDGE_DIR".len()); exported_header_dirs .entry(k) .or_default() .push((sort_key, PathBuf::from(v))); } for (k, mut dirs) in exported_header_dirs { dirs.sort_by_key(|(sort_key, _dir)| *sort_key); crates .entry(k) .or_default() .header_dirs .extend(dirs.into_iter().map(|(_sort_key, dir)| HeaderDir { exported: true, path: dir, })); } crates.into_iter().map(|entry| entry.1).collect() } ================================================ FILE: gen/build/src/error.rs ================================================ use crate::cfg::CFG; use crate::gen::fs; use std::error::Error as StdError; use std::ffi::OsString; use std::fmt::{self, Display}; use std::path::Path; pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { NoEnv(OsString), Fs(fs::Error), ExportedDirNotAbsolute(&'static Path), ExportedEmptyPrefix, ExportedDirsWithoutLinks, ExportedPrefixesWithoutLinks, ExportedLinksWithoutLinks, UnusedExportedPrefix(&'static str), UnusedExportedLinks(&'static str), } macro_rules! expr { ($expr:expr) => {{ let _ = $expr; // ensure it doesn't fall out of sync with CFG definition stringify!($expr) }}; } const LINKS_DOCUMENTATION: &str = "https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key"; impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::NoEnv(var) => { write!(f, "missing {} environment variable", var.to_string_lossy()) } Error::Fs(err) => err.fmt(f), Error::ExportedDirNotAbsolute(path) => write!( f, "element of {} must be absolute path, but was: `{}`", expr!(CFG.exported_header_dirs), path.display(), ), Error::ExportedEmptyPrefix => write!( f, "element of {} must not be empty string", expr!(CFG.exported_header_prefixes), ), Error::ExportedDirsWithoutLinks => write!( f, "if {} is nonempty then `links` needs to be set in Cargo.toml; see {}", expr!(CFG.exported_header_dirs), LINKS_DOCUMENTATION, ), Error::ExportedPrefixesWithoutLinks => write!( f, "if {} is nonempty then `links` needs to be set in Cargo.toml; see {}", expr!(CFG.exported_header_prefixes), LINKS_DOCUMENTATION, ), Error::ExportedLinksWithoutLinks => write!( f, "if {} is nonempty then `links` needs to be set in Cargo.toml; see {}", expr!(CFG.exported_header_links), LINKS_DOCUMENTATION, ), Error::UnusedExportedPrefix(unused) => write!( f, "unused element in {}: {:?} does not match the include prefix of any direct dependency", expr!(CFG.exported_header_prefixes), unused, ), Error::UnusedExportedLinks(unused) => write!( f, "unused element in {}: {:?} does not match the `links` attribute any direct dependency", expr!(CFG.exported_header_links), unused, ), } } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { Error::Fs(err) => err.source(), _ => None, } } } impl From for Error { fn from(err: fs::Error) -> Self { Error::Fs(err) } } ================================================ FILE: gen/build/src/intern.rs ================================================ use crate::syntax::set::UnorderedSet as Set; use std::sync::{Mutex, OnceLock, PoisonError}; #[derive(Copy, Clone, Default)] pub(crate) struct InternedString(&'static str); impl InternedString { pub(crate) fn str(self) -> &'static str { self.0 } } pub(crate) fn intern(s: &str) -> InternedString { static INTERN: OnceLock>> = OnceLock::new(); let mut set = INTERN .get_or_init(|| Mutex::new(Set::new())) .lock() .unwrap_or_else(PoisonError::into_inner); InternedString(match set.get(s) { Some(interned) => *interned, None => { let interned = Box::leak(Box::from(s)); set.insert(interned); interned } }) } ================================================ FILE: gen/build/src/lib.rs ================================================ //! The CXX code generator for constructing and compiling C++ code. //! //! This is intended to be used from Cargo build scripts to execute CXX's //! C++ code generator, set up any additional compiler flags depending on //! the use case, and make the C++ compiler invocation. //! //!
    //! //! # Example //! //! Example of a canonical Cargo build script that builds a CXX bridge: //! //! ```no_run //! // build.rs //! //! fn main() { //! cxx_build::bridge("src/main.rs") //! .file("src/demo.cc") //! .std("c++11") //! .compile("cxxbridge-demo"); //! //! println!("cargo:rerun-if-changed=src/demo.cc"); //! println!("cargo:rerun-if-changed=include/demo.h"); //! } //! ``` //! //! A runnable working setup with this build script is shown in the *demo* //! directory of [https://github.com/dtolnay/cxx]. //! //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx //! //!
    //! //! # Alternatives //! //! For use in non-Cargo builds like Bazel or Buck, CXX provides an //! alternate way of invoking the C++ code generator as a standalone command //! line tool. The tool is packaged as the `cxxbridge-cmd` crate. //! //! ```bash //! $ cargo install cxxbridge-cmd # or build it from the repo //! //! $ cxxbridge src/main.rs --header > path/to/mybridge.h //! $ cxxbridge src/main.rs > path/to/mybridge.cc //! ``` #![doc(html_root_url = "https://docs.rs/cxx-build/1.0.194")] #![cfg_attr(not(check_cfg), allow(unexpected_cfgs))] #![allow( clippy::cast_sign_loss, clippy::default_trait_access, clippy::doc_markdown, clippy::elidable_lifetime_names, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, // https://github.com/rust-lang/rust-clippy/issues/15842 clippy::explicit_auto_deref, clippy::inherent_to_string, clippy::items_after_statements, clippy::match_bool, clippy::match_like_matches_macro, clippy::match_same_arms, clippy::needless_continue, clippy::needless_doctest_main, clippy::needless_lifetimes, clippy::needless_pass_by_value, clippy::nonminimal_bool, clippy::precedence, clippy::redundant_else, clippy::ref_option, clippy::similar_names, clippy::single_match_else, clippy::struct_excessive_bools, clippy::struct_field_names, clippy::too_many_arguments, clippy::too_many_lines, clippy::toplevel_ref_arg, clippy::uninlined_format_args, clippy::upper_case_acronyms )] #![allow(unknown_lints, mismatched_lifetime_syntaxes)] mod cargo; mod cfg; mod deps; mod error; mod gen; mod intern; mod out; mod paths; mod syntax; mod target; mod vec; use crate::cargo::CargoEnvCfgEvaluator; use crate::deps::{Crate, HeaderDir}; use crate::error::{Error, Result}; use crate::gen::error::report; use crate::gen::Opt; use crate::paths::PathExt; use crate::syntax::map::{Entry, UnorderedMap}; use crate::target::TargetDir; use cc::Build; use std::collections::BTreeSet; use std::env; use std::ffi::{OsStr, OsString}; use std::io::{self, Write}; use std::iter; use std::path::{Path, PathBuf}; use std::process; pub use crate::cfg::{Cfg, CFG}; /// This returns a [`cc::Build`] on which you should continue to set up any /// additional source files or compiler flags, and lastly call its [`compile`] /// method to execute the C++ build. /// /// [`compile`]: cc::Build::compile #[must_use] pub fn bridge(rust_source_file: impl AsRef) -> Build { bridges(iter::once(rust_source_file)) } /// `cxx_build::bridge` but for when more than one file contains a /// #\[cxx::bridge\] module. /// /// ```no_run /// let source_files = vec!["src/main.rs", "src/path/to/other.rs"]; /// cxx_build::bridges(source_files) /// .file("src/demo.cc") /// .std("c++11") /// .compile("cxxbridge-demo"); /// ``` #[must_use] pub fn bridges(rust_source_files: impl IntoIterator>) -> Build { let ref mut rust_source_files = rust_source_files.into_iter(); build(rust_source_files).unwrap_or_else(|err| { let _ = writeln!(io::stderr(), "\n\ncxxbridge error: {}\n\n", report(err)); process::exit(1); }) } struct Project { include_prefix: PathBuf, manifest_dir: PathBuf, // The `links = "..."` value from Cargo.toml. links_attribute: Option, // Output directory as received from Cargo. out_dir: PathBuf, // Directory into which to symlink all generated code. // // This is *not* used for an #include path, only as a debugging convenience. // Normally available at target/cxxbridge/ if we are able to know where the // target dir is, otherwise under a common scratch dir. // // The reason this isn't the #include dir is that we do not want builds to // have access to headers from arbitrary other parts of the dependency // graph. Using a global directory for all builds would be both a race // condition depending on what order Cargo randomly executes the build // scripts, as well as semantically undesirable for builds not to have to // declare their real dependencies. shared_dir: PathBuf, } impl Project { fn init() -> Result { let include_prefix = Path::new(CFG.include_prefix); assert!(include_prefix.is_relative()); let include_prefix = include_prefix.components().collect(); let links_attribute = env::var_os("CARGO_MANIFEST_LINKS"); let manifest_dir = paths::manifest_dir()?; let out_dir = paths::out_dir()?; let shared_dir = match target::find_target_dir(&out_dir) { TargetDir::Path(target_dir) => target_dir.join("cxxbridge"), TargetDir::Unknown => scratch::path("cxxbridge"), }; Ok(Project { include_prefix, manifest_dir, links_attribute, out_dir, shared_dir, }) } } // We lay out the OUT_DIR as follows. Everything is namespaced under a cxxbridge // subdirectory to avoid stomping on other things that the caller's build script // might be doing inside OUT_DIR. // // $OUT_DIR/ // cxxbridge/ // crate/ // $CARGO_PKG_NAME -> $CARGO_MANIFEST_DIR // include/ // rust/ // cxx.h // $CARGO_PKG_NAME/ // .../ // lib.rs.h // sources/ // $CARGO_PKG_NAME/ // .../ // lib.rs.cc // // The crate/ and include/ directories are placed on the #include path for the // current build as well as for downstream builds that have a direct dependency // on the current crate. fn build(rust_source_files: &mut dyn Iterator>) -> Result { let ref prj = Project::init()?; validate_cfg(prj)?; let this_crate = make_this_crate(prj)?; let mut build = Build::new(); build.cpp(true); build.cpp_link_stdlib(None); // linked via link-cplusplus crate for path in rust_source_files { generate_bridge(prj, &mut build, path.as_ref())?; } this_crate.print_to_cargo(); eprintln!("\nCXX include path:"); for header_dir in this_crate.header_dirs { build.include(&header_dir.path); if header_dir.exported { eprintln!(" {}", header_dir.path.display()); } else { eprintln!(" {} (private)", header_dir.path.display()); } } Ok(build) } fn validate_cfg(prj: &Project) -> Result<()> { for exported_dir in &CFG.exported_header_dirs { if !exported_dir.is_absolute() { return Err(Error::ExportedDirNotAbsolute(exported_dir)); } } for prefix in &CFG.exported_header_prefixes { if prefix.is_empty() { return Err(Error::ExportedEmptyPrefix); } } if prj.links_attribute.is_none() { if !CFG.exported_header_dirs.is_empty() { return Err(Error::ExportedDirsWithoutLinks); } if !CFG.exported_header_prefixes.is_empty() { return Err(Error::ExportedPrefixesWithoutLinks); } if !CFG.exported_header_links.is_empty() { return Err(Error::ExportedLinksWithoutLinks); } } Ok(()) } fn make_this_crate(prj: &Project) -> Result { let crate_dir = make_crate_dir(prj); let include_dir = make_include_dir(prj)?; let mut this_crate = Crate { include_prefix: Some(prj.include_prefix.clone()), links: prj.links_attribute.clone(), header_dirs: Vec::new(), }; // The generated code directory (include_dir) is placed in front of // crate_dir on the include line so that `#include "path/to/file.rs"` from // C++ "magically" works and refers to the API generated from that Rust // source file. this_crate.header_dirs.push(HeaderDir { exported: true, path: include_dir, }); this_crate.header_dirs.push(HeaderDir { exported: true, path: crate_dir, }); for exported_dir in &CFG.exported_header_dirs { this_crate.header_dirs.push(HeaderDir { exported: true, path: PathBuf::from(exported_dir), }); } let mut header_dirs_index = UnorderedMap::new(); let mut used_header_links = BTreeSet::new(); let mut used_header_prefixes = BTreeSet::new(); for krate in deps::direct_dependencies() { let mut is_link_exported = || match &krate.links { None => false, Some(links_attribute) => CFG.exported_header_links.iter().any(|&exported| { let matches = links_attribute == exported; if matches { used_header_links.insert(exported); } matches }), }; let mut is_prefix_exported = || match &krate.include_prefix { None => false, Some(include_prefix) => CFG.exported_header_prefixes.iter().any(|&exported| { let matches = include_prefix.starts_with(exported); if matches { used_header_prefixes.insert(exported); } matches }), }; let exported = is_link_exported() || is_prefix_exported(); for dir in krate.header_dirs { // Deduplicate dirs reachable via multiple transitive dependencies. match header_dirs_index.entry(dir.path.clone()) { Entry::Vacant(entry) => { entry.insert(this_crate.header_dirs.len()); this_crate.header_dirs.push(HeaderDir { exported, path: dir.path, }); } Entry::Occupied(entry) => { let index = *entry.get(); this_crate.header_dirs[index].exported |= exported; } } } } if let Some(unused) = CFG .exported_header_links .iter() .find(|&exported| !used_header_links.contains(exported)) { return Err(Error::UnusedExportedLinks(unused)); } if let Some(unused) = CFG .exported_header_prefixes .iter() .find(|&exported| !used_header_prefixes.contains(exported)) { return Err(Error::UnusedExportedPrefix(unused)); } Ok(this_crate) } fn make_crate_dir(prj: &Project) -> PathBuf { if prj.include_prefix.as_os_str().is_empty() { return prj.manifest_dir.clone(); } let crate_dir = prj.out_dir.join("cxxbridge").join("crate"); let ref link = crate_dir.join(&prj.include_prefix); let ref manifest_dir = prj.manifest_dir; if out::relative_symlink_dir(manifest_dir, link).is_err() && cfg!(not(unix)) { let cachedir_tag = "\ Signature: 8a477f597d28d172789f06886806bc55\n\ # This file is a cache directory tag created by cxx.\n\ # For information about cache directory tags see https://bford.info/cachedir/\n"; let _ = out::write(crate_dir.join("CACHEDIR.TAG"), cachedir_tag.as_bytes()); let max_depth = 6; best_effort_copy_headers(manifest_dir, link, max_depth); } crate_dir } fn make_include_dir(prj: &Project) -> Result { let include_dir = prj.out_dir.join("cxxbridge").join("include"); let cxx_h = include_dir.join("rust").join("cxx.h"); let ref shared_cxx_h = prj.shared_dir.join("rust").join("cxx.h"); if let Some(ref original) = env::var_os("DEP_CXXBRIDGE1_HEADER") { out::absolute_symlink_file(original, cxx_h)?; out::absolute_symlink_file(original, shared_cxx_h)?; } else { out::write(shared_cxx_h, gen::include::HEADER.as_bytes())?; out::relative_symlink_file(shared_cxx_h, cxx_h)?; } Ok(include_dir) } fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) -> Result<()> { let opt = Opt { allow_dot_includes: false, cfg_evaluator: Box::new(CargoEnvCfgEvaluator), doxygen: CFG.doxygen, ..Opt::default() }; if !rust_source_file.starts_with(&prj.out_dir) { println!("cargo:rerun-if-changed={}", rust_source_file.display()); } let generated = gen::generate_from_path(rust_source_file, &opt); let ref rel_path = paths::local_relative_path(rust_source_file); let cxxbridge = prj.out_dir.join("cxxbridge"); let include_dir = cxxbridge.join("include").join(&prj.include_prefix); let sources_dir = cxxbridge.join("sources").join(&prj.include_prefix); let ref rel_path_h = rel_path.with_appended_extension(".h"); let ref header_path = include_dir.join(rel_path_h); out::write(header_path, &generated.header)?; let ref link_path = include_dir.join(rel_path); let _ = out::relative_symlink_file(header_path, link_path); let ref rel_path_cc = rel_path.with_appended_extension(".cc"); let ref implementation_path = sources_dir.join(rel_path_cc); out::write(implementation_path, &generated.implementation)?; build.file(implementation_path); let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h); let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc); let _ = out::relative_symlink_file(header_path, shared_h); let _ = out::relative_symlink_file(implementation_path, shared_cc); Ok(()) } fn best_effort_copy_headers(src: &Path, dst: &Path, max_depth: usize) { // Not using crate::gen::fs because we aren't reporting the errors. use std::fs; let mut dst_created = false; let Ok(mut entries) = fs::read_dir(src) else { return; }; while let Some(Ok(entry)) = entries.next() { let file_name = entry.file_name(); if file_name.to_string_lossy().starts_with('.') { continue; } match entry.file_type() { Ok(file_type) if file_type.is_dir() && max_depth > 0 => { let src = entry.path(); if src.join("Cargo.toml").exists() || src.join("CACHEDIR.TAG").exists() { continue; } let dst = dst.join(file_name); best_effort_copy_headers(&src, &dst, max_depth - 1); } Ok(file_type) if file_type.is_file() => { let src = entry.path(); match src.extension().and_then(OsStr::to_str) { Some("h" | "hh" | "hpp") => {} _ => continue, } if !dst_created && fs::create_dir_all(dst).is_err() { return; } dst_created = true; let dst = dst.join(file_name); let _ = fs::remove_file(&dst); let _ = fs::copy(src, dst); } _ => {} } } } fn env_os(key: impl AsRef) -> Result { let key = key.as_ref(); env::var_os(key).ok_or_else(|| Error::NoEnv(key.to_owned())) } ================================================ FILE: gen/build/src/out.rs ================================================ use crate::error::{Error, Result}; use crate::gen::fs; use crate::paths; use std::path::{Component, Path, PathBuf}; use std::{env, io}; pub(crate) fn write(path: impl AsRef, content: &[u8]) -> Result<()> { let path = path.as_ref(); let mut create_dir_error = None; if fs::exists(path) { if let Ok(existing) = fs::read(path) { if existing == content { // Avoid bumping modified time with unchanged contents. return Ok(()); } } best_effort_remove(path); } else { let parent = path.parent().unwrap(); create_dir_error = fs::create_dir_all(parent).err(); } match fs::write(path, content) { // As long as write succeeded, ignore any create_dir_all error. Ok(()) => Ok(()), // If create_dir_all and write both failed, prefer the first error. Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))), } } pub(crate) fn relative_symlink_file( original: impl AsRef, link: impl AsRef, ) -> Result<()> { let original = original.as_ref(); let link = link.as_ref(); let parent_directory_error = prepare_parent_directory_for_symlink(link).err(); let relativized = best_effort_relativize_symlink(original, link); symlink_file(&relativized, original, link, parent_directory_error) } pub(crate) fn absolute_symlink_file( original: impl AsRef, link: impl AsRef, ) -> Result<()> { let original = original.as_ref(); let link = link.as_ref(); let parent_directory_error = prepare_parent_directory_for_symlink(link).err(); symlink_file(original, original, link, parent_directory_error) } pub(crate) fn relative_symlink_dir( original: impl AsRef, link: impl AsRef, ) -> Result<()> { let original = original.as_ref(); let link = link.as_ref(); let parent_directory_error = prepare_parent_directory_for_symlink(link).err(); let relativized = best_effort_relativize_symlink(original, link); symlink_dir(&relativized, link, parent_directory_error) } fn prepare_parent_directory_for_symlink(link: &Path) -> fs::Result<()> { if fs::exists(link) { best_effort_remove(link); Ok(()) } else { let parent = link.parent().unwrap(); fs::create_dir_all(parent) } } fn symlink_file( path_for_symlink: &Path, path_for_copy: &Path, link: &Path, parent_directory_error: Option, ) -> Result<()> { match paths::symlink_or_copy(path_for_symlink, path_for_copy, link) { // As long as symlink_or_copy succeeded, ignore any create_dir_all error. Ok(()) => Ok(()), Err(err) => { if err.kind() == io::ErrorKind::AlreadyExists { // This is fine, a different simultaneous build script already // created the same link or copy. The cxx_build target directory // is laid out such that the same path never refers to two // different targets during the same multi-crate build, so if // some other build script already created the same path then we // know it refers to the identical target that the current build // script was trying to create. Ok(()) } else { // If create_dir_all and symlink_or_copy both failed, prefer the // first error. Err(Error::Fs(parent_directory_error.unwrap_or(err))) } } } } fn symlink_dir( path_for_symlink: &Path, link: &Path, parent_directory_error: Option, ) -> Result<()> { match fs::symlink_dir(path_for_symlink, link) { // As long as symlink_dir succeeded, ignore any create_dir_all error. Ok(()) => Ok(()), // If create_dir_all and symlink_dir both failed, prefer the first error. Err(err) => Err(Error::Fs(parent_directory_error.unwrap_or(err))), } } fn best_effort_remove(path: &Path) { use std::fs; if cfg!(windows) { // On Windows, the correct choice of remove_file vs remove_dir needs to // be used according to what the symlink *points to*. Trying to use // remove_file to remove a symlink which points to a directory fails // with "Access is denied". if let Ok(metadata) = fs::metadata(path) { if metadata.is_dir() { let _ = fs::remove_dir_all(path); } else { let _ = fs::remove_file(path); } } else if fs::symlink_metadata(path).is_ok() { // The symlink might exist but be dangling, in which case there is // no standard way to determine what "kind" of symlink it is. Try // deleting both ways. if fs::remove_dir_all(path).is_err() { let _ = fs::remove_file(path); } } } else { // On non-Windows, we check metadata not following symlinks. All // symlinks are removed using remove_file. if let Ok(metadata) = fs::symlink_metadata(path) { if metadata.is_dir() { let _ = fs::remove_dir_all(path); } else { let _ = fs::remove_file(path); } } } } fn best_effort_relativize_symlink(original: impl AsRef, link: impl AsRef) -> PathBuf { let original = original.as_ref(); let link = link.as_ref(); let Some(relative_path) = abstractly_relativize_symlink(original, link) else { return original.to_path_buf(); }; // Sometimes "a/b/../c" refers to a different canonical location than "a/c". // This can happen if 'b' is a symlink. The '..' canonicalizes to the parent // directory of the symlink's target, not back to 'a'. In cxx-build's case // someone could be using `--target-dir` with a location containing such // symlinks. if let Ok(original_canonical) = original.canonicalize() { if let Ok(relative_canonical) = link.parent().unwrap().join(&relative_path).canonicalize() { if original_canonical == relative_canonical { return relative_path; } } } original.to_path_buf() } fn abstractly_relativize_symlink( original: impl AsRef, link: impl AsRef, ) -> Option { let original = original.as_ref(); let link = link.as_ref(); // Relativization only makes sense if there is a semantically meaningful // base directory shared between the two paths. // // For example /Volumes/code/library/src/lib.rs // and /Volumes/code/library/target/path/to/something.a // have a meaningful shared base of /Volumes/code/library. The target and // source directory only likely ever get relocated as one unit. // // On the other hand, /Volumes/code/library/src/lib.rs // and /Volumes/shared_target // do not, since upon moving library to a different location it should // continue referring to the original location of that shared Cargo target // directory. let likely_no_semantic_prefix = env::var_os("CARGO_TARGET_DIR").is_some(); if likely_no_semantic_prefix || original.is_relative() || link.is_relative() || path_contains_intermediate_components(original) || path_contains_intermediate_components(link) { return None; } let (common_prefix, rest_of_original, rest_of_link) = split_after_common_prefix(original, link); if common_prefix == Path::new("") { return None; } let mut rest_of_link = rest_of_link.components(); rest_of_link .next_back() .expect("original can't be a subdirectory of link"); let mut path_to_common_prefix = PathBuf::new(); while rest_of_link.next_back().is_some() { path_to_common_prefix.push(Component::ParentDir); } Some(path_to_common_prefix.join(rest_of_original)) } fn path_contains_intermediate_components(path: impl AsRef) -> bool { path.as_ref() .components() .any(|component| component == Component::ParentDir) } fn split_after_common_prefix<'first, 'second>( first: &'first Path, second: &'second Path, ) -> (&'first Path, &'first Path, &'second Path) { let entire_first = first; let mut first = first.components(); let mut second = second.components(); loop { let rest_of_first = first.as_path(); let rest_of_second = second.as_path(); match (first.next(), second.next()) { (Some(first_component), Some(second_component)) if first_component == second_component => {} _ => { let mut common_prefix = entire_first; for _ in rest_of_first.components().rev() { if let Some(parent) = common_prefix.parent() { common_prefix = parent; } else { common_prefix = Path::new(""); break; } } return (common_prefix, rest_of_first, rest_of_second); } } } } #[cfg(test)] mod tests { use crate::out::abstractly_relativize_symlink; use std::path::Path; #[cfg(not(windows))] #[test] fn test_relativize_symlink_unix() { assert_eq!( abstractly_relativize_symlink("/foo/bar/baz", "/foo/spam/eggs").as_deref(), Some(Path::new("../bar/baz")), ); assert_eq!( abstractly_relativize_symlink("/foo/bar/../baz", "/foo/spam/eggs"), None, ); assert_eq!( abstractly_relativize_symlink("/foo/bar/baz", "/foo/spam/./eggs").as_deref(), Some(Path::new("../bar/baz")), ); } #[cfg(windows)] #[test] fn test_relativize_symlink_windows() { use std::path::PathBuf; let windows_target = PathBuf::from_iter(["c:\\", "windows", "foo"]); let windows_link = PathBuf::from_iter(["c:\\", "users", "link"]); let windows_different_volume_link = PathBuf::from_iter(["d:\\", "users", "link"]); assert_eq!( abstractly_relativize_symlink(&windows_target, windows_link).as_deref(), Some(Path::new("..\\windows\\foo")), ); assert_eq!( abstractly_relativize_symlink(&windows_target, windows_different_volume_link), None, ); } } ================================================ FILE: gen/build/src/paths.rs ================================================ use crate::error::Result; use crate::gen::fs; use std::ffi::OsStr; use std::path::{Component, Path, PathBuf}; pub(crate) fn manifest_dir() -> Result { crate::env_os("CARGO_MANIFEST_DIR").map(PathBuf::from) } pub(crate) fn out_dir() -> Result { crate::env_os("OUT_DIR").map(PathBuf::from) } // Given a path provided by the user, determines where generated files related // to that path should go in our out dir. In particular we don't want to // accidentally write generated code upward of our out dir, even if the user // passed a path containing lots of `..` or an absolute path. pub(crate) fn local_relative_path(path: &Path) -> PathBuf { let mut rel_path = PathBuf::new(); for component in path.components() { match component { Component::Prefix(_) | Component::RootDir | Component::CurDir => {} Component::ParentDir => drop(rel_path.pop()), // noop if empty Component::Normal(name) => rel_path.push(name), } } rel_path } pub(crate) trait PathExt { fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf; } impl PathExt for Path { fn with_appended_extension(&self, suffix: impl AsRef) -> PathBuf { let mut file_name = self.file_name().unwrap().to_owned(); file_name.push(suffix); self.with_file_name(file_name) } } #[cfg(unix)] pub(crate) fn symlink_or_copy( path_for_symlink: impl AsRef, _path_for_copy: impl AsRef, link: impl AsRef, ) -> fs::Result<()> { fs::symlink_file(path_for_symlink, link) } #[cfg(windows)] pub(crate) fn symlink_or_copy( path_for_symlink: impl AsRef, path_for_copy: impl AsRef, link: impl AsRef, ) -> fs::Result<()> { // Pre-Windows 10, symlinks require admin privileges. Since Windows 10, they // require Developer Mode. If it fails, fall back to copying the file. let path_for_symlink = path_for_symlink.as_ref(); let link = link.as_ref(); if fs::symlink_file(path_for_symlink, link).is_err() { let path_for_copy = path_for_copy.as_ref(); fs::copy(path_for_copy, link)?; } Ok(()) } #[cfg(not(any(unix, windows)))] pub(crate) fn symlink_or_copy( _path_for_symlink: impl AsRef, path_for_copy: impl AsRef, copy: impl AsRef, ) -> fs::Result<()> { fs::copy(path_for_copy, copy)?; Ok(()) } ================================================ FILE: gen/build/src/target.rs ================================================ use std::env; use std::ffi::OsStr; use std::path::{Path, PathBuf}; pub(crate) enum TargetDir { Path(PathBuf), Unknown, } pub(crate) fn find_target_dir(out_dir: &Path) -> TargetDir { if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") { let target_dir = PathBuf::from(target_dir); return if target_dir.is_absolute() { TargetDir::Path(target_dir) } else { TargetDir::Unknown }; } // fs::canonicalize on Windows produces UNC paths which cl.exe is unable to // handle in includes. // https://github.com/rust-lang/rust/issues/42869 // https://github.com/alexcrichton/cc-rs/issues/169 let mut also_try_canonical = cfg!(not(windows)); let mut dir = out_dir.to_owned(); loop { if dir.join(".rustc_info.json").exists() || dir.join("CACHEDIR.TAG").exists() || dir.file_name() == Some(OsStr::new("target")) && dir .parent() .is_some_and(|parent| parent.join("Cargo.toml").exists()) { return TargetDir::Path(dir); } if dir.pop() { continue; } if also_try_canonical { if let Ok(canonical_dir) = out_dir.canonicalize() { dir = canonical_dir; also_try_canonical = false; continue; } } return TargetDir::Unknown; } } ================================================ FILE: gen/build/src/vec.rs ================================================ use crate::intern::{self, InternedString}; use std::path::Path; pub(crate) trait InternedVec where T: ?Sized, { fn vec(&self) -> Vec<&'static T>; } impl InternedVec for Vec where T: ?Sized + Element, { fn vec(&self) -> Vec<&'static T> { self.iter().copied().map(Element::unintern).collect() } } pub(crate) fn intern(elements: &[&T]) -> Vec where T: ?Sized + Element, { elements.iter().copied().map(Element::intern).collect() } pub(crate) trait Element { fn intern(&self) -> InternedString; fn unintern(_: InternedString) -> &'static Self; } impl Element for str { fn intern(&self) -> InternedString { intern::intern(self) } fn unintern(interned: InternedString) -> &'static Self { interned.str() } } impl Element for Path { fn intern(&self) -> InternedString { intern::intern(&self.to_string_lossy()) } fn unintern(interned: InternedString) -> &'static Self { Path::new(interned.str()) } } ================================================ FILE: gen/cmd/Cargo.toml ================================================ [package] name = "cxxbridge-cmd" version = "1.0.194" authors = ["David Tolnay "] categories = ["development-tools::build-utils", "development-tools::ffi"] description = "C++ code generator for integrating `cxx` crate into a non-Cargo build." edition = "2021" exclude = ["build.rs"] homepage = "https://cxx.rs" keywords = ["ffi"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/cxx" rust-version = "1.85" [[bin]] name = "cxxbridge" path = "src/main.rs" [dependencies] clap = { version = "4.3.11", default-features = false, features = ["error-context", "help", "std", "suggestions", "usage"] } codespan-reporting = "0.13.1" indexmap = "2.9.0" proc-macro2 = { version = "1.0.74", default-features = false, features = ["span-locations"] } quote = { version = "1.0.35", default-features = false } syn = { version = "2.0.46", default-features = false, features = ["clone-impls", "full", "parsing", "printing"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] ================================================ FILE: gen/cmd/build.rs ================================================ include!("../../tools/cargo/build.rs"); ================================================ FILE: gen/cmd/src/app.rs ================================================ #[cfg(test)] #[path = "test.rs"] mod test; use super::{Opt, Output}; use crate::cfg::{self, CfgValue}; use crate::gen::include::Include; use crate::syntax::IncludeKind; use clap::builder::{ArgAction, ValueParser}; use clap::{Arg, Command}; use std::collections::{BTreeMap as Map, BTreeSet as Set}; use std::path::PathBuf; use std::process; use std::sync::{Arc, Mutex, PoisonError}; use syn::parse::Parser; const USAGE: &str = "\ cxxbridge .rs Emit .cc file for bridge to stdout cxxbridge .rs --header Emit .h file for bridge to stdout cxxbridge --header Emit \"rust/cxx.h\" header to stdout\ "; const TEMPLATE: &str = "\ {bin} {version} David Tolnay https://github.com/dtolnay/cxx {usage-heading} {usage} {all-args}\ "; fn app() -> Command { let mut app = Command::new("cxxbridge") .override_usage(USAGE) .help_template(TEMPLATE) .next_line_help(true) .disable_help_flag(true) .disable_version_flag(true) .arg(arg_input()) .arg(arg_cfg()) .arg(arg_cxx_impl_annotations()) .arg(arg_header()) .arg(arg_help()) .arg(arg_include()) .arg(arg_output()); if let Some(version) = option_env!("CARGO_PKG_VERSION") { app = app.arg(arg_version()).version(version); } app } const INPUT: &str = "input"; const CFG: &str = "cfg"; const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations"; const HELP: &str = "help"; const HEADER: &str = "header"; const INCLUDE: &str = "include"; const OUTPUT: &str = "output"; const VERSION: &str = "version"; pub(super) fn from_args() -> Opt { let matches = app().get_matches(); if matches.get_flag(HELP) { let _ = app().print_long_help(); process::exit(0); } let input = matches.get_one::(INPUT).cloned(); let cxx_impl_annotations = matches .get_one::(CXX_IMPL_ANNOTATIONS) .map(String::clone); let header = matches.get_flag(HEADER); let include = matches .get_many::(INCLUDE) .unwrap_or_default() .map(|include| { if include.starts_with('<') && include.ends_with('>') { Include { path: include[1..include.len() - 1].to_owned(), kind: IncludeKind::Bracketed, } } else { Include { path: include.clone(), kind: IncludeKind::Quoted, } } }) .collect(); let mut outputs = Vec::new(); for path in matches.get_many::(OUTPUT).unwrap_or_default() { outputs.push(if path.as_os_str() == "-" { Output::Stdout } else { Output::File(path.clone()) }); } if outputs.is_empty() { outputs.push(Output::Stdout); } let mut cfg = Map::new(); for arg in matches.get_many::(CFG).unwrap_or_default() { let (name, value) = cfg::parse.parse_str(arg).unwrap(); cfg.entry(name).or_insert_with(Set::new).insert(value); } Opt { input, header, cxx_impl_annotations, include, outputs, cfg, } } fn arg_input() -> Arg { Arg::new(INPUT) .help("Input Rust source file containing #[cxx::bridge].") .required_unless_present_any([HEADER, HELP]) .value_parser(ValueParser::path_buf()) } fn arg_cfg() -> Arg { const HELP: &str = "\ Compilation configuration matching what will be used to build the Rust side of the bridge."; let bool_cfgs = Arc::new(Mutex::new(Map::::new())); Arg::new(CFG) .long(CFG) .num_args(1) .value_name("name=\"value\" | name[=true] | name=false") .action(ArgAction::Append) .value_parser(move |arg: &str| match cfg::parse.parse_str(arg) { Ok((_, CfgValue::Str(_))) => Ok(arg.to_owned()), Ok((name, CfgValue::Bool(value))) => { let mut bool_cfgs = bool_cfgs.lock().unwrap_or_else(PoisonError::into_inner); if let Some(&prev) = bool_cfgs.get(&name) { if prev != value { return Err(format!("cannot have both {0}=false and {0}=true", name)); } } bool_cfgs.insert(name, value); Ok(arg.to_owned()) } Err(_) => Err("expected name=\"value\", name=true, or name=false".to_owned()), }) .help(HELP) } fn arg_cxx_impl_annotations() -> Arg { const HELP: &str = "\ Optional annotation for implementations of C++ function wrappers that may be exposed to Rust. You may for example need to provide __declspec(dllexport) or __attribute__((visibility(\"default\"))) if Rust code from one shared object or executable depends on these C++ functions in another."; Arg::new(CXX_IMPL_ANNOTATIONS) .long(CXX_IMPL_ANNOTATIONS) .num_args(1) .value_name("annotation") .value_parser(ValueParser::string()) .help(HELP) } fn arg_header() -> Arg { const HELP: &str = "\ Emit header with declarations only. Optional if using `-o` with a path ending in `.h`."; Arg::new(HEADER).long(HEADER).num_args(0).help(HELP) } fn arg_help() -> Arg { Arg::new(HELP) .long(HELP) .help("Print help information.") .num_args(0) } fn arg_include() -> Arg { const HELP: &str = "\ Any additional headers to #include. The cxxbridge tool does not parse or even require the given paths to exist; they simply go into the generated C++ code as #include lines."; Arg::new(INCLUDE) .long(INCLUDE) .short('i') .num_args(1) .action(ArgAction::Append) .value_parser(ValueParser::string()) .help(HELP) } fn arg_output() -> Arg { const HELP: &str = "\ Path of file to write as output. Output goes to stdout if -o is not specified."; Arg::new(OUTPUT) .long(OUTPUT) .short('o') .num_args(1) .action(ArgAction::Append) .value_parser(ValueParser::path_buf()) .help(HELP) } fn arg_version() -> Arg { Arg::new(VERSION) .long(VERSION) .help("Print version information.") .action(ArgAction::Version) } ================================================ FILE: gen/cmd/src/cfg.rs ================================================ use crate::gen::{CfgEvaluator, CfgResult}; use std::collections::{BTreeMap as Map, BTreeSet as Set}; use std::fmt::{self, Debug}; use syn::parse::ParseStream; use syn::{Ident, LitBool, LitStr, Token}; #[derive(Ord, PartialOrd, Eq, PartialEq)] pub(crate) enum CfgValue { Bool(bool), Str(String), } impl CfgValue { const FALSE: Self = CfgValue::Bool(false); const TRUE: Self = CfgValue::Bool(true); } pub(crate) struct FlagsCfgEvaluator { map: Map>, } impl FlagsCfgEvaluator { pub(crate) fn new(map: Map>) -> Self { FlagsCfgEvaluator { map } } } impl CfgEvaluator for FlagsCfgEvaluator { fn eval(&self, name: &str, value: Option<&str>) -> CfgResult { let set = self.map.get(name); if let Some(value) = value { if let Some(set) = set { CfgResult::from(set.contains(&CfgValue::Str(value.to_owned()))) } else if name == "feature" { CfgResult::False } else { let msg = format!( "pass `--cfg {}=\"...\"` to be able to use this attribute", name, ); CfgResult::Undetermined { msg } } } else { let (mut is_false, mut is_true) = (false, false); if let Some(set) = set { is_false = set.contains(&CfgValue::FALSE); is_true = set.contains(&CfgValue::TRUE); } if is_false && is_true { let msg = format!("the cxxbridge flags say both {0}=false and {0}=true", name); CfgResult::Undetermined { msg } } else if is_false { CfgResult::False } else if is_true { CfgResult::True } else { let msg = format!( "pass either `--cfg {0}=true` or `--cfg {0}=false` to be able to use this cfg attribute", name, ); CfgResult::Undetermined { msg } } } } } impl Debug for CfgValue { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self { CfgValue::Bool(value) => Debug::fmt(value, formatter), CfgValue::Str(value) => Debug::fmt(value, formatter), } } } pub(crate) fn parse(input: ParseStream) -> syn::Result<(String, CfgValue)> { let ident: Ident = input.parse()?; let name = ident.to_string(); if input.is_empty() { return Ok((name, CfgValue::TRUE)); } input.parse::()?; let lookahead = input.lookahead1(); if lookahead.peek(LitBool) { let lit: LitBool = input.parse()?; Ok((name, CfgValue::Bool(lit.value))) } else if lookahead.peek(LitStr) { let lit: LitStr = input.parse()?; Ok((name, CfgValue::Str(lit.value()))) } else { Err(lookahead.error()) } } ================================================ FILE: gen/cmd/src/main.rs ================================================ #![cfg_attr(not(check_cfg), allow(unexpected_cfgs))] #![allow( clippy::cast_sign_loss, clippy::default_trait_access, clippy::elidable_lifetime_names, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, // https://github.com/rust-lang/rust-clippy/issues/15842 clippy::inherent_to_string, clippy::items_after_statements, clippy::map_clone, clippy::match_bool, clippy::match_like_matches_macro, clippy::match_same_arms, clippy::needless_continue, clippy::needless_lifetimes, clippy::needless_pass_by_value, clippy::nonminimal_bool, clippy::precedence, clippy::redundant_else, clippy::ref_option, clippy::similar_names, clippy::single_match_else, clippy::struct_excessive_bools, clippy::struct_field_names, clippy::too_many_arguments, clippy::too_many_lines, clippy::toplevel_ref_arg, clippy::uninlined_format_args )] #![allow(unknown_lints, mismatched_lifetime_syntaxes)] mod app; mod cfg; mod gen; mod output; mod syntax; use crate::cfg::{CfgValue, FlagsCfgEvaluator}; use crate::gen::error::{report, Result}; use crate::gen::fs; use crate::gen::include::{self, Include}; use crate::output::Output; use std::collections::{BTreeMap as Map, BTreeSet as Set}; use std::io::{self, Write}; use std::path::PathBuf; use std::process; #[derive(Debug)] struct Opt { input: Option, header: bool, cxx_impl_annotations: Option, include: Vec, outputs: Vec, cfg: Map>, } fn main() { if let Err(err) = try_main() { let _ = writeln!(io::stderr(), "cxxbridge: {}", report(err)); process::exit(1); } } enum Kind { GeneratedHeader, GeneratedImplementation, Header, } fn try_main() -> Result<()> { let opt = app::from_args(); let mut outputs = Vec::new(); let mut gen_header = false; let mut gen_implementation = false; for output in opt.outputs { let kind = if opt.input.is_none() { Kind::Header } else if opt.header || output.ends_with(".h") || output.ends_with(".hh") || output.ends_with(".hpp") { gen_header = true; Kind::GeneratedHeader } else { gen_implementation = true; Kind::GeneratedImplementation }; outputs.push((output, kind)); } let gen = gen::Opt { include: opt.include, cxx_impl_annotations: opt.cxx_impl_annotations, gen_header, gen_implementation, cfg_evaluator: Box::new(FlagsCfgEvaluator::new(opt.cfg)), ..Default::default() }; let generated_code = if let Some(input) = opt.input { gen::generate_from_path(&input, &gen) } else { Default::default() }; for (output, kind) in outputs { let content = match kind { Kind::GeneratedHeader => &generated_code.header, Kind::GeneratedImplementation => &generated_code.implementation, Kind::Header => include::HEADER.as_bytes(), }; match output { Output::Stdout => drop(io::stdout().write_all(content)), Output::File(path) => fs::write(path, content)?, } } Ok(()) } ================================================ FILE: gen/cmd/src/output.rs ================================================ use std::path::PathBuf; #[derive(Debug)] pub(crate) enum Output { Stdout, File(PathBuf), } impl Output { pub(crate) fn ends_with(&self, suffix: &str) -> bool { match self { Output::Stdout => false, Output::File(path) => path.to_string_lossy().ends_with(suffix), } } } ================================================ FILE: gen/cmd/src/test.rs ================================================ const EXPECTED: &str = "\ cxxbridge $VERSION David Tolnay https://github.com/dtolnay/cxx Usage: cxxbridge .rs Emit .cc file for bridge to stdout cxxbridge .rs --header Emit .h file for bridge to stdout cxxbridge --header Emit \"rust/cxx.h\" header to stdout Arguments: [input] Input Rust source file containing #[cxx::bridge]. Options: --cfg Compilation configuration matching what will be used to build the Rust side of the bridge. --cxx-impl-annotations Optional annotation for implementations of C++ function wrappers that may be exposed to Rust. You may for example need to provide __declspec(dllexport) or __attribute__((visibility(\"default\"))) if Rust code from one shared object or executable depends on these C++ functions in another. --header Emit header with declarations only. Optional if using `-o` with a path ending in `.h`. --help Print help information. -i, --include Any additional headers to #include. The cxxbridge tool does not parse or even require the given paths to exist; they simply go into the generated C++ code as #include lines. -o, --output Path of file to write as output. Output goes to stdout if -o is not specified. --version Print version information. "; #[test] fn test_help() { let mut app = super::app(); let mut out = Vec::new(); app.write_long_help(&mut out).unwrap(); let help = String::from_utf8(out).unwrap(); let version = option_env!("CARGO_PKG_VERSION").unwrap_or_default(); let expected = EXPECTED.replace("$VERSION", version); assert_eq!(help, expected); } #[test] fn test_cli() { let app = super::app(); app.debug_assert(); } ================================================ FILE: gen/lib/Cargo.toml ================================================ [package] name = "cxx-gen" version = "0.7.194" authors = ["Adrian Taylor "] categories = ["development-tools::ffi"] description = "C++ code generator for integrating `cxx` crate into higher level tools." documentation = "https://docs.rs/cxx-gen" edition = "2021" exclude = ["build.rs"] keywords = ["ffi"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/cxx" rust-version = "1.85" [dependencies] codespan-reporting = "0.13.1" indexmap = "2.9.0" proc-macro2 = { version = "1.0.74", default-features = false, features = ["span-locations"] } quote = { version = "1.0.35", default-features = false } syn = { version = "2.0.46", default-features = false, features = ["clone-impls", "full", "parsing", "printing"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--generate-link-to-definition", "--generate-macro-expansion", "--extern-html-root-url=core=https://doc.rust-lang.org", "--extern-html-root-url=alloc=https://doc.rust-lang.org", "--extern-html-root-url=std=https://doc.rust-lang.org", ] ================================================ FILE: gen/lib/build.rs ================================================ include!("../../tools/cargo/build.rs"); ================================================ FILE: gen/lib/src/error.rs ================================================ // We can expose more detail on the error as the need arises, but start with an // opaque error type for now. use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; use std::iter; #[allow(missing_docs)] pub struct Error { pub(crate) err: crate::gen::Error, } impl Error { /// Returns the span of the error, if available. pub fn span(&self) -> Option { match &self.err { crate::gen::Error::Syn(err) => Some(err.span()), _ => None, } } } impl From for Error { fn from(err: crate::gen::Error) -> Self { Error { err } } } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&self.err, f) } } impl Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Debug::fmt(&self.err, f) } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { self.err.source() } } impl IntoIterator for Error { type Item = Error; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { match self.err { crate::gen::Error::Syn(err) => IntoIter::Syn(err.into_iter()), _ => IntoIter::Other(iter::once(self)), } } } pub enum IntoIter { Syn(::IntoIter), Other(iter::Once), } impl Iterator for IntoIter { type Item = Error; fn next(&mut self) -> Option { match self { IntoIter::Syn(iter) => iter .next() .map(|syn_err| Error::from(crate::gen::Error::Syn(syn_err))), IntoIter::Other(iter) => iter.next(), } } } ================================================ FILE: gen/lib/src/lib.rs ================================================ //! The CXX code generator for constructing and compiling C++ code. //! //! This is intended as a mechanism for embedding the `cxx` crate into //! higher-level code generators. See [dtolnay/cxx#235] and //! [https://github.com/google/autocxx]. //! //! [dtolnay/cxx#235]: https://github.com/dtolnay/cxx/issues/235 //! [https://github.com/google/autocxx]: https://github.com/google/autocxx #![doc(html_root_url = "https://docs.rs/cxx-gen/0.7.194")] #![deny(missing_docs)] #![expect(dead_code)] #![cfg_attr(not(check_cfg), allow(unexpected_cfgs))] #![allow( clippy::cast_sign_loss, clippy::default_trait_access, clippy::elidable_lifetime_names, clippy::enum_glob_use, clippy::expl_impl_clone_on_copy, // https://github.com/rust-lang/rust-clippy/issues/15842 clippy::inherent_to_string, clippy::items_after_statements, clippy::match_bool, clippy::match_like_matches_macro, clippy::match_same_arms, clippy::missing_errors_doc, clippy::must_use_candidate, clippy::needless_continue, clippy::needless_lifetimes, clippy::needless_pass_by_value, clippy::nonminimal_bool, clippy::precedence, clippy::redundant_else, clippy::ref_option, clippy::similar_names, clippy::single_match_else, clippy::struct_excessive_bools, clippy::struct_field_names, clippy::too_many_arguments, clippy::too_many_lines, clippy::toplevel_ref_arg, clippy::uninlined_format_args )] #![allow(unknown_lints, mismatched_lifetime_syntaxes)] mod error; mod gen; mod syntax; pub use crate::error::Error; pub use crate::gen::include::{Include, HEADER}; pub use crate::gen::{CfgEvaluator, CfgResult, GeneratedCode, Opt}; pub use crate::syntax::IncludeKind; use proc_macro2::TokenStream; /// Generate C++ bindings code from a Rust token stream. This should be a Rust /// token stream which somewhere contains a `#[cxx::bridge] mod {}`. pub fn generate_header_and_cc(rust_source: TokenStream, opt: &Opt) -> Result { let syntax = syn::parse2(rust_source) .map_err(crate::gen::Error::from) .map_err(Error::from)?; gen::generate(syntax, opt).map_err(Error::from) } ================================================ FILE: gen/lib/tests/test.rs ================================================ use cxx_gen::Opt; use quote::quote; #[test] fn test_positive() { let rs = quote! { #[cxx::bridge] mod ffi { unsafe extern "C++" { fn in_C(); } extern "Rust" { fn in_rs(); } } }; let opt = Opt::default(); let code = cxx_gen::generate_header_and_cc(rs, &opt).unwrap(); assert!(!code.header.is_empty()); assert!(!code.implementation.is_empty()); } #[test] fn test_negative() { let rs = quote! {}; let opt = Opt::default(); assert!(cxx_gen::generate_header_and_cc(rs, &opt).is_err()); } ================================================ FILE: gen/src/block.rs ================================================ use proc_macro2::Ident; #[derive(Copy, Clone, PartialEq, Debug)] pub(crate) enum Block<'a> { AnonymousNamespace, Namespace(&'a str), UserDefinedNamespace(&'a Ident), InlineNamespace(&'a str), ExternC, } impl<'a> Block<'a> { pub(crate) fn write_begin(self, out: &mut String) { if let Block::InlineNamespace(_) = self { out.push_str("inline "); } self.write_common(out); out.push_str(" {\n"); } pub(crate) fn write_end(self, out: &mut String) { out.push_str("} // "); self.write_common(out); out.push('\n'); } fn write_common(self, out: &mut String) { match self { Block::AnonymousNamespace => out.push_str("namespace"), Block::Namespace(name) => { out.push_str("namespace "); out.push_str(name); } Block::UserDefinedNamespace(name) => { out.push_str("namespace "); out.push_str(&name.to_string()); } Block::InlineNamespace(name) => { out.push_str("namespace "); out.push_str(name); } Block::ExternC => out.push_str("extern \"C\""), } } } ================================================ FILE: gen/src/builtin/alignmax.h ================================================ #pragma once #include namespace rust { inline namespace cxxbridge1 { namespace repr { #ifndef CXXBRIDGE_ALIGNMAX #define CXXBRIDGE_ALIGNMAX // This would be cleaner as the following, but GCC does not implement that // correctly. // // template <::std::size_t... N> // class alignas(N...) alignmax {}; // // Next, it could be this, but MSVC does not implement this correctly. // // template <::std::size_t... N> // class alignmax { alignas(N...) union {} members; }; // template <::std::size_t N> class alignas(N) aligned {}; // template class alignmax_t { alignas(T...) union {} members; }; // template <::std::size_t... N> using alignmax = alignmax_t...>; #endif // CXXBRIDGE_ALIGNMAX } // namespace repr } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/deleter_if.h ================================================ #pragma once namespace rust { inline namespace cxxbridge1 { namespace { template struct deleter_if { template void operator()(T *) {} }; // template <> struct deleter_if { template void operator()(T *ptr) { ptr->~T(); } }; } // namespace } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/destroy.h ================================================ #pragma once namespace rust { inline namespace cxxbridge1 { namespace { template void destroy(T *ptr) { ptr->~T(); } } // namespace } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/friend_impl.h ================================================ #pragma once namespace rust { inline namespace cxxbridge1 { namespace { template class impl; } // namespace } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/manually_drop.h ================================================ #pragma once #include #pragma GCC diagnostic ignored "-Wshadow" namespace rust { inline namespace cxxbridge1 { template union ManuallyDrop { T value; ManuallyDrop(T &&value) : value(::std::move(value)) {} ~ManuallyDrop() {} }; } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/maybe_uninit.h ================================================ #pragma once #include "./maybe_uninit_detail.h" #include namespace rust { inline namespace cxxbridge1 { template union MaybeUninit { T value; void *operator new(::std::size_t sz) { return detail::operator_new{}(sz); } MaybeUninit() {} ~MaybeUninit() {} }; } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/maybe_uninit_detail.h ================================================ #pragma once #include #include namespace rust { inline namespace cxxbridge1 { namespace detail { template struct operator_new { void *operator()(::std::size_t sz) { return ::operator new(sz); } }; template struct operator_new { void *operator()(::std::size_t sz) { return T::operator new(sz); } }; } // namespace detail } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/ptr_len.h ================================================ #pragma once #include namespace rust { inline namespace cxxbridge1 { namespace repr { struct PtrLen final { void *ptr; ::std::size_t len; }; } // namespace repr } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/relocatable_or_array.h ================================================ #pragma once #include "../../../include/cxx.h" #include namespace rust { inline namespace cxxbridge1 { namespace { template struct IsRelocatableOrArray : IsRelocatable {}; // template struct IsRelocatableOrArray : IsRelocatableOrArray {}; } // namespace } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/repr_fat.h ================================================ #pragma once #include #include namespace rust { inline namespace cxxbridge1 { namespace repr { using Fat = ::std::array<::std::uintptr_t, 2>; } // namespace repr } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/rust_error.h ================================================ #pragma once #include "../../../include/cxx.h" #include "./friend_impl.h" #include "./ptr_len.h" namespace rust { inline namespace cxxbridge1 { namespace { template <> class impl final { public: static Error error(repr::PtrLen repr) noexcept { Error error; error.msg = static_cast(repr.ptr); error.len = repr.len; return error; } }; } // namespace } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/rust_slice_uninit.h ================================================ #pragma once #include "../../../include/cxx.h" namespace rust { inline namespace cxxbridge1 { template class Slice::uninit {}; // template inline Slice::Slice(uninit) noexcept {} } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/rust_str_uninit.h ================================================ #pragma once #include "../../../include/cxx.h" namespace rust { inline namespace cxxbridge1 { class Str::uninit {}; // inline Str::Str(uninit) noexcept {} } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/shared_ptr.h ================================================ #pragma once #include "../../../include/cxx.h" #include namespace rust { inline namespace cxxbridge1 { namespace { template ::value> struct is_destructible : ::std::false_type {}; // template struct is_destructible : ::std::is_destructible {}; // template struct is_destructible : is_destructible {}; // template ::value> struct shared_ptr_if_destructible { explicit shared_ptr_if_destructible(typename ::std::shared_ptr::element_type *) {} }; // template struct shared_ptr_if_destructible : ::std::shared_ptr { using ::std::shared_ptr::shared_ptr; }; } // namespace } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/trycatch.h ================================================ #pragma once #include "./trycatch_detail.h" #include #include #include namespace rust { namespace behavior { class missing {}; missing trycatch(...); template static typename ::std::enable_if<::std::is_same< decltype(trycatch(::std::declval(), ::std::declval())), missing>::value>::type trycatch(Try &&func, Fail &&fail) noexcept try { func(); } catch (::std::exception const &e) { fail(e.what()); } } // namespace behavior } // namespace rust ================================================ FILE: gen/src/builtin/trycatch_detail.h ================================================ #pragma once #include "./ptr_len.h" #include #pragma GCC diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" namespace rust { inline namespace cxxbridge1 { namespace detail { class Fail final { ::rust::repr::PtrLen &throw$; // public: Fail(::rust::repr::PtrLen &throw$) noexcept : throw$(throw$) {} void operator()(char const *) noexcept; void operator()(std::string const &) noexcept; }; } // namespace detail } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin/vector.h ================================================ #pragma once #include "../../../include/cxx.h" #include #include namespace rust { inline namespace cxxbridge1 { namespace { template ::value> struct if_move_constructible { static bool reserve(::std::vector &, ::std::size_t) noexcept { return false; } }; // template struct if_move_constructible { static bool reserve(::std::vector &vec, ::std::size_t new_cap) { vec.reserve(new_cap); return true; } }; } // namespace } // namespace cxxbridge1 } // namespace rust ================================================ FILE: gen/src/builtin.rs ================================================ use crate::gen::block::Block; use crate::gen::ifndef; use crate::gen::include::Includes; use crate::gen::out::{Content, OutFile}; use crate::gen::pragma::Pragma; #[derive(Default, PartialEq)] pub(crate) struct Builtins<'a> { pub panic: bool, pub rust_string: bool, pub rust_str: bool, pub rust_slice: bool, pub rust_box: bool, pub rust_vec: bool, pub rust_fn: bool, pub rust_isize: bool, pub opaque: bool, pub layout: bool, pub unsafe_bitcopy: bool, pub unsafe_bitcopy_t: bool, pub rust_error: bool, pub manually_drop: bool, pub maybe_uninit: bool, pub trycatch: bool, pub ptr_len: bool, pub repr_fat: bool, pub rust_str_new_unchecked: bool, pub rust_str_repr: bool, pub rust_slice_new: bool, pub rust_slice_repr: bool, pub relocatable: bool, pub relocatable_or_array: bool, pub friend_impl: bool, pub is_complete: bool, pub destroy: bool, pub deleter_if: bool, pub shared_ptr: bool, pub vector: bool, pub alignmax: bool, pub content: Content<'a>, } impl<'a> Builtins<'a> { pub(crate) fn new() -> Self { Builtins::default() } } pub(super) fn write(out: &mut OutFile) { if out.builtin == Default::default() { return; } let include = &mut out.include; let pragma = &mut out.pragma; let builtin = &mut out.builtin; let out = &mut builtin.content; if builtin.rust_string { include.array = true; include.cstdint = true; include.string = true; } if builtin.rust_str { include.array = true; include.cstdint = true; include.string = true; include.string_view = true; builtin.friend_impl = true; } if builtin.rust_vec { include.algorithm = true; include.array = true; include.cassert = true; include.cstddef = true; include.cstdint = true; include.initializer_list = true; include.iterator = true; include.new = true; include.stdexcept = true; include.type_traits = true; include.utility = true; builtin.panic = true; builtin.rust_slice = true; builtin.unsafe_bitcopy_t = true; } if builtin.rust_slice { include.array = true; include.cassert = true; include.cstddef = true; include.cstdint = true; include.iterator = true; include.ranges = true; include.stdexcept = true; include.type_traits = true; builtin.friend_impl = true; builtin.layout = true; builtin.panic = true; } if builtin.rust_box { include.new = true; include.type_traits = true; include.utility = true; } if builtin.rust_fn { include.utility = true; } if builtin.rust_error { include.exception = true; builtin.friend_impl = true; } if builtin.rust_isize { include.basetsd = true; include.sys_types = true; } if builtin.relocatable_or_array { include.cstddef = true; builtin.relocatable = true; } if builtin.relocatable { include.type_traits = true; } if builtin.layout { include.type_traits = true; include.cstddef = true; builtin.is_complete = true; } if builtin.shared_ptr { include.memory = true; include.type_traits = true; builtin.is_complete = true; } if builtin.is_complete { include.cstddef = true; include.type_traits = true; } if builtin.unsafe_bitcopy { builtin.unsafe_bitcopy_t = true; } if builtin.trycatch { builtin.ptr_len = true; } out.begin_block(Block::Namespace("rust")); out.begin_block(Block::InlineNamespace("cxxbridge1")); let cxx_header = include.has_cxx_header(); if !cxx_header { writeln!(out, "// #include \"rust/cxx.h\""); ifndef::write(out, builtin.panic, "CXXBRIDGE1_PANIC"); if builtin.rust_string { out.next_section(); writeln!(out, "struct unsafe_bitcopy_t;"); } if builtin.friend_impl { out.begin_block(Block::AnonymousNamespace); writeln!(out, "template "); writeln!(out, "class impl;"); out.end_block(Block::AnonymousNamespace); } out.next_section(); if builtin.rust_str && !builtin.rust_string { writeln!(out, "class String;"); } if builtin.layout && !builtin.opaque { writeln!(out, "class Opaque;"); } if builtin.rust_slice { out.next_section(); writeln!(out, "template "); writeln!(out, "::std::size_t size_of();"); writeln!(out, "template "); writeln!(out, "::std::size_t align_of();"); } ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING"); ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR"); ifndef::write(out, builtin.rust_slice, "CXXBRIDGE1_RUST_SLICE"); ifndef::write(out, builtin.rust_box, "CXXBRIDGE1_RUST_BOX"); ifndef::write(out, builtin.unsafe_bitcopy_t, "CXXBRIDGE1_RUST_BITCOPY_T"); ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE1_RUST_BITCOPY"); ifndef::write(out, builtin.rust_vec, "CXXBRIDGE1_RUST_VEC"); ifndef::write(out, builtin.rust_fn, "CXXBRIDGE1_RUST_FN"); ifndef::write(out, builtin.rust_error, "CXXBRIDGE1_RUST_ERROR"); ifndef::write(out, builtin.rust_isize, "CXXBRIDGE1_RUST_ISIZE"); ifndef::write(out, builtin.opaque, "CXXBRIDGE1_RUST_OPAQUE"); ifndef::write(out, builtin.is_complete, "CXXBRIDGE1_IS_COMPLETE"); ifndef::write(out, builtin.layout, "CXXBRIDGE1_LAYOUT"); ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE"); } out.end_block(Block::InlineNamespace("cxxbridge1")); out.end_block(Block::Namespace("rust")); macro_rules! write_builtin { ($path:literal) => { write_builtin(out, include, pragma, include_str!($path)); }; } // namespace rust::cxxbridge1 if builtin.rust_str_new_unchecked { write_builtin!("builtin/rust_str_uninit.h"); } if builtin.rust_slice_new { write_builtin!("builtin/rust_slice_uninit.h"); } // namespace rust::cxxbridge1::repr if builtin.repr_fat { write_builtin!("builtin/repr_fat.h"); } if builtin.ptr_len { write_builtin!("builtin/ptr_len.h"); } if builtin.alignmax { write_builtin!("builtin/alignmax.h"); } // namespace rust::cxxbridge1::detail if builtin.maybe_uninit { write_builtin!("builtin/maybe_uninit_detail.h"); } if builtin.trycatch { write_builtin!("builtin/trycatch_detail.h"); } // namespace rust::cxxbridge1 if builtin.manually_drop { write_builtin!("builtin/manually_drop.h"); } if builtin.maybe_uninit { write_builtin!("builtin/maybe_uninit.h"); } out.begin_block(Block::Namespace("rust")); out.begin_block(Block::InlineNamespace("cxxbridge1")); out.begin_block(Block::AnonymousNamespace); if builtin.rust_str_new_unchecked || builtin.rust_str_repr { out.next_section(); writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); if builtin.rust_str_new_unchecked { writeln!( out, " static Str new_unchecked(repr::Fat repr) noexcept {{", ); writeln!(out, " Str str = Str::uninit{{}};"); writeln!(out, " str.repr = repr;"); writeln!(out, " return str;"); writeln!(out, " }}"); } if builtin.rust_str_repr { writeln!(out, " static repr::Fat repr(Str str) noexcept {{"); writeln!(out, " return str.repr;"); writeln!(out, " }}"); } writeln!(out, "}};"); } if builtin.rust_slice_new || builtin.rust_slice_repr { out.next_section(); writeln!(out, "template "); writeln!(out, "class impl> final {{"); writeln!(out, "public:"); if builtin.rust_slice_new { writeln!(out, " static Slice slice(repr::Fat repr) noexcept {{"); writeln!(out, " Slice slice = typename Slice::uninit{{}};"); writeln!(out, " slice.repr = repr;"); writeln!(out, " return slice;"); writeln!(out, " }}"); } if builtin.rust_slice_repr { writeln!(out, " static repr::Fat repr(Slice slice) noexcept {{"); writeln!(out, " return slice.repr;"); writeln!(out, " }}"); } writeln!(out, "}};"); } out.end_block(Block::AnonymousNamespace); out.end_block(Block::InlineNamespace("cxxbridge1")); out.end_block(Block::Namespace("rust")); // namespace rust::cxxbridge1::(anonymous) if builtin.rust_error { write_builtin!("builtin/rust_error.h"); } if builtin.destroy { write_builtin!("builtin/destroy.h"); } if builtin.deleter_if { write_builtin!("builtin/deleter_if.h"); } if builtin.shared_ptr { write_builtin!("builtin/shared_ptr.h"); } if builtin.vector { write_builtin!("builtin/vector.h"); } if builtin.relocatable_or_array { write_builtin!("builtin/relocatable_or_array.h"); } // namespace rust::behavior if builtin.trycatch { write_builtin!("builtin/trycatch.h"); } } fn write_builtin<'a>( out: &mut Content<'a>, include: &mut Includes, pragma: &mut Pragma<'a>, src: &'a str, ) { let mut namespace = Vec::new(); let mut ready = false; for line in src.lines() { if line == "#pragma once" || line.starts_with("#include \".") { continue; } else if let Some(rest) = line.strip_prefix("#include <") { let Includes { custom: _, algorithm, array, cassert, cstddef, cstdint, cstring, exception, functional, initializer_list, iterator, limits, memory, new, ranges, stdexcept, string, string_view, type_traits, utility, vector, basetsd: _, sys_types: _, content: _, } = include; match rest.strip_suffix(">").unwrap() { "algorithm" => *algorithm = true, "array" => *array = true, "cassert" => *cassert = true, "cstddef" => *cstddef = true, "cstdint" => *cstdint = true, "cstring" => *cstring = true, "exception" => *exception = true, "functional" => *functional = true, "initializer_list" => *initializer_list = true, "iterator" => *iterator = true, "limits" => *limits = true, "memory" => *memory = true, "new" => *new = true, "ranges" => *ranges = true, "stdexcept" => *stdexcept = true, "string" => *string = true, "string_view" => *string_view = true, "type_traits" => *type_traits = true, "utility" => *utility = true, "vector" => *vector = true, _ => unimplemented!("{}", line), } } else if let Some(rest) = line.strip_prefix("#pragma GCC diagnostic ignored \"") { let diagnostic = rest.strip_suffix('"').unwrap(); pragma.gnu_diagnostic_ignore.insert(diagnostic); ready = false; } else if let Some(rest) = line.strip_prefix("#pragma clang diagnostic ignored \"") { let diagnostic = rest.strip_suffix('"').unwrap(); pragma.clang_diagnostic_ignore.insert(diagnostic); ready = false; } else if line == "namespace {" { namespace.push(Block::AnonymousNamespace); out.begin_block(Block::AnonymousNamespace); } else if let Some(rest) = line.strip_prefix("namespace ") { let name = rest.strip_suffix(" {").unwrap(); namespace.push(Block::Namespace(name)); out.begin_block(Block::Namespace(name)); } else if let Some(rest) = line.strip_prefix("inline namespace ") { let name = rest.strip_suffix(" {").unwrap(); namespace.push(Block::InlineNamespace(name)); out.begin_block(Block::InlineNamespace(name)); } else if line.starts_with("} // namespace") { out.end_block(namespace.pop().unwrap()); } else if line.is_empty() && !ready { out.next_section(); ready = true; } else if !line.trim_start_matches(' ').starts_with("//") { assert!(ready); writeln!(out, "{}", line); } } assert!(namespace.is_empty()); assert!(ready); } #[cfg(test)] mod tests { use crate::gen::include::Includes; use crate::gen::out::Content; use crate::gen::pragma::Pragma; use std::fs; #[test] fn test_write_builtin() { let mut builtin_src = Vec::new(); for entry in fs::read_dir("src/gen/builtin").unwrap() { let path = entry.unwrap().path(); let src = fs::read_to_string(path).unwrap(); builtin_src.push(src); } assert_ne!(builtin_src.len(), 0); builtin_src.sort(); let mut content = Content::new(); let mut include = Includes::new(); let mut pragma = Pragma::new(); for src in &builtin_src { super::write_builtin(&mut content, &mut include, &mut pragma, src); } } } ================================================ FILE: gen/src/cfg.rs ================================================ use crate::gen::{CfgEvaluator, CfgResult}; use crate::syntax::cfg::CfgExpr; use crate::syntax::report::Errors; use crate::syntax::Api; use quote::quote; use std::collections::BTreeSet as Set; use std::mem; use syn::{Error, LitStr}; pub(super) struct UnsupportedCfgEvaluator; impl CfgEvaluator for UnsupportedCfgEvaluator { fn eval(&self, name: &str, value: Option<&str>) -> CfgResult { let _ = name; let _ = value; let msg = "cfg attribute is not supported".to_owned(); CfgResult::Undetermined { msg } } } pub(super) fn strip( cx: &mut Errors, cfg_errors: &mut Set, cfg_evaluator: &dyn CfgEvaluator, apis: &mut Vec, ) { let mut eval = |cfg: &mut CfgExpr| { let cfg = mem::replace(cfg, CfgExpr::Unconditional); self::eval(cx, cfg_errors, cfg_evaluator, &cfg) }; apis.retain_mut(|api| { eval(match api { Api::Include(include) => &mut include.cfg, Api::Struct(strct) => &mut strct.cfg, Api::Enum(enm) => &mut enm.cfg, Api::CxxType(ety) | Api::RustType(ety) => &mut ety.cfg, Api::CxxFunction(efn) | Api::RustFunction(efn) => &mut efn.cfg, Api::TypeAlias(alias) => &mut alias.cfg, Api::Impl(imp) => &mut imp.cfg, }) }); for api in apis { match api { Api::Struct(strct) => strct.fields.retain_mut(|field| eval(&mut field.cfg)), Api::Enum(enm) => enm.variants.retain_mut(|variant| eval(&mut variant.cfg)), _ => {} } } } pub(super) fn eval( cx: &mut Errors, cfg_errors: &mut Set, cfg_evaluator: &dyn CfgEvaluator, expr: &CfgExpr, ) -> bool { match try_eval(cfg_evaluator, expr) { Ok(value) => value, Err(errors) => { for error in errors { if cfg_errors.insert(error.to_string()) { cx.push(error); } } false } } } fn try_eval(cfg_evaluator: &dyn CfgEvaluator, expr: &CfgExpr) -> Result> { match expr { CfgExpr::Unconditional => Ok(true), CfgExpr::Eq(ident, string) => { let key = ident.to_string(); let value = string.as_ref().map(LitStr::value); match cfg_evaluator.eval(&key, value.as_deref()) { CfgResult::True => Ok(true), CfgResult::False => Ok(false), CfgResult::Undetermined { msg } => { let span = quote!(#ident #string); Err(vec![Error::new_spanned(span, msg)]) } } } CfgExpr::All(list) => { let mut all_errors = Vec::new(); for subexpr in list { match try_eval(cfg_evaluator, subexpr) { Ok(true) => {} Ok(false) => return Ok(false), Err(errors) => all_errors.extend(errors), } } if all_errors.is_empty() { Ok(true) } else { Err(all_errors) } } CfgExpr::Any(list) => { let mut all_errors = Vec::new(); for subexpr in list { match try_eval(cfg_evaluator, subexpr) { Ok(true) => return Ok(true), Ok(false) => {} Err(errors) => all_errors.extend(errors), } } if all_errors.is_empty() { Ok(false) } else { Err(all_errors) } } CfgExpr::Not(subexpr) => match try_eval(cfg_evaluator, subexpr) { Ok(value) => Ok(!value), Err(errors) => Err(errors), }, } } impl From for CfgResult { fn from(value: bool) -> Self { if value { CfgResult::True } else { CfgResult::False } } } ================================================ FILE: gen/src/check.rs ================================================ use crate::gen::Opt; use crate::syntax::report::Errors; use crate::syntax::{error, Api}; use quote::{quote, quote_spanned}; use std::path::{Component, Path}; pub(super) use crate::syntax::check::{typecheck, Generator}; pub(super) fn precheck(cx: &mut Errors, apis: &[Api], opt: &Opt) { if !opt.allow_dot_includes { check_dot_includes(cx, apis); } } fn check_dot_includes(cx: &mut Errors, apis: &[Api]) { for api in apis { if let Api::Include(include) = api { let first_component = Path::new(&include.path).components().next(); if let Some(Component::CurDir | Component::ParentDir) = first_component { let begin = quote_spanned!(include.begin_span=> .); let end = quote_spanned!(include.end_span=> .); let span = quote!(#begin #end); cx.error(span, error::DOT_INCLUDE.msg); } } } } ================================================ FILE: gen/src/error.rs ================================================ use crate::gen::fs; use crate::syntax; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use codespan_reporting::term::{self, Config, WriteStyle}; use std::borrow::Cow; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io::{self, Write}; use std::ops::Range; use std::path::{Path, PathBuf}; use std::process; use std::str::Utf8Error; pub(crate) type Result = std::result::Result; #[derive(Debug)] pub(crate) enum Error { NoBridgeMod, Fs(fs::Error), Utf8(PathBuf, Utf8Error), Syn(syn::Error), } impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), Error::Fs(err) => err.fmt(f), Error::Utf8(path, _) => write!(f, "Failed to read file `{}`", path.display()), Error::Syn(err) => err.fmt(f), } } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { Error::Fs(err) => err.source(), Error::Utf8(_, err) => Some(err), Error::Syn(err) => err.source(), Error::NoBridgeMod => None, } } } impl From for Error { fn from(err: fs::Error) -> Self { Error::Fs(err) } } impl From for Error { fn from(err: syn::Error) -> Self { Error::Syn(err) } } pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! { match error { Error::Syn(syn_error) => { let syn_error = sort_syn_errors(syn_error); let writer = StandardStream::stderr(ColorChoice::Auto); let ref mut stderr = writer.lock(); for error in syn_error { let _ = writeln!(stderr); display_syn_error(stderr, path, source, error); } } Error::NoBridgeMod => { let _ = writeln!( io::stderr(), "cxxbridge: no #[cxx::bridge] module found in {}", path.display(), ); } _ => { let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error)); } } process::exit(1); } pub(crate) fn report(error: impl StdError) -> impl Display { struct Report(E); impl Display for Report { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "{}", self.0)?; let mut error: &dyn StdError = &self.0; while let Some(cause) = error.source() { write!(formatter, "\n\nCaused by:\n {}", cause)?; error = cause; } Ok(()) } } Report(error) } fn sort_syn_errors(error: syn::Error) -> Vec { let mut errors: Vec<_> = error.into_iter().collect(); errors.sort_by_key(|e| { let start = e.span().start(); (start.line, start.column) }); errors } fn display_syn_error(stderr: &mut dyn WriteStyle, path: &Path, source: &str, error: syn::Error) { let span = error.span(); let start = span.start(); let end = span.end(); let mut start_offset = 0; for _ in 1..start.line { start_offset += source[start_offset..].find('\n').unwrap() + 1; } let start_column = source[start_offset..] .chars() .take(start.column) .map(char::len_utf8) .sum::(); start_offset += start_column; let mut end_offset = start_offset; if start.line == end.line { end_offset -= start_column; } else { for _ in 0..end.line - start.line { end_offset += source[end_offset..].find('\n').unwrap() + 1; } } end_offset += source[end_offset..] .chars() .take(end.column) .map(char::len_utf8) .sum::(); let mut path = path.to_string_lossy(); if path == "-" { path = Cow::Borrowed(if cfg!(unix) { "/dev/stdin" } else { "stdin" }); } let mut files = SimpleFiles::new(); let file = files.add(path, source); let diagnostic = diagnose(file, start_offset..end_offset, error); let config = Config::default(); let _ = term::emit_to_write_style(stderr, &config, &files, &diagnostic); } fn diagnose(file: usize, range: Range, error: syn::Error) -> Diagnostic { let message = error.to_string(); let info = syntax::error::ERRORS .iter() .find(|e| message.contains(e.msg)); let mut diagnostic = Diagnostic::error().with_message(&message); let mut label = Label::primary(file, range); if let Some(info) = info { label.message = info.label.map_or(message, str::to_owned); diagnostic.labels.push(label); diagnostic.notes.extend(info.note.map(str::to_owned)); } else { label.message = message; diagnostic.labels.push(label); } diagnostic.code = Some("cxxbridge".to_owned()); diagnostic } ================================================ FILE: gen/src/file.rs ================================================ use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use syn::parse::discouraged::Speculative; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{braced, Attribute, Ident, Item, Meta, Token, Visibility}; pub(crate) struct File { pub modules: Vec, } impl Parse for File { fn parse(input: ParseStream) -> Result { let mut modules = Vec::new(); parse(input, &mut modules)?; Ok(File { modules }) } } fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { input.call(Attribute::parse_inner)?; while !input.is_empty() { let mut cxx_bridge = false; let mut namespace = Namespace::ROOT; let mut attrs = input.call(Attribute::parse_outer)?; for attr in &attrs { let path = &attr.path().segments; if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { cxx_bridge = true; namespace = parse_args(attr)?; break; } } let ahead = input.fork(); ahead.parse::()?; ahead.parse::>()?; if !ahead.peek(Token![mod]) { let item: Item = input.parse()?; if cxx_bridge { return Err(Error::new_spanned(item, "expected a module")); } continue; } if cxx_bridge { let mut module: Module = input.parse()?; module.namespace = namespace; attrs.extend(module.attrs); module.attrs = attrs; modules.push(module); } else { input.advance_to(&ahead); input.parse::()?; input.parse::()?; let semi: Option = input.parse()?; if semi.is_none() { let content; braced!(content in input); parse(&content, modules)?; } } } Ok(()) } fn parse_args(attr: &Attribute) -> Result { if let Meta::Path(_) = attr.meta { Ok(Namespace::ROOT) } else { attr.parse_args_with(Namespace::parse_bridge_attr_namespace) } } ================================================ FILE: gen/src/fs.rs ================================================ #![allow(dead_code)] use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io::{self, Read}; use std::path::{Path, PathBuf}; pub(crate) type Result = std::result::Result; #[derive(Debug)] pub(crate) struct Error { source: Option, message: String, } impl Error { pub(crate) fn kind(&self) -> io::ErrorKind { match &self.source { Some(io_error) => io_error.kind(), None => io::ErrorKind::Other, } } } impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str(&self.message) } } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { let source = self.source.as_ref()?; Some(source) } } macro_rules! err { ($io_error:expr, $fmt:expr $(, $path:expr)* $(,)?) => { Err(Error { source: Option::from($io_error), message: format!($fmt $(, $path.display())*), }) } } pub(crate) fn copy(from: impl AsRef, to: impl AsRef) -> Result { let from = from.as_ref(); let to = to.as_ref(); match std::fs::copy(from, to) { Ok(n) => Ok(n), Err(e) => err!(e, "Failed to copy `{}` -> `{}`", from, to), } } pub(crate) fn create_dir_all(path: impl AsRef) -> Result<()> { let path = path.as_ref(); match std::fs::create_dir_all(path) { Ok(()) => Ok(()), Err(e) => err!(e, "Failed to create directory `{}`", path), } } pub(crate) fn current_dir() -> Result { match std::env::current_dir() { Ok(dir) => Ok(dir), Err(e) => err!(e, "Failed to determine current directory"), } } pub(crate) fn exists(path: impl AsRef) -> bool { let path = path.as_ref(); // If path is a symlink, this returns true, regardless of whether the // symlink points to a path that exists. std::fs::symlink_metadata(path).is_ok() } pub(crate) fn read(path: impl AsRef) -> Result> { let path = path.as_ref(); match std::fs::read(path) { Ok(string) => Ok(string), Err(e) => err!(e, "Failed to read file `{}`", path), } } pub(crate) fn read_stdin() -> Result> { let mut bytes = Vec::new(); match io::stdin().read_to_end(&mut bytes) { Ok(_len) => Ok(bytes), Err(e) => err!(e, "Failed to read input from stdin"), } } pub(crate) fn remove_file(path: impl AsRef) -> Result<()> { let path = path.as_ref(); match std::fs::remove_file(path) { Ok(()) => Ok(()), Err(e) => err!(e, "Failed to remove file `{}`", path), } } pub(crate) fn remove_dir(path: impl AsRef) -> Result<()> { let path = path.as_ref(); match std::fs::remove_dir(path) { Ok(()) => Ok(()), Err(e) => err!(e, "Failed to remove directory `{}`", path), } } fn symlink<'a>( original: &'a Path, link: &'a Path, fun: fn(&'a Path, &'a Path) -> io::Result<()>, ) -> Result<()> { match fun(original, link) { Ok(()) => Ok(()), Err(e) => err!( e, "Failed to create symlink `{}` pointing to `{}`", link, original, ), } } pub(crate) fn symlink_fail(original: impl AsRef, link: impl AsRef) -> Result<()> { err!( None, "Failed to create symlink `{}` pointing to `{}`", link.as_ref(), original.as_ref(), ) } #[cfg(unix)] #[allow(unused_imports)] pub(crate) use self::symlink_file as symlink_dir; #[cfg(not(any(unix, windows)))] #[allow(unused_imports)] pub(crate) use self::symlink_fail as symlink_dir; #[cfg(unix)] pub(crate) fn symlink_file(original: impl AsRef, link: impl AsRef) -> Result<()> { symlink(original.as_ref(), link.as_ref(), std::os::unix::fs::symlink) } #[cfg(windows)] pub(crate) fn symlink_file(original: impl AsRef, link: impl AsRef) -> Result<()> { symlink( original.as_ref(), link.as_ref(), std::os::windows::fs::symlink_file, ) } #[cfg(windows)] pub(crate) fn symlink_dir(original: impl AsRef, link: impl AsRef) -> Result<()> { symlink( original.as_ref(), link.as_ref(), std::os::windows::fs::symlink_dir, ) } pub(crate) fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> Result<()> { let path = path.as_ref(); match std::fs::write(path, contents) { Ok(()) => Ok(()), Err(e) => err!(e, "Failed to write file `{}`", path), } } ================================================ FILE: gen/src/guard.rs ================================================ use crate::gen::out::OutFile; use crate::syntax::symbol::Symbol; use crate::syntax::Pair; use std::fmt::{self, Display}; pub(crate) struct Guard { kind: &'static str, symbol: Symbol, } impl Guard { pub fn new(out: &mut OutFile, kind: &'static str, name: &Pair) -> Self { let symbol = name.to_symbol(); out.pragma.dollar_in_identifier |= symbol.contains('$'); Guard { kind, symbol } } } impl Display for Guard { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "{}_{}", self.kind, self.symbol) } } ================================================ FILE: gen/src/ifndef.rs ================================================ use crate::gen::include::HEADER; use crate::gen::out::Content; pub(super) fn write(out: &mut Content, needed: bool, guard: &str) { let ifndef = format!("#ifndef {}", guard); let define = format!("#define {}", guard); let endif = format!("#endif // {}", guard); let mut offset = 0; loop { let begin = find_line(offset, &ifndef); let end = find_line(offset, &endif); if let (Some(begin), Some(end)) = (begin, end) { if !needed { return; } out.next_section(); if offset == 0 { writeln!(out, "{}", ifndef); writeln!(out, "{}", define); } for line in HEADER[begin + ifndef.len()..end].trim().lines() { if line != define && !line.trim_start().starts_with("//") { writeln!(out, "{}", line); } } offset = end + endif.len(); } else if offset == 0 { panic!("not found in cxx.h header: {}", guard) } else { writeln!(out, "{}", endif); return; } } } fn find_line(mut offset: usize, line: &str) -> Option { loop { offset += HEADER[offset..].find(line)?; let rest = &HEADER[offset + line.len()..]; if rest.starts_with('\n') || rest.starts_with('\r') { return Some(offset); } offset += line.len(); } } ================================================ FILE: gen/src/include.rs ================================================ use crate::gen::out::{Content, OutFile}; use crate::syntax::{self, IncludeKind}; use std::ops::{Deref, DerefMut}; /// The complete contents of the "rust/cxx.h" header. pub static HEADER: &str = include_str!("include/cxx.h"); /// A header to #include. /// /// The cxxbridge tool does not parse or even require the given paths to exist; /// they simply go into the generated C++ code as #include lines. #[derive(Clone, PartialEq, Debug)] pub struct Include { /// The header's path, not including the enclosing quotation marks or angle /// brackets. pub path: String, /// Whether to emit `#include "path"` or `#include `. pub kind: IncludeKind, } #[derive(Default, PartialEq)] pub(crate) struct Includes<'a> { pub custom: Vec, pub algorithm: bool, pub array: bool, pub cassert: bool, pub cstddef: bool, pub cstdint: bool, pub cstring: bool, pub exception: bool, pub functional: bool, pub initializer_list: bool, pub iterator: bool, pub limits: bool, pub memory: bool, pub new: bool, pub ranges: bool, pub stdexcept: bool, pub string: bool, pub string_view: bool, pub type_traits: bool, pub utility: bool, pub vector: bool, pub basetsd: bool, pub sys_types: bool, pub content: Content<'a>, } impl<'a> Includes<'a> { pub(crate) fn new() -> Self { Includes::default() } pub(crate) fn insert(&mut self, include: impl Into) { self.custom.push(include.into()); } pub(crate) fn has_cxx_header(&self) -> bool { self.custom .iter() .any(|header| header.path == "rust/cxx.h" || header.path == "rust\\cxx.h") } } pub(super) fn write(out: &mut OutFile) { let header = out.header; let include = &mut out.include; let cxx_header = include.has_cxx_header(); let out = &mut include.content; if header { writeln!(out, "#pragma once"); } for include in &include.custom { match include.kind { IncludeKind::Quoted => { writeln!(out, "#include \"{}\"", include.path.escape_default()); } IncludeKind::Bracketed => { writeln!(out, "#include <{}>", include.path); } } } let Includes { custom: _, algorithm, array, cassert, cstddef, cstdint, cstring, exception, functional, initializer_list, iterator, limits, memory, new, ranges, stdexcept, string, string_view, type_traits, utility, vector, basetsd, sys_types, content: _, } = *include; if algorithm && !cxx_header { writeln!(out, "#include "); } if array && !cxx_header { writeln!(out, "#include "); } if cassert && !cxx_header { writeln!(out, "#include "); } if cstddef && !cxx_header { writeln!(out, "#include "); } if cstdint && !cxx_header { writeln!(out, "#include "); } if cstring { writeln!(out, "#include "); } if exception && !cxx_header { writeln!(out, "#include "); } if functional { writeln!(out, "#include "); } if initializer_list && !cxx_header { writeln!(out, "#include "); } if iterator && !cxx_header { writeln!(out, "#include "); } if limits { writeln!(out, "#include "); } if memory { writeln!(out, "#include "); } if new && !cxx_header { writeln!(out, "#include "); } if stdexcept && !cxx_header { writeln!(out, "#include "); } if string && !cxx_header { writeln!(out, "#include "); } if type_traits && !cxx_header { writeln!(out, "#include "); } if utility && !cxx_header { writeln!(out, "#include "); } if vector && !cxx_header { writeln!(out, "#include "); } if basetsd && !cxx_header { writeln!(out, "#if defined(_WIN32)"); writeln!(out, "#include "); } if sys_types && !cxx_header { if basetsd { writeln!(out, "#else"); } else { writeln!(out, "#if not defined(_WIN32)"); } } if sys_types && !cxx_header { writeln!(out, "#include "); } if (basetsd || sys_types) && !cxx_header { writeln!(out, "#endif"); } if string_view && !cxx_header { writeln!(out, "#if __cplusplus >= 201703L"); writeln!(out, "#include "); writeln!(out, "#endif"); } if ranges && !cxx_header { writeln!(out, "#if __cplusplus >= 202002L"); writeln!(out, "#include "); writeln!(out, "#endif"); } } impl<'i, 'a> Extend<&'i Include> for Includes<'a> { fn extend>(&mut self, iter: I) { self.custom.extend(iter.into_iter().cloned()); } } impl From<&syntax::Include> for Include { fn from(include: &syntax::Include) -> Self { Include { path: include.path.clone(), kind: include.kind, } } } impl<'a> Deref for Includes<'a> { type Target = Content<'a>; fn deref(&self) -> &Self::Target { &self.content } } impl<'a> DerefMut for Includes<'a> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.content } } ================================================ FILE: gen/src/mod.rs ================================================ // Functionality that is shared between the cxx_build::bridge entry point and // the cxxbridge CLI command. mod block; mod builtin; mod cfg; mod check; pub(super) mod error; mod file; pub(super) mod fs; mod guard; mod ifndef; pub(super) mod include; mod names; mod namespace; mod nested; pub(super) mod out; mod pragma; mod write; use self::cfg::UnsupportedCfgEvaluator; use self::error::{format_err, Result}; use self::file::File; use self::include::Include; use crate::syntax::cfg::CfgExpr; use crate::syntax::report::Errors; use crate::syntax::{self, attrs, Types}; use std::collections::BTreeSet as Set; use std::path::Path; pub(super) use self::error::Error; /// Options for C++ code generation. /// /// We expect options to be added over time, so this is a non-exhaustive struct. /// To instantiate one you need to crate a default value and mutate those fields /// that you want to modify. /// /// ``` /// # use cxx_gen::Opt; /// # /// let impl_annotations = r#"__attribute__((visibility("default")))"#.to_owned(); /// /// let mut opt = Opt::default(); /// opt.cxx_impl_annotations = Some(impl_annotations); /// ``` #[non_exhaustive] pub struct Opt { /// Any additional headers to #include. The cxxbridge tool does not parse or /// even require the given paths to exist; they simply go into the generated /// C++ code as #include lines. pub include: Vec, /// Optional annotation for implementations of C++ function wrappers that /// may be exposed to Rust. You may for example need to provide /// `__declspec(dllexport)` or `__attribute__((visibility("default")))` if /// Rust code from one shared object or executable depends on these C++ /// functions in another. pub cxx_impl_annotations: Option, /// Impl for handling conditional compilation attributes. pub cfg_evaluator: Box, pub(super) gen_header: bool, pub(super) gen_implementation: bool, pub(super) allow_dot_includes: bool, pub(super) doxygen: bool, } /// Logic to decide whether a conditional compilation attribute is enabled or /// disabled. pub trait CfgEvaluator { /// A name-only attribute such as `cfg(ident)` is passed with a `value` of /// None, while `cfg(key = "value")` is passed with the "value" in `value`. fn eval(&self, name: &str, value: Option<&str>) -> CfgResult; } /// Result of a [`CfgEvaluator`] evaluation. pub enum CfgResult { /// Cfg option is enabled. True, /// Cfg option is disabled. False, /// Cfg option is neither enabled nor disabled. Undetermined { /// Message explaining why the cfg option is undetermined. msg: String, }, } /// Results of code generation. #[derive(Default)] pub struct GeneratedCode { /// The bytes of a C++ header file. pub header: Vec, /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.) pub implementation: Vec, } impl Default for Opt { fn default() -> Self { Opt { include: Vec::new(), cxx_impl_annotations: None, gen_header: true, gen_implementation: true, allow_dot_includes: true, cfg_evaluator: Box::new(UnsupportedCfgEvaluator), doxygen: false, } } } pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode { let source = match read_to_string(path) { Ok(source) => source, Err(err) => format_err(path, "", err), }; match generate_from_string(&source, opt) { Ok(out) => out, Err(err) => format_err(path, &source, err), } } fn read_to_string(path: &Path) -> Result { let bytes = if path == Path::new("-") { fs::read_stdin() } else { fs::read(path) }?; match String::from_utf8(bytes) { Ok(string) => Ok(string), Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())), } } fn generate_from_string(source: &str, opt: &Opt) -> Result { let mut source = source; if source.starts_with("#!") && !source.starts_with("#![") { let shebang_end = source.find('\n').unwrap_or(source.len()); source = &source[shebang_end..]; } let syntax: File = syn::parse_str(source)?; generate(syntax, opt) } pub(super) fn generate(syntax: File, opt: &Opt) -> Result { if syntax.modules.is_empty() { return Err(Error::NoBridgeMod); } let ref mut apis = Vec::new(); let ref mut errors = Errors::new(); let ref mut cfg_errors = Set::new(); for bridge in syntax.modules { let mut cfg = CfgExpr::Unconditional; let _ = attrs::parse( errors, bridge.attrs, attrs::Parser { cfg: Some(&mut cfg), ignore_unrecognized: true, ..Default::default() }, ); if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) { let ref namespace = bridge.namespace; let trusted = bridge.unsafety.is_some(); apis.extend(syntax::parse_items( errors, bridge.content, trusted, namespace, )); } } cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis); errors.propagate()?; let ref types = Types::collect(errors, apis); check::precheck(errors, apis, opt); errors.propagate()?; let generator = check::Generator::Build; check::typecheck(errors, apis, types, generator); errors.propagate()?; // Some callers may wish to generate both header and implementation from the // same token stream to avoid parsing twice. Others only need to generate // one or the other. let (mut header, mut implementation) = Default::default(); if opt.gen_header { header = write::gen(apis, types, opt, true); } if opt.gen_implementation { implementation = write::gen(apis, types, opt, false); } Ok(GeneratedCode { header, implementation, }) } ================================================ FILE: gen/src/names.rs ================================================ use crate::syntax::Pair; impl Pair { pub(crate) fn to_fully_qualified(&self) -> String { let mut fully_qualified = String::new(); for segment in &self.namespace { fully_qualified += "::"; fully_qualified += &segment.to_string(); } fully_qualified += "::"; fully_qualified += &self.cxx.to_string(); fully_qualified } } ================================================ FILE: gen/src/namespace.rs ================================================ use crate::syntax::namespace::Namespace; use crate::syntax::Api; impl Api { pub(crate) fn namespace(&self) -> &Namespace { match self { Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.name.namespace, Api::CxxType(ety) | Api::RustType(ety) => &ety.name.namespace, Api::Enum(enm) => &enm.name.namespace, Api::Struct(strct) => &strct.name.namespace, Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(), } } } ================================================ FILE: gen/src/nested.rs ================================================ use crate::syntax::map::UnorderedMap as Map; use crate::syntax::Api; use proc_macro2::Ident; pub(crate) struct NamespaceEntries<'a> { direct: Vec<&'a Api>, nested: Vec<(&'a Ident, NamespaceEntries<'a>)>, } impl<'a> NamespaceEntries<'a> { pub(crate) fn new(apis: Vec<&'a Api>) -> Self { sort_by_inner_namespace(apis, 0) } pub(crate) fn direct_content(&self) -> &[&'a Api] { &self.direct } pub(crate) fn nested_content( &self, ) -> impl Iterator)> { self.nested.iter().map(|(k, entries)| (*k, entries)) } } fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { let mut direct = Vec::new(); let mut nested_namespaces = Vec::new(); let mut index_of_namespace = Map::new(); for api in &apis { if let Some(first_ns_elem) = api.namespace().iter().nth(depth) { match index_of_namespace.get(first_ns_elem) { None => { index_of_namespace.insert(first_ns_elem, nested_namespaces.len()); nested_namespaces.push((first_ns_elem, vec![*api])); } Some(&index) => nested_namespaces[index].1.push(*api), } continue; } direct.push(*api); } let nested = nested_namespaces .into_iter() .map(|(k, apis)| (k, sort_by_inner_namespace(apis, depth + 1))) .collect(); NamespaceEntries { direct, nested } } #[cfg(test)] mod tests { use super::NamespaceEntries; use crate::syntax::attrs::OtherAttrs; use crate::syntax::cfg::CfgExpr; use crate::syntax::namespace::Namespace; use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair}; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::Token; #[test] fn test_ns_entries_sort() { let apis = &[ make_api(None, "C"), make_api(None, "A"), make_api(Some("G"), "E"), make_api(Some("D"), "F"), make_api(Some("G"), "H"), make_api(Some("D::K"), "L"), make_api(Some("D::K"), "M"), make_api(None, "B"), make_api(Some("D"), "I"), make_api(Some("D"), "J"), ]; let root = NamespaceEntries::new(Vec::from_iter(apis)); // :: let root_direct = root.direct_content(); assert_eq!(root_direct.len(), 3); assert_ident(root_direct[0], "C"); assert_ident(root_direct[1], "A"); assert_ident(root_direct[2], "B"); let mut root_nested = root.nested_content(); let (id, g) = root_nested.next().unwrap(); assert_eq!(id, "G"); let (id, d) = root_nested.next().unwrap(); assert_eq!(id, "D"); assert!(root_nested.next().is_none()); // ::G let g_direct = g.direct_content(); assert_eq!(g_direct.len(), 2); assert_ident(g_direct[0], "E"); assert_ident(g_direct[1], "H"); let mut g_nested = g.nested_content(); assert!(g_nested.next().is_none()); // ::D let d_direct = d.direct_content(); assert_eq!(d_direct.len(), 3); assert_ident(d_direct[0], "F"); assert_ident(d_direct[1], "I"); assert_ident(d_direct[2], "J"); let mut d_nested = d.nested_content(); let (id, k) = d_nested.next().unwrap(); assert_eq!(id, "K"); // ::D::K let k_direct = k.direct_content(); assert_eq!(k_direct.len(), 2); assert_ident(k_direct[0], "L"); assert_ident(k_direct[1], "M"); } fn assert_ident(api: &Api, expected: &str) { if let Api::CxxType(cxx_type) = api { assert_eq!(cxx_type.name.cxx.to_string(), expected); } else { unreachable!() } } fn make_api(ns: Option<&str>, ident: &str) -> Api { let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap()); Api::CxxType(ExternType { cfg: CfgExpr::Unconditional, lang: Lang::Rust, doc: Doc::new(), derives: Vec::new(), attrs: OtherAttrs::new(), visibility: Token![pub](Span::call_site()), type_token: Token![type](Span::call_site()), name: Pair { namespace: ns, cxx: ForeignName::parse(ident, Span::call_site()).unwrap(), rust: Ident::new(ident, Span::call_site()), }, generics: Lifetimes { lt_token: None, lifetimes: Punctuated::new(), gt_token: None, }, colon_token: None, bounds: Vec::new(), semi_token: Token![;](Span::call_site()), trusted: false, }) } } ================================================ FILE: gen/src/out.rs ================================================ use crate::gen::block::Block; use crate::gen::builtin::Builtins; use crate::gen::include::Includes; use crate::gen::pragma::Pragma; use crate::gen::Opt; use crate::syntax::namespace::Namespace; use crate::syntax::Types; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile<'a> { pub header: bool, pub opt: &'a Opt, pub types: &'a Types<'a>, pub include: Includes<'a>, pub pragma: Pragma<'a>, pub builtin: Builtins<'a>, content: RefCell>, } #[derive(Default)] pub(crate) struct Content<'a> { bytes: String, namespace: &'a Namespace, blocks: Vec>, suppress_next_section: bool, section_pending: bool, blocks_pending: usize, } #[derive(Copy, Clone, PartialEq, Debug)] enum BlockBoundary<'a> { Begin(Block<'a>), End(Block<'a>), } impl<'a> OutFile<'a> { pub(crate) fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self { OutFile { header, opt, types, include: Includes::new(), pragma: Pragma::new(), builtin: Builtins::new(), content: RefCell::new(Content::new()), } } // Write a blank line if the preceding section had any contents. pub(crate) fn next_section(&mut self) { self.content.get_mut().next_section(); } pub(crate) fn suppress_next_section(&mut self) { self.content.get_mut().suppress_next_section(); } pub(crate) fn begin_block(&mut self, block: Block<'a>) { self.content.get_mut().begin_block(block); } pub(crate) fn end_block(&mut self, block: Block<'a>) { self.content.get_mut().end_block(block); } pub(crate) fn set_namespace(&mut self, namespace: &'a Namespace) { self.content.get_mut().set_namespace(namespace); } pub(crate) fn content(&mut self) -> Vec { self.flush(); let include = &self.include.content.bytes; let pragma_begin = &self.pragma.begin.bytes; let builtin = &self.builtin.content.bytes; let content = &self.content.get_mut().bytes; let pragma_end = &self.pragma.end.bytes; let mut out = String::new(); out.push_str(include); if !out.is_empty() && !pragma_begin.is_empty() { out.push('\n'); } out.push_str(pragma_begin); if !out.is_empty() && !builtin.is_empty() { out.push('\n'); } out.push_str(builtin); if !out.is_empty() && !content.is_empty() { out.push('\n'); } out.push_str(content); if !out.is_empty() && !pragma_end.is_empty() { out.push('\n'); } out.push_str(pragma_end); if out.is_empty() { out.push_str("// empty\n"); } out.into_bytes() } fn flush(&mut self) { self.include.content.flush(); self.pragma.begin.flush(); self.builtin.content.flush(); self.content.get_mut().flush(); self.pragma.end.flush(); } } impl<'a> Write for Content<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { self.write(s); Ok(()) } } impl<'a> PartialEq for Content<'a> { fn eq(&self, _other: &Self) -> bool { true } } impl<'a> Content<'a> { pub(crate) fn new() -> Self { Content::default() } pub(crate) fn next_section(&mut self) { self.section_pending = !self.suppress_next_section; } pub(crate) fn suppress_next_section(&mut self) { self.suppress_next_section = true; } pub(crate) fn begin_block(&mut self, block: Block<'a>) { self.push_block_boundary(BlockBoundary::Begin(block)); } pub(crate) fn end_block(&mut self, block: Block<'a>) { self.push_block_boundary(BlockBoundary::End(block)); } pub(crate) fn set_namespace(&mut self, namespace: &'a Namespace) { for name in self.namespace.iter().rev() { self.end_block(Block::UserDefinedNamespace(name)); } for name in namespace { self.begin_block(Block::UserDefinedNamespace(name)); } self.namespace = namespace; } pub(crate) fn write_fmt(&mut self, args: Arguments) { Write::write_fmt(self, args).unwrap(); } fn write(&mut self, b: &str) { if !b.is_empty() { if self.blocks_pending > 0 { self.flush_blocks(); } if self.section_pending && !self.bytes.is_empty() { self.bytes.push('\n'); } self.bytes.push_str(b); self.suppress_next_section = false; self.section_pending = false; self.blocks_pending = 0; } } fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) { if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() { self.blocks.pop(); self.blocks_pending -= 1; } else { self.blocks.push(boundary); self.blocks_pending += 1; } } fn flush(&mut self) { self.set_namespace(Default::default()); if self.blocks_pending > 0 { self.flush_blocks(); } } fn flush_blocks(&mut self) { self.section_pending = !self.bytes.is_empty(); let mut read = self.blocks.len() - self.blocks_pending; let mut write = read; while read < self.blocks.len() { match self.blocks[read] { BlockBoundary::Begin(begin_block) => { if self.section_pending { self.bytes.push('\n'); self.section_pending = false; } Block::write_begin(begin_block, &mut self.bytes); self.blocks[write] = BlockBoundary::Begin(begin_block); write += 1; } BlockBoundary::End(end_block) => { write = write.checked_sub(1).unwrap(); let begin_block = self.blocks[write]; assert_eq!(begin_block, BlockBoundary::Begin(end_block)); Block::write_end(end_block, &mut self.bytes); self.section_pending = true; } } read += 1; } self.blocks.truncate(write); } } impl<'a> BlockBoundary<'a> { fn rev(self) -> BlockBoundary<'a> { match self { BlockBoundary::Begin(block) => BlockBoundary::End(block), BlockBoundary::End(block) => BlockBoundary::Begin(block), } } } pub(crate) trait InfallibleWrite { fn write_fmt(&mut self, args: Arguments); } impl InfallibleWrite for String { fn write_fmt(&mut self, args: Arguments) { Write::write_fmt(self, args).unwrap(); } } impl<'a> InfallibleWrite for Content<'a> { fn write_fmt(&mut self, args: Arguments) { Write::write_fmt(self, args).unwrap(); } } impl<'a> InfallibleWrite for OutFile<'a> { fn write_fmt(&mut self, args: Arguments) { InfallibleWrite::write_fmt(self.content.get_mut(), args); } } ================================================ FILE: gen/src/pragma.rs ================================================ use crate::gen::out::{Content, OutFile}; use std::collections::BTreeSet; #[derive(Default)] pub(crate) struct Pragma<'a> { pub gnu_diagnostic_ignore: BTreeSet<&'a str>, pub clang_diagnostic_ignore: BTreeSet<&'a str>, pub dollar_in_identifier: bool, pub mismatched_new_delete: bool, pub missing_declarations: bool, pub return_type_c_linkage: bool, pub begin: Content<'a>, pub end: Content<'a>, } impl<'a> Pragma<'a> { pub fn new() -> Self { Pragma::default() } } pub(super) fn write(out: &mut OutFile) { let Pragma { ref mut gnu_diagnostic_ignore, ref mut clang_diagnostic_ignore, dollar_in_identifier, mismatched_new_delete, missing_declarations, return_type_c_linkage, ref mut begin, ref mut end, } = out.pragma; if dollar_in_identifier { clang_diagnostic_ignore.insert("-Wdollar-in-identifier-extension"); } if mismatched_new_delete { gnu_diagnostic_ignore.insert("-Wmismatched-new-delete"); } if missing_declarations { gnu_diagnostic_ignore.insert("-Wmissing-declarations"); } if return_type_c_linkage { clang_diagnostic_ignore.insert("-Wreturn-type-c-linkage"); } let gnu_diagnostic_ignore = &*gnu_diagnostic_ignore; let clang_diagnostic_ignore = &*clang_diagnostic_ignore; if !gnu_diagnostic_ignore.is_empty() { writeln!(begin, "#ifdef __GNUC__"); if out.header { writeln!(begin, "#pragma GCC diagnostic push"); } for diag in gnu_diagnostic_ignore { writeln!(begin, "#pragma GCC diagnostic ignored \"{diag}\""); } } if !clang_diagnostic_ignore.is_empty() { writeln!(begin, "#ifdef __clang__"); if out.header && gnu_diagnostic_ignore.is_empty() { writeln!(begin, "#pragma clang diagnostic push"); } for diag in clang_diagnostic_ignore { writeln!(begin, "#pragma clang diagnostic ignored \"{diag}\""); } writeln!(begin, "#endif // __clang__"); } if !gnu_diagnostic_ignore.is_empty() { writeln!(begin, "#endif // __GNUC__"); } if out.header { if !gnu_diagnostic_ignore.is_empty() { writeln!(end, "#ifdef __GNUC__"); writeln!(end, "#pragma GCC diagnostic pop"); writeln!(end, "#endif // __GNUC__"); } else if !clang_diagnostic_ignore.is_empty() { writeln!(end, "#ifdef __clang__"); writeln!(end, "#pragma clang diagnostic pop"); writeln!(end, "#endif // __clang__"); } } } ================================================ FILE: gen/src/write.rs ================================================ use crate::gen::block::Block; use crate::gen::guard::Guard; use crate::gen::nested::NamespaceEntries; use crate::gen::out::{InfallibleWrite, OutFile}; use crate::gen::{builtin, include, pragma, Opt}; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::discriminant::{Discriminant, Limits}; use crate::syntax::instantiate::{ImplKey, NamedImplKey}; use crate::syntax::map::UnorderedMap as Map; use crate::syntax::namespace::Namespace; use crate::syntax::primitive::{self, PrimitiveKind}; use crate::syntax::set::UnorderedSet; use crate::syntax::symbol::Symbol; use crate::syntax::trivial::{self, TrivialReason}; use crate::syntax::{ derive, mangle, Api, Doc, Enum, ExternFn, ExternType, FnKind, Lang, Pair, Signature, Struct, Trait, Type, TypeAlias, Types, Var, }; pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec { let mut out_file = OutFile::new(header, opt, types); let out = &mut out_file; pick_includes_and_builtins(out, apis); out.include.extend(&opt.include); write_macros(out, apis); write_forward_declarations(out, apis); write_data_structures(out, apis); write_functions(out, apis); write_generic_instantiations(out); builtin::write(out); pragma::write(out); include::write(out); out_file.content() } fn write_macros(out: &mut OutFile, apis: &[Api]) { let mut needs_default_value = false; for api in apis { if let Api::Struct(strct) = api { if !out.types.cxx.contains(&strct.name.rust) { for field in &strct.fields { needs_default_value |= primitive::kind(&field.ty).is_some(); } } } } if needs_default_value { out.next_section(); writeln!(out, "#if __cplusplus >= 201402L"); writeln!(out, "#define CXX_DEFAULT_VALUE(value) = value"); writeln!(out, "#else"); writeln!(out, "#define CXX_DEFAULT_VALUE(value)"); writeln!(out, "#endif"); } } fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) { let needs_forward_declaration = |api: &&Api| match api { Api::Struct(_) | Api::CxxType(_) | Api::RustType(_) => true, Api::Enum(enm) => !out.types.cxx.contains(&enm.name.rust), _ => false, }; let apis_by_namespace = NamespaceEntries::new(apis.iter().filter(needs_forward_declaration).collect()); out.next_section(); write(out, &apis_by_namespace, 0); fn write(out: &mut OutFile, ns_entries: &NamespaceEntries, indent: usize) { let apis = ns_entries.direct_content(); for api in apis { write!(out, "{:1$}", "", indent); match api { Api::Struct(strct) => write_struct_decl(out, &strct.name), Api::Enum(enm) => write_enum_decl(out, enm), Api::CxxType(ety) => write_struct_using(out, &ety.name), Api::RustType(ety) => write_struct_decl(out, &ety.name), _ => unreachable!(), } } for (namespace, nested_ns_entries) in ns_entries.nested_content() { writeln!(out, "{:2$}namespace {} {{", "", namespace, indent); write(out, nested_ns_entries, indent + 2); writeln!(out, "{:1$}}}", "", indent); } } } fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { let mut methods_for_type = Map::new(); for api in apis { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api { if let Some(self_type) = efn.self_type() { methods_for_type .entry(self_type) .or_insert_with(Vec::new) .push(efn); } } } let mut structs_written = UnorderedSet::new(); let mut toposorted_structs = out.types.toposorted_structs.iter(); for api in apis { match api { Api::Struct(strct) if !structs_written.contains(&strct.name.rust) => { for next in &mut toposorted_structs { if !out.types.cxx.contains(&next.name.rust) { out.next_section(); let methods = methods_for_type .get(&next.name.rust) .map(Vec::as_slice) .unwrap_or_default(); write_struct(out, next, methods); } structs_written.insert(&next.name.rust); if next.name.rust == strct.name.rust { break; } } } Api::Enum(enm) => { out.next_section(); if out.types.cxx.contains(&enm.name.rust) { check_enum(out, enm); } else { write_enum(out, enm); } } Api::RustType(ety) => { out.next_section(); let methods = methods_for_type .get(&ety.name.rust) .map(Vec::as_slice) .unwrap_or_default(); write_opaque_type(out, ety, methods); } _ => {} } } if out.header { return; } out.set_namespace(Default::default()); out.next_section(); for api in apis { if let Api::TypeAlias(ety) = api { if let Some(reasons) = out.types.required_trivial.get(&ety.name.rust) { check_trivial_extern_type(out, ety, reasons); } } } } fn write_functions<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { if !out.header { for api in apis { match api { Api::Struct(strct) => write_struct_operator_decls(out, strct), Api::RustType(ety) => write_opaque_type_layout_decls(out, ety), Api::CxxFunction(efn) => write_cxx_function_shim(out, efn), Api::RustFunction(efn) => write_rust_function_decl(out, efn), _ => {} } } write_std_specializations(out, apis); } for api in apis { match api { Api::Struct(strct) => write_struct_operators(out, strct), Api::RustType(ety) => write_opaque_type_layout(out, ety), Api::RustFunction(efn) => { out.next_section(); write_rust_function_shim(out, efn); } _ => {} } } } 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(); out.include.cstddef = true; out.include.functional = true; out.pragma.dollar_in_identifier = true; let qualified = strct.name.to_fully_qualified(); writeln!(out, "template <> struct hash<{}> {{", qualified); writeln!( out, " ::std::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 { out.include.insert(include); } } for ty in out.types { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { Some(U8 | U16 | U32 | U64 | I8 | I16 | I32 | I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, Some(Isize) => out.builtin.rust_isize = true, Some(CxxString) => out.include.string = true, Some(RustString) => out.builtin.rust_string = true, Some(Bool | Char | F32 | F64) | None => {} }, Type::RustBox(_) => out.builtin.rust_box = true, Type::RustVec(_) => out.builtin.rust_vec = true, Type::UniquePtr(_) => out.include.memory = true, Type::SharedPtr(_) | Type::WeakPtr(_) => out.include.memory = true, Type::Str(_) => out.builtin.rust_str = true, Type::CxxVector(_) => out.include.vector = true, Type::Fn(_) => out.builtin.rust_fn = true, Type::SliceRef(_) => out.builtin.rust_slice = true, Type::Array(_) => out.include.array = true, Type::Ref(_) | Type::Void(_) | Type::Ptr(_) => {} } } } fn write_doc(out: &mut OutFile, indent: &str, doc: &Doc) { let mut lines = 0; for line in doc.to_string().lines() { if out.opt.doxygen { writeln!(out, "{}///{}", indent, line); } else { writeln!(out, "{}//{}", indent, line); } lines += 1; } // According to https://www.doxygen.nl/manual/docblocks.html, Doxygen only // interprets `///` as a Doxygen comment block if there are at least 2 of // them. In Rust, a single `///` is definitely still documentation so we // make sure to propagate that as a Doxygen comment. if out.opt.doxygen && lines == 1 { writeln!(out, "{}///", indent); } } fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&ExternFn]) { let operator_eq = derive::contains(&strct.derives, Trait::PartialEq); let operator_ord = derive::contains(&strct.derives, Trait::PartialOrd); out.set_namespace(&strct.name.namespace); let guard = Guard::new(out, "CXXBRIDGE1_STRUCT", &strct.name); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); write_doc(out, "", &strct.doc); write!(out, "struct"); if let Some(align) = &strct.align { out.builtin.alignmax = true; writeln!(out, " alignas(::rust::repr::alignmax<"); writeln!(out, " {},", align.base10_parse::().unwrap()); for (i, field) in strct.fields.iter().enumerate() { write!(out, " alignof("); write_type(out, &field.ty); write!(out, ")"); if i + 1 != strct.fields.len() { write!(out, ","); } writeln!(out); } write!(out, ">)"); } writeln!(out, " {} final {{", strct.name.cxx); for field in &strct.fields { write_doc(out, " ", &field.doc); write!(out, " "); write_type_space(out, &field.ty); write!(out, "{}", field.name.cxx); if let Some(primitive) = primitive::kind(&field.ty) { let default_value = match primitive { PrimitiveKind::Boolean => "false", PrimitiveKind::Number => "0", PrimitiveKind::Pointer => "nullptr", }; write!(out, " CXX_DEFAULT_VALUE({})", default_value); } writeln!(out, ";"); } out.next_section(); for method in methods { if !method.doc.is_empty() { out.next_section(); } write_doc(out, " ", &method.doc); write!(out, " "); let local_name = method.name.cxx.to_string(); let sig = &method.sig; let in_class = true; let indirect_call = false; let main = false; write_rust_function_shim_decl(out, &local_name, sig, in_class, indirect_call, main); writeln!(out, ";"); if !method.doc.is_empty() { out.next_section(); } } if operator_eq { writeln!( out, " bool operator==({} const &) const noexcept;", strct.name.cxx, ); writeln!( out, " bool operator!=({} const &) const noexcept;", strct.name.cxx, ); } if operator_ord { writeln!( out, " bool operator<({} const &) const noexcept;", strct.name.cxx, ); writeln!( out, " bool operator<=({} const &) const noexcept;", strct.name.cxx, ); writeln!( out, " bool operator>({} const &) const noexcept;", strct.name.cxx, ); writeln!( out, " bool operator>=({} const &) const noexcept;", strct.name.cxx, ); } out.include.type_traits = true; writeln!(out, " using IsRelocatable = ::std::true_type;"); writeln!(out, "}};"); writeln!(out, "#endif // {}", guard); } fn write_struct_decl(out: &mut OutFile, ident: &Pair) { writeln!(out, "struct {};", ident.cxx); } fn write_enum_decl(out: &mut OutFile, enm: &Enum) { write!(out, "enum class {} : ", enm.name.cxx); write_atom(out, enm.repr.atom); writeln!(out, ";"); } fn write_struct_using(out: &mut OutFile, ident: &Pair) { writeln!(out, "using {} = {};", ident.cxx, ident.to_fully_qualified()); } fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[&ExternFn]) { out.set_namespace(&ety.name.namespace); let guard = Guard::new(out, "CXXBRIDGE1_STRUCT", &ety.name); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); write_doc(out, "", &ety.doc); out.builtin.opaque = true; writeln!( out, "struct {} final : public ::rust::Opaque {{", ety.name.cxx, ); for (i, method) in methods.iter().enumerate() { if i > 0 && !method.doc.is_empty() { out.next_section(); } write_doc(out, " ", &method.doc); write!(out, " "); let local_name = method.name.cxx.to_string(); let sig = &method.sig; let in_class = true; let indirect_call = false; let main = false; write_rust_function_shim_decl(out, &local_name, sig, in_class, indirect_call, main); writeln!(out, ";"); if !method.doc.is_empty() { out.next_section(); } } writeln!(out, " ~{}() = delete;", ety.name.cxx); writeln!(out); out.builtin.layout = true; out.include.cstddef = true; writeln!(out, "private:"); writeln!(out, " friend ::rust::layout;"); writeln!(out, " struct layout {{"); writeln!(out, " static ::std::size_t size() noexcept;"); writeln!(out, " static ::std::size_t align() noexcept;"); writeln!(out, " }};"); writeln!(out, "}};"); writeln!(out, "#endif // {}", guard); } fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { out.set_namespace(&enm.name.namespace); let guard = Guard::new(out, "CXXBRIDGE1_ENUM", &enm.name); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); write_doc(out, "", &enm.doc); write!(out, "enum class {} : ", enm.name.cxx); write_atom(out, enm.repr.atom); writeln!(out, " {{"); for variant in &enm.variants { write_doc(out, " ", &variant.doc); write!(out, " {} = ", variant.name.cxx); write_discriminant(out, enm.repr.atom, variant.discriminant); writeln!(out, ","); } writeln!(out, "}};"); if out.header { write_enum_operators(out, enm); } writeln!(out, "#endif // {}", guard); } fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) { out.set_namespace(&enm.name.namespace); out.include.type_traits = true; writeln!( out, "static_assert(::std::is_enum<{}>::value, \"expected enum\");", enm.name.cxx, ); write!(out, "static_assert(sizeof({}) == sizeof(", enm.name.cxx); write_atom(out, enm.repr.atom); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { write!(out, "static_assert(static_cast<"); write_atom(out, enm.repr.atom); write!(out, ">({}::{}) == ", enm.name.cxx, variant.name.cxx); write_discriminant(out, enm.repr.atom, variant.discriminant); writeln!(out, ", \"disagrees with the value in #[cxx::bridge]\");"); } if out.header && (derive::contains(&enm.derives, Trait::BitAnd) || derive::contains(&enm.derives, Trait::BitOr) || derive::contains(&enm.derives, Trait::BitXor)) { out.next_section(); let guard = Guard::new(out, "CXXBRIDGE1_ENUM", &enm.name); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); out.suppress_next_section(); write_enum_operators(out, enm); writeln!(out, "#endif // {}", guard); } } fn write_discriminant(out: &mut OutFile, repr: Atom, discriminant: Discriminant) { let limits = Limits::of(repr).unwrap(); if discriminant == limits.min && limits.min < Discriminant::zero() { out.include.limits = true; write!(out, "::std::numeric_limits<"); write_atom(out, repr); write!(out, ">::min()"); } else { write!(out, "{}", discriminant); } } fn write_binary_bitwise_op(out: &mut OutFile, op: &str, enm: &Enum) { let enum_name = &enm.name.cxx; writeln!( out, "inline {enum_name} operator{op}({enum_name} lhs, {enum_name} rhs) {{", ); write!(out, " return static_cast<{enum_name}>(static_cast<"); write_atom(out, enm.repr.atom); write!(out, ">(lhs) {op} static_cast<"); write_atom(out, enm.repr.atom); writeln!(out, ">(rhs));"); writeln!(out, "}}"); } fn write_enum_operators(out: &mut OutFile, enm: &Enum) { if derive::contains(&enm.derives, Trait::BitAnd) { out.next_section(); write_binary_bitwise_op(out, "&", enm); } if derive::contains(&enm.derives, Trait::BitOr) { out.next_section(); write_binary_bitwise_op(out, "|", enm); } if derive::contains(&enm.derives, Trait::BitXor) { out.next_section(); write_binary_bitwise_op(out, "^", enm); } } fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[TrivialReason]) { // NOTE: The following static assertion is just nice-to-have and not // necessary for soundness. That's because triviality is always declared by // the user in the form of an unsafe impl of cxx::ExternType: // // unsafe impl ExternType for MyType { // type Id = cxx::type_id!("..."); // type Kind = cxx::kind::Trivial; // } // // Since the user went on the record with their unsafe impl to unsafely // claim they KNOW that the type is trivial, it's fine for that to be on // them if that were wrong. However, in practice correctly reasoning about // the relocatability of C++ types is challenging, particularly if the type // definition were to change over time, so for now we add this check. // // There may be legitimate reasons to opt out of this assertion for support // of types that the programmer knows are soundly Rust-movable despite not // being recognized as such by the C++ type system due to a move constructor // or destructor. To opt out of the relocatability check, they need to do // one of the following things in any header used by `include!` in their // bridge. // // --- if they define the type: // struct MyType { // ... // + using IsRelocatable = std::true_type; // }; // // --- otherwise: // + template <> // + struct rust::IsRelocatable : std::true_type {}; // let id = alias.name.to_fully_qualified(); out.builtin.relocatable = true; let mut rust_type_ok = true; let mut array_ok = true; for reason in reasons { // Allow extern type that inherits from ::rust::Opaque in positions // where an opaque Rust type would be allowed. rust_type_ok &= match reason { TrivialReason::BoxTarget { .. } | TrivialReason::VecElement { .. } | TrivialReason::SliceElement { .. } => true, TrivialReason::StructField(_) | TrivialReason::FunctionArgument(_) | TrivialReason::FunctionReturn(_) => false, }; // If the type is only used as a struct field or Vec element, not as // by-value function argument or return value, then C array of trivially // relocatable type is also permissible. // // --- means something sane: // struct T { char buf[N]; }; // // --- means something totally different: // void f(char buf[N]); // array_ok &= match reason { TrivialReason::StructField(_) | TrivialReason::VecElement { .. } => true, TrivialReason::FunctionArgument(_) | TrivialReason::FunctionReturn(_) | TrivialReason::BoxTarget { .. } | TrivialReason::SliceElement { .. } => false, }; } writeln!(out, "static_assert("); write!(out, " "); if rust_type_ok { out.include.type_traits = true; out.builtin.opaque = true; write!(out, "::std::is_base_of<::rust::Opaque, {}>::value || ", id); } if array_ok { out.builtin.relocatable_or_array = true; writeln!(out, "::rust::IsRelocatableOrArray<{}>::value,", id); } else { writeln!(out, "::rust::IsRelocatable<{}>::value,", id); } writeln!( out, " \"type {} should be trivially move constructible and trivially destructible in C++ to be used as {} in Rust\");", id.trim_start_matches("::"), trivial::as_what(&alias.name, reasons), ); } fn write_struct_operator_decls<'a>(out: &mut OutFile<'a>, strct: &'a Struct) { out.set_namespace(&strct.name.namespace); out.begin_block(Block::ExternC); if derive::contains(&strct.derives, Trait::PartialEq) { out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; let link_name = mangle::operator(&strct.name, "eq"); writeln!( out, "bool {}({1} const &, {1} const &) noexcept;", link_name, strct.name.cxx, ); if !derive::contains(&strct.derives, Trait::Eq) { let link_name = mangle::operator(&strct.name, "ne"); writeln!( out, "bool {}({1} const &, {1} const &) noexcept;", link_name, strct.name.cxx, ); } } if derive::contains(&strct.derives, Trait::PartialOrd) { out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; let link_name = mangle::operator(&strct.name, "lt"); writeln!( out, "bool {}({1} const &, {1} const &) noexcept;", link_name, strct.name.cxx, ); let link_name = mangle::operator(&strct.name, "le"); writeln!( out, "bool {}({1} const &, {1} const &) noexcept;", link_name, strct.name.cxx, ); if !derive::contains(&strct.derives, Trait::Ord) { let link_name = mangle::operator(&strct.name, "gt"); writeln!( out, "bool {}({1} const &, {1} const &) noexcept;", link_name, strct.name.cxx, ); let link_name = mangle::operator(&strct.name, "ge"); writeln!( out, "bool {}({1} const &, {1} const &) noexcept;", link_name, strct.name.cxx, ); } } if derive::contains(&strct.derives, Trait::Hash) { out.include.cstddef = true; out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; let link_name = mangle::operator(&strct.name, "hash"); writeln!( out, "::std::size_t {}({} const &) noexcept;", link_name, strct.name.cxx, ); } out.end_block(Block::ExternC); } fn write_struct_operators<'a>(out: &mut OutFile<'a>, strct: &'a Struct) { if out.header { return; } out.set_namespace(&strct.name.namespace); if derive::contains(&strct.derives, Trait::PartialEq) { out.pragma.dollar_in_identifier = true; out.next_section(); writeln!( out, "bool {0}::operator==({0} const &rhs) const noexcept {{", strct.name.cxx, ); let link_name = mangle::operator(&strct.name, "eq"); writeln!(out, " return {}(*this, rhs);", link_name); writeln!(out, "}}"); out.next_section(); writeln!( out, "bool {0}::operator!=({0} const &rhs) const noexcept {{", strct.name.cxx, ); if derive::contains(&strct.derives, Trait::Eq) { writeln!(out, " return !(*this == rhs);"); } else { let link_name = mangle::operator(&strct.name, "ne"); writeln!(out, " return {}(*this, rhs);", link_name); } writeln!(out, "}}"); } if derive::contains(&strct.derives, Trait::PartialOrd) { out.pragma.dollar_in_identifier = true; out.next_section(); writeln!( out, "bool {0}::operator<({0} const &rhs) const noexcept {{", strct.name.cxx, ); let link_name = mangle::operator(&strct.name, "lt"); writeln!(out, " return {}(*this, rhs);", link_name); writeln!(out, "}}"); out.next_section(); writeln!( out, "bool {0}::operator<=({0} const &rhs) const noexcept {{", strct.name.cxx, ); let link_name = mangle::operator(&strct.name, "le"); writeln!(out, " return {}(*this, rhs);", link_name); writeln!(out, "}}"); out.next_section(); writeln!( out, "bool {0}::operator>({0} const &rhs) const noexcept {{", strct.name.cxx, ); if derive::contains(&strct.derives, Trait::Ord) { writeln!(out, " return !(*this <= rhs);"); } else { let link_name = mangle::operator(&strct.name, "gt"); writeln!(out, " return {}(*this, rhs);", link_name); } writeln!(out, "}}"); out.next_section(); writeln!( out, "bool {0}::operator>=({0} const &rhs) const noexcept {{", strct.name.cxx, ); if derive::contains(&strct.derives, Trait::Ord) { writeln!(out, " return !(*this < rhs);"); } else { let link_name = mangle::operator(&strct.name, "ge"); writeln!(out, " return {}(*this, rhs);", link_name); } writeln!(out, "}}"); } } fn write_opaque_type_layout_decls<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) { out.set_namespace(&ety.name.namespace); out.begin_block(Block::ExternC); out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; let link_name = mangle::operator(&ety.name, "sizeof"); writeln!(out, "::std::size_t {}() noexcept;", link_name); let link_name = mangle::operator(&ety.name, "alignof"); writeln!(out, "::std::size_t {}() noexcept;", link_name); out.end_block(Block::ExternC); } fn write_opaque_type_layout<'a>(out: &mut OutFile<'a>, ety: &'a ExternType) { if out.header { return; } out.set_namespace(&ety.name.namespace); out.pragma.dollar_in_identifier = true; out.next_section(); let link_name = mangle::operator(&ety.name, "sizeof"); writeln!( out, "::std::size_t {}::layout::size() noexcept {{", ety.name.cxx, ); writeln!(out, " return {}();", link_name); writeln!(out, "}}"); out.next_section(); let link_name = mangle::operator(&ety.name, "alignof"); writeln!( out, "::std::size_t {}::layout::align() noexcept {{", ety.name.cxx, ); writeln!(out, " return {}();", link_name); writeln!(out, "}}"); } fn begin_function_definition(out: &mut OutFile) { if let Some(annotation) = &out.opt.cxx_impl_annotations { write!(out, "{} ", annotation); } } fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; out.next_section(); out.set_namespace(&efn.name.namespace); out.begin_block(Block::ExternC); begin_function_definition(out); if efn.throws { out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, efn, efn.lang); } let mangled = mangle::extern_fn(efn, out.types); write!(out, "{}(", mangled); if let FnKind::Method(receiver) = &efn.kind { write!( out, "{}", out.types.resolve(&receiver.ty).name.to_fully_qualified(), ); if !receiver.mutable { write!(out, " const"); } write!(out, " &self"); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || matches!(efn.kind, FnKind::Method(_)) { write!(out, ", "); } if arg.ty == RustString { write_type_space(out, &arg.ty); write!(out, "const *{}", arg.name.cxx); } else if let Type::RustVec(_) = arg.ty { write_type_space(out, &arg.ty); write!(out, "const *{}", arg.name.cxx); } else { write_extern_arg(out, arg); } } let indirect_return = indirect_return(efn, out.types, efn.lang); if indirect_return { if !efn.args.is_empty() || matches!(efn.kind, FnKind::Method(_)) { write!(out, ", "); } write_indirect_return_type_space(out, efn.ret.as_ref().unwrap()); write!(out, "*return$"); } write!(out, ")"); match efn.lang { Lang::Cxx => write!(out, " noexcept"), Lang::CxxUnwind => {} Lang::Rust => unreachable!(), } writeln!(out, " {{"); write!(out, " "); write_return_type(out, &efn.ret); match efn.receiver() { None => write!(out, "(*{}$)(", efn.name.rust), Some(receiver) => write!( out, "({}::*{}$)(", out.types.resolve(&receiver.ty).name.to_fully_qualified(), efn.name.rust, ), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); } write_type(out, &arg.ty); } write!(out, ")"); if let Some(receiver) = efn.receiver() { if !receiver.mutable { write!(out, " const"); } } write!(out, " = "); match efn.self_type() { None => write!(out, "{}", efn.name.to_fully_qualified()), Some(self_type) => write!( out, "&{}::{}", out.types.resolve(self_type).name.to_fully_qualified(), efn.name.cxx, ), } writeln!(out, ";"); write!(out, " "); if efn.throws { out.builtin.ptr_len = true; out.builtin.trycatch = true; writeln!(out, "::rust::repr::PtrLen throw$;"); writeln!(out, " ::rust::behavior::trycatch("); writeln!(out, " [&] {{"); write!(out, " "); } if indirect_return { out.include.new = true; write!(out, "new (return$) "); write_indirect_return_type(out, efn.ret.as_ref().unwrap()); write!(out, "("); } else if efn.ret.is_some() { write!(out, "return "); } match &efn.ret { Some(Type::Ref(_)) => write!(out, "&"), Some(Type::Str(_)) if !indirect_return => { out.builtin.rust_str_repr = true; write!(out, "::rust::impl<::rust::Str>::repr("); } Some(ty @ Type::SliceRef(_)) if !indirect_return => { out.builtin.rust_slice_repr = true; write!(out, "::rust::impl<"); write_type(out, ty); write!(out, ">::repr("); } _ => {} } match efn.receiver() { None => write!(out, "{}$(", efn.name.rust), Some(_) => write!(out, "(self.*{}$)(", efn.name.rust), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); } if let Type::RustBox(_) = &arg.ty { write_type(out, &arg.ty); write!(out, "::from_raw({})", arg.name.cxx); } else if let Type::UniquePtr(_) = &arg.ty { write_type(out, &arg.ty); write!(out, "({})", arg.name.cxx); } else if arg.ty == RustString { out.builtin.unsafe_bitcopy = true; write!( out, "::rust::String(::rust::unsafe_bitcopy, *{})", arg.name.cxx, ); } else if let Type::RustVec(_) = arg.ty { out.builtin.unsafe_bitcopy = true; write_type(out, &arg.ty); write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx); } else if out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; write!(out, "::std::move(*{})", arg.name.cxx); } else { write!(out, "{}", arg.name.cxx); } } write!(out, ")"); match &efn.ret { Some(Type::RustBox(_)) => write!(out, ".into_raw()"), Some(Type::UniquePtr(_)) => write!(out, ".release()"), Some(Type::Str(_) | Type::SliceRef(_)) if !indirect_return => write!(out, ")"), _ => {} } if indirect_return { write!(out, ")"); } writeln!(out, ";"); if efn.throws { writeln!(out, " throw$.ptr = nullptr;"); writeln!(out, " }},"); writeln!(out, " ::rust::detail::Fail(throw$));"); writeln!(out, " return throw$;"); } writeln!(out, "}}"); for arg in &efn.args { if let Type::Fn(f) = &arg.ty { let var = &arg.name; write_function_pointer_trampoline(out, efn, var, f); } } out.end_block(Block::ExternC); } fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pair, f: &Signature) { out.pragma.return_type_c_linkage = true; let r_trampoline = mangle::r_trampoline(efn, var, out.types); let indirect_call = true; write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call); out.next_section(); let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string(); let doc = Doc::new(); let main = false; write_rust_function_shim_impl( out, &c_trampoline, f, &doc, &r_trampoline, indirect_call, main, ); } fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.set_namespace(&efn.name.namespace); out.begin_block(Block::ExternC); let link_name = mangle::extern_fn(efn, out.types); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, indirect_call); out.end_block(Block::ExternC); } fn write_rust_function_decl_impl( out: &mut OutFile, link_name: &Symbol, sig: &Signature, indirect_call: bool, ) { out.next_section(); out.pragma.dollar_in_identifier = true; if sig.throws { out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen "); } else { write_extern_return_type_space(out, sig, Lang::Rust); } write!(out, "{}(", link_name); let mut needs_comma = false; if let FnKind::Method(receiver) = &sig.kind { write!( out, "{}", out.types.resolve(&receiver.ty).name.to_fully_qualified(), ); if !receiver.mutable { write!(out, " const"); } write!(out, " &self"); needs_comma = true; } for arg in &sig.args { if needs_comma { write!(out, ", "); } write_extern_arg(out, arg); needs_comma = true; } if indirect_return(sig, out.types, Lang::Rust) { if needs_comma { write!(out, ", "); } match sig.ret.as_ref().unwrap() { Type::Ref(ret) => { write_type_space(out, &ret.inner); if !ret.mutable { write!(out, "const "); } write!(out, "*"); } ret => write_type_space(out, ret), } write!(out, "*return$"); needs_comma = true; } if indirect_call { if needs_comma { write!(out, ", "); } write!(out, "void *"); } writeln!(out, ") noexcept;"); } fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.set_namespace(&efn.name.namespace); let local_name = match efn.self_type() { None => efn.name.cxx.to_string(), Some(self_type) => format!( "{}::{}", out.types.resolve(self_type).name.cxx, efn.name.cxx, ), }; let doc = &efn.doc; let invoke = mangle::extern_fn(efn, out.types); let indirect_call = false; let main = efn.name.cxx == *"main" && efn.name.namespace == Namespace::ROOT && efn.sig.asyncness.is_none() && matches!(efn.kind, FnKind::Free) && efn.sig.args.is_empty() && efn.sig.ret.is_none() && !efn.sig.throws; write_rust_function_shim_impl(out, &local_name, efn, doc, &invoke, indirect_call, main); } fn write_rust_function_shim_decl( out: &mut OutFile, local_name: &str, sig: &Signature, in_class: bool, indirect_call: bool, main: bool, ) { begin_function_definition(out); if matches!(sig.kind, FnKind::Assoc(_)) && in_class { write!(out, "static "); } if main { write!(out, "int "); } else { write_return_type(out, &sig.ret); } write!(out, "{}(", local_name); for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); } write_type_space(out, &arg.ty); write!(out, "{}", arg.name.cxx); } if indirect_call { if !sig.args.is_empty() { write!(out, ", "); } write!(out, "void *extern$"); } write!(out, ")"); if let FnKind::Method(receiver) = &sig.kind { if !receiver.mutable { write!(out, " const"); } } if !sig.throws { write!(out, " noexcept"); } } fn write_rust_function_shim_impl( out: &mut OutFile, local_name: &str, sig: &Signature, doc: &Doc, invoke: &Symbol, indirect_call: bool, main: bool, ) { if match sig.kind { FnKind::Free => false, FnKind::Method(_) | FnKind::Assoc(_) => out.header, } { // We've already defined this inside the struct. return; } out.pragma.dollar_in_identifier = true; if matches!(sig.kind, FnKind::Free) { // Member functions already documented at their declaration. write_doc(out, "", doc); } let in_class = false; write_rust_function_shim_decl(out, local_name, sig, in_class, indirect_call, main); if out.header { writeln!(out, ";"); return; } writeln!(out, " {{"); for arg in &sig.args { if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) { out.include.utility = true; out.builtin.manually_drop = true; write!(out, " ::rust::ManuallyDrop<"); write_type(out, &arg.ty); writeln!(out, "> {}$(::std::move({0}));", arg.name.cxx); } } write!(out, " "); let indirect_return = indirect_return(sig, out.types, Lang::Rust); if indirect_return { out.builtin.maybe_uninit = true; write!(out, "::rust::MaybeUninit<"); match sig.ret.as_ref().unwrap() { Type::Ref(ret) => { write_type_space(out, &ret.inner); if !ret.mutable { write!(out, "const "); } write!(out, "*"); } ret => write_type(out, ret), } writeln!(out, "> return$;"); write!(out, " "); } else if let Some(ret) = &sig.ret { write!(out, "return "); match ret { Type::RustBox(_) => { write_type(out, ret); write!(out, "::from_raw("); } Type::UniquePtr(_) => { write_type(out, ret); write!(out, "("); } Type::Ref(_) => write!(out, "*"), Type::Str(_) => { out.builtin.rust_str_new_unchecked = true; write!(out, "::rust::impl<::rust::Str>::new_unchecked("); } Type::SliceRef(_) => { out.builtin.rust_slice_new = true; write!(out, "::rust::impl<"); write_type(out, ret); write!(out, ">::slice("); } _ => {} } } if sig.throws { out.builtin.ptr_len = true; write!(out, "::rust::repr::PtrLen error$ = "); } write!(out, "{}(", invoke); let mut needs_comma = false; if matches!(sig.kind, FnKind::Method(_)) { write!(out, "*this"); needs_comma = true; } for arg in &sig.args { if needs_comma { write!(out, ", "); } if out.types.needs_indirect_abi(&arg.ty) { write!(out, "&"); } write!(out, "{}", arg.name.cxx); match &arg.ty { Type::RustBox(_) => write!(out, ".into_raw()"), Type::UniquePtr(_) => write!(out, ".release()"), ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"), _ => {} } needs_comma = true; } if indirect_return { if needs_comma { write!(out, ", "); } write!(out, "&return$.value"); needs_comma = true; } if indirect_call { if needs_comma { write!(out, ", "); } write!(out, "extern$"); } write!(out, ")"); if !indirect_return { if let Some(Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) | Type::SliceRef(_)) = &sig.ret { write!(out, ")"); } } writeln!(out, ";"); if sig.throws { out.builtin.rust_error = true; writeln!(out, " if (error$.ptr) {{"); writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); writeln!(out, " }}"); } if indirect_return { write!(out, " return "); match sig.ret.as_ref().unwrap() { Type::Ref(_) => write!(out, "*return$.value"), _ => { out.include.utility = true; write!(out, "::std::move(return$.value)"); } } writeln!(out, ";"); } writeln!(out, "}}"); } fn write_return_type(out: &mut OutFile, ty: &Option) { match ty { None => write!(out, "void "), Some(ty) => write_type_space(out, ty), } } fn indirect_return(sig: &Signature, types: &Types, lang: Lang) -> bool { sig.ret.as_ref().is_some_and(|ret| { sig.throws || types.needs_indirect_abi(ret) || match lang { Lang::Cxx | Lang::CxxUnwind => types.contains_elided_lifetime(ret), Lang::Rust => false, } }) } fn write_indirect_return_type(out: &mut OutFile, ty: &Type) { match ty { Type::RustBox(ty) | Type::UniquePtr(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } Type::Ref(ty) => { write_type_space(out, &ty.inner); if !ty.mutable { write!(out, "const "); } write!(out, "*"); } _ => write_type(out, ty), } } fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) { write_indirect_return_type(out, ty); match ty { Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {} Type::Str(_) | Type::SliceRef(_) => write!(out, " "), _ => write_space_after_type(out, ty), } } fn write_extern_return_type_space(out: &mut OutFile, sig: &Signature, lang: Lang) { match &sig.ret { Some(_) if indirect_return(sig, out.types, lang) => write!(out, "void "), Some(Type::RustBox(ty) | Type::UniquePtr(ty)) => { write_type_space(out, &ty.inner); write!(out, "*"); } Some(Type::Ref(ty)) => { write_type_space(out, &ty.inner); if !ty.mutable { write!(out, "const "); } write!(out, "*"); } Some(Type::Str(_) | Type::SliceRef(_)) => { out.builtin.repr_fat = true; write!(out, "::rust::repr::Fat "); } ty => write_return_type(out, ty), } } fn write_extern_arg(out: &mut OutFile, arg: &Var) { match &arg.ty { Type::RustBox(ty) | Type::UniquePtr(ty) | Type::CxxVector(ty) => { write_type_space(out, &ty.inner); write!(out, "*"); } _ => write_type_space(out, &arg.ty), } if out.types.needs_indirect_abi(&arg.ty) { write!(out, "*"); } write!(out, "{}", arg.name.cxx); } fn write_type(out: &mut OutFile, ty: &Type) { write_type_to_generic_writer(out, ty, out.types); } fn stringify_type(ty: &Type, types: &Types) -> String { let mut s = String::new(); write_type_to_generic_writer(&mut s, ty, types); s } fn write_type_to_generic_writer(out: &mut impl InfallibleWrite, ty: &Type, types: &Types) { match ty { Type::Ident(ident) => match Atom::from(&ident.rust) { Some(atom) => write_atom(out, atom), None => write!(out, "{}", types.resolve(ident).name.to_fully_qualified()), }, Type::RustBox(ty) => { write!(out, "::rust::Box<"); write_type_to_generic_writer(out, &ty.inner, types); write!(out, ">"); } Type::RustVec(ty) => { write!(out, "::rust::Vec<"); write_type_to_generic_writer(out, &ty.inner, types); write!(out, ">"); } Type::UniquePtr(ptr) => { write!(out, "::std::unique_ptr<"); write_type_to_generic_writer(out, &ptr.inner, types); write!(out, ">"); } Type::SharedPtr(ptr) => { write!(out, "::std::shared_ptr<"); write_type_to_generic_writer(out, &ptr.inner, types); write!(out, ">"); } Type::WeakPtr(ptr) => { write!(out, "::std::weak_ptr<"); write_type_to_generic_writer(out, &ptr.inner, types); write!(out, ">"); } Type::CxxVector(ty) => { write!(out, "::std::vector<"); write_type_to_generic_writer(out, &ty.inner, types); write!(out, ">"); } Type::Ref(r) => { write_type_space_to_generic_writer(out, &r.inner, types); if !r.mutable { write!(out, "const "); } write!(out, "&"); } Type::Ptr(p) => { write_type_space_to_generic_writer(out, &p.inner, types); if !p.mutable { write!(out, "const "); } write!(out, "*"); } Type::Str(_) => { write!(out, "::rust::Str"); } Type::SliceRef(slice) => { write!(out, "::rust::Slice<"); write_type_space_to_generic_writer(out, &slice.inner, types); if slice.mutability.is_none() { write!(out, "const"); } write!(out, ">"); } Type::Fn(f) => { write!(out, "::rust::Fn<"); match &f.ret { Some(ret) => write_type_to_generic_writer(out, ret, types), None => write!(out, "void"), } write!(out, "("); for (i, arg) in f.args.iter().enumerate() { if i > 0 { write!(out, ", "); } write_type_to_generic_writer(out, &arg.ty, types); } write!(out, ")>"); } Type::Array(a) => { write!(out, "::std::array<"); write_type_to_generic_writer(out, &a.inner, types); write!(out, ", {}>", &a.len); } Type::Void(_) => unreachable!(), } } fn write_atom(out: &mut impl InfallibleWrite, atom: Atom) { match atom { Bool => write!(out, "bool"), Char => write!(out, "char"), U8 => write!(out, "::std::uint8_t"), U16 => write!(out, "::std::uint16_t"), U32 => write!(out, "::std::uint32_t"), U64 => write!(out, "::std::uint64_t"), Usize => write!(out, "::std::size_t"), I8 => write!(out, "::std::int8_t"), I16 => write!(out, "::std::int16_t"), I32 => write!(out, "::std::int32_t"), I64 => write!(out, "::std::int64_t"), Isize => write!(out, "::rust::isize"), F32 => write!(out, "float"), F64 => write!(out, "double"), CxxString => write!(out, "::std::string"), RustString => write!(out, "::rust::String"), } } fn write_type_space(out: &mut OutFile, ty: &Type) { write_type_space_to_generic_writer(out, ty, out.types); } fn write_type_space_to_generic_writer(out: &mut impl InfallibleWrite, ty: &Type, types: &Types) { write_type_to_generic_writer(out, ty, types); write_space_after_type(out, ty); } fn write_space_after_type(out: &mut impl InfallibleWrite, ty: &Type) { match ty { Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::SharedPtr(_) | Type::WeakPtr(_) | Type::Str(_) | Type::CxxVector(_) | Type::RustVec(_) | Type::SliceRef(_) | Type::Fn(_) | Type::Array(_) => write!(out, " "), Type::Ref(_) | Type::Ptr(_) => {} Type::Void(_) => unreachable!(), } } fn write_generic_instantiations(out: &mut OutFile) { if out.header { return; } out.next_section(); out.set_namespace(Default::default()); out.begin_block(Block::ExternC); for impl_key in out.types.impls.keys() { out.next_section(); match impl_key { ImplKey::RustBox(ident) => write_rust_box_extern(out, ident), ImplKey::RustVec(ident) => write_rust_vec_extern(out, ident), ImplKey::UniquePtr(ident) => write_unique_ptr(out, ident), ImplKey::SharedPtr(ident) => write_shared_ptr(out, ident), ImplKey::WeakPtr(ident) => write_weak_ptr(out, ident), ImplKey::CxxVector(ident) => write_cxx_vector(out, ident), } } out.end_block(Block::ExternC); out.begin_block(Block::Namespace("rust")); out.begin_block(Block::InlineNamespace("cxxbridge1")); for impl_key in out.types.impls.keys() { match impl_key { ImplKey::RustBox(ident) => write_rust_box_impl(out, ident), ImplKey::RustVec(ident) => write_rust_vec_impl(out, ident), _ => {} } } out.end_block(Block::InlineNamespace("cxxbridge1")); out.end_block(Block::Namespace("rust")); } fn write_rust_box_extern(out: &mut OutFile, key: &NamedImplKey) { let inner = stringify_type(key.inner, out.types); let instance = &key.symbol; out.pragma.dollar_in_identifier = true; writeln!( out, "{} *cxxbridge1$box${}$alloc() noexcept;", inner, instance, ); writeln!( out, "void cxxbridge1$box${}$dealloc({} *) noexcept;", instance, inner, ); writeln!( out, "void cxxbridge1$box${}$drop(::rust::Box<{}> *ptr) noexcept;", instance, inner, ); } fn write_rust_vec_extern(out: &mut OutFile, key: &NamedImplKey) { let inner = stringify_type(key.inner, out.types); let instance = &key.symbol; out.include.cstddef = true; out.pragma.dollar_in_identifier = true; writeln!( out, "void cxxbridge1$rust_vec${}$new(::rust::Vec<{}> const *ptr) noexcept;", instance, inner, ); writeln!( out, "void cxxbridge1$rust_vec${}$drop(::rust::Vec<{}> *ptr) noexcept;", instance, inner, ); writeln!( out, "::std::size_t cxxbridge1$rust_vec${}$len(::rust::Vec<{}> const *ptr) noexcept;", instance, inner, ); writeln!( out, "::std::size_t cxxbridge1$rust_vec${}$capacity(::rust::Vec<{}> const *ptr) noexcept;", instance, inner, ); writeln!( out, "{} const *cxxbridge1$rust_vec${}$data(::rust::Vec<{0}> const *ptr) noexcept;", inner, instance, ); writeln!( out, "void cxxbridge1$rust_vec${}$reserve_total(::rust::Vec<{}> *ptr, ::std::size_t new_cap) noexcept;", instance, inner, ); writeln!( out, "void cxxbridge1$rust_vec${}$set_len(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;", instance, inner, ); writeln!( out, "void cxxbridge1$rust_vec${}$truncate(::rust::Vec<{}> *ptr, ::std::size_t len) noexcept;", instance, inner, ); } fn write_rust_box_impl(out: &mut OutFile, key: &NamedImplKey) { let inner = stringify_type(key.inner, out.types); let instance = &key.symbol; out.pragma.dollar_in_identifier = true; writeln!(out, "template <>"); begin_function_definition(out); writeln!( out, "{} *Box<{}>::allocation::alloc() noexcept {{", inner, inner, ); writeln!(out, " return cxxbridge1$box${}$alloc();", instance); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!( out, "void Box<{}>::allocation::dealloc({} *ptr) noexcept {{", inner, inner, ); writeln!(out, " cxxbridge1$box${}$dealloc(ptr);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!(out, "void Box<{}>::drop() noexcept {{", inner); writeln!(out, " cxxbridge1$box${}$drop(this);", instance); writeln!(out, "}}"); } fn write_rust_vec_impl(out: &mut OutFile, key: &NamedImplKey) { let inner = stringify_type(key.inner, out.types); let instance = &key.symbol; out.include.cstddef = true; out.pragma.dollar_in_identifier = true; writeln!(out, "template <>"); begin_function_definition(out); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); writeln!(out, " cxxbridge1$rust_vec${}$new(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!(out, "void Vec<{}>::drop() noexcept {{", inner); writeln!(out, " return cxxbridge1$rust_vec${}$drop(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!( out, "::std::size_t Vec<{}>::size() const noexcept {{", inner, ); writeln!(out, " return cxxbridge1$rust_vec${}$len(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!( out, "::std::size_t Vec<{}>::capacity() const noexcept {{", inner, ); writeln!( out, " return cxxbridge1$rust_vec${}$capacity(this);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!(out, "{} const *Vec<{0}>::data() const noexcept {{", inner); writeln!(out, " return cxxbridge1$rust_vec${}$data(this);", instance); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!( out, "void Vec<{}>::reserve_total(::std::size_t new_cap) noexcept {{", inner, ); writeln!( out, " return cxxbridge1$rust_vec${}$reserve_total(this, new_cap);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!( out, "void Vec<{}>::set_len(::std::size_t len) noexcept {{", inner, ); writeln!( out, " return cxxbridge1$rust_vec${}$set_len(this, len);", instance, ); writeln!(out, "}}"); writeln!(out, "template <>"); begin_function_definition(out); writeln!(out, "void Vec<{}>::truncate(::std::size_t len) {{", inner); writeln!( out, " return cxxbridge1$rust_vec${}$truncate(this, len);", instance, ); writeln!(out, "}}"); } fn write_unique_ptr(out: &mut OutFile, key: &NamedImplKey) { write_unique_ptr_common(out, key.inner); } // Shared by UniquePtr and UniquePtr>. fn write_unique_ptr_common(out: &mut OutFile, ty: &Type) { out.include.new = true; out.include.utility = true; out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; let inner = stringify_type(ty, out.types); let instance = mangle::typename(ty, &out.types.resolutions) .expect("unexpected UniquePtr generic parameter allowed through by syntax/check.rs"); // Some aliases are to opaque types; some are to trivial types. We can't // know at code generation time, so we generate both C++ and Rust side // bindings for a "new" method anyway. But the Rust code can't be called for // Opaque types because the 'new' method is not implemented. let can_construct_from_value = out.types.is_maybe_trivial(ty); out.builtin.is_complete = true; writeln!( out, "static_assert(::rust::detail::is_complete<::std::remove_extent<{}>::type>::value, \"definition of `{}` is required\");", inner, inner, ); writeln!( out, "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");", inner, ); writeln!( out, "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");", inner, ); begin_function_definition(out); writeln!( out, "void cxxbridge1$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>();", inner); writeln!(out, "}}"); if can_construct_from_value { out.builtin.maybe_uninit = true; out.pragma.mismatched_new_delete = true; begin_function_definition(out); writeln!( out, "{} *cxxbridge1$unique_ptr${}$uninit(::std::unique_ptr<{}> *ptr) noexcept {{", inner, instance, inner, ); writeln!( out, " {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);", inner, inner, inner, ); writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(uninit);", inner); writeln!(out, " return uninit;"); writeln!(out, "}}"); } begin_function_definition(out); writeln!( out, "void cxxbridge1$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, ::std::unique_ptr<{}>::pointer raw) noexcept {{", instance, inner, inner, ); writeln!(out, " ::new (ptr) ::std::unique_ptr<{}>(raw);", inner); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "::std::unique_ptr<{}>::element_type const *cxxbridge1$unique_ptr${}$get(::std::unique_ptr<{}> const &ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.get();"); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "::std::unique_ptr<{}>::pointer cxxbridge1$unique_ptr${}$release(::std::unique_ptr<{}> &ptr) noexcept {{", inner, instance, inner, ); writeln!(out, " return ptr.release();"); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{", instance, inner, ); out.builtin.deleter_if = true; writeln!( out, " ::rust::deleter_if<::rust::detail::is_complete<{}>::value>{{}}(ptr);", inner, ); writeln!(out, "}}"); } fn write_shared_ptr(out: &mut OutFile, key: &NamedImplKey) { let inner = stringify_type(key.inner, out.types); let instance = &key.symbol; out.include.new = true; out.include.utility = true; out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; // Some aliases are to opaque types; some are to trivial types. We can't // know at code generation time, so we generate both C++ and Rust side // bindings for a "new" method anyway. But the Rust code can't be called for // Opaque types because the 'new' method is not implemented. let can_construct_from_value = out.types.is_maybe_trivial(key.inner); writeln!( out, "static_assert(sizeof(::std::shared_ptr<{}>) == 2 * sizeof(void *), \"\");", inner, ); writeln!( out, "static_assert(alignof(::std::shared_ptr<{}>) == alignof(void *), \"\");", inner, ); begin_function_definition(out); writeln!( out, "void cxxbridge1$shared_ptr${}$null(::std::shared_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>();", inner); writeln!(out, "}}"); if can_construct_from_value { out.builtin.maybe_uninit = true; out.pragma.mismatched_new_delete = true; begin_function_definition(out); writeln!( out, "{} *cxxbridge1$shared_ptr${}$uninit(::std::shared_ptr<{}> *ptr) noexcept {{", inner, instance, inner, ); writeln!( out, " {} *uninit = reinterpret_cast<{} *>(new ::rust::MaybeUninit<{}>);", inner, inner, inner, ); writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>(uninit);", inner); writeln!(out, " return uninit;"); writeln!(out, "}}"); } out.builtin.shared_ptr = true; begin_function_definition(out); writeln!( out, "bool cxxbridge1$shared_ptr${}$raw(::std::shared_ptr<{}> *ptr, ::std::shared_ptr<{}>::element_type *raw) noexcept {{", instance, inner, inner, ); writeln!( out, " ::new (ptr) ::rust::shared_ptr_if_destructible<{}>(raw);", inner, ); writeln!(out, " return ::rust::is_destructible<{}>::value;", inner); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$shared_ptr${}$clone(::std::shared_ptr<{}> const &self, ::std::shared_ptr<{}> *ptr) noexcept {{", instance, inner, inner, ); writeln!(out, " ::new (ptr) ::std::shared_ptr<{}>(self);", inner); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "::std::shared_ptr<{}>::element_type const *cxxbridge1$shared_ptr${}$get(::std::shared_ptr<{}> const &self) noexcept {{", inner, instance, inner, ); writeln!(out, " return self.get();"); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$shared_ptr${}$drop(::std::shared_ptr<{}> *self) noexcept {{", instance, inner, ); writeln!(out, " self->~shared_ptr();"); writeln!(out, "}}"); } fn write_weak_ptr(out: &mut OutFile, key: &NamedImplKey) { let inner = stringify_type(key.inner, out.types); let instance = &key.symbol; out.include.new = true; out.include.utility = true; out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; writeln!( out, "static_assert(sizeof(::std::weak_ptr<{}>) == 2 * sizeof(void *), \"\");", inner, ); writeln!( out, "static_assert(alignof(::std::weak_ptr<{}>) == alignof(void *), \"\");", inner, ); begin_function_definition(out); writeln!( out, "void cxxbridge1$weak_ptr${}$null(::std::weak_ptr<{}> *ptr) noexcept {{", instance, inner, ); writeln!(out, " ::new (ptr) ::std::weak_ptr<{}>();", inner); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$weak_ptr${}$clone(::std::weak_ptr<{}> const &self, ::std::weak_ptr<{}> *ptr) noexcept {{", instance, inner, inner, ); writeln!(out, " ::new (ptr) ::std::weak_ptr<{}>(self);", inner); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$weak_ptr${}$downgrade(::std::shared_ptr<{}> const &shared, ::std::weak_ptr<{}> *weak) noexcept {{", instance, inner, inner, ); writeln!(out, " ::new (weak) ::std::weak_ptr<{}>(shared);", inner); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$weak_ptr${}$upgrade(::std::weak_ptr<{}> const &weak, ::std::shared_ptr<{}> *shared) noexcept {{", instance, inner, inner, ); writeln!( out, " ::new (shared) ::std::shared_ptr<{}>(weak.lock());", inner, ); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$weak_ptr${}$drop(::std::weak_ptr<{}> *self) noexcept {{", instance, inner, ); writeln!(out, " self->~weak_ptr();"); writeln!(out, "}}"); } fn write_cxx_vector(out: &mut OutFile, key: &NamedImplKey) { let inner = stringify_type(key.inner, out.types); let instance = &key.symbol; out.include.cstddef = true; out.include.utility = true; out.builtin.destroy = true; out.builtin.vector = true; out.pragma.dollar_in_identifier = true; out.pragma.missing_declarations = true; begin_function_definition(out); writeln!( out, "::std::vector<{}> *cxxbridge1$std$vector${}$new() noexcept {{", inner, instance, ); writeln!(out, " return new ::std::vector<{}>();", inner); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "::std::size_t cxxbridge1$std$vector${}$size(::std::vector<{}> const &s) noexcept {{", instance, inner, ); writeln!(out, " return s.size();"); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "::std::size_t cxxbridge1$std$vector${}$capacity(::std::vector<{}> const &s) noexcept {{", instance, inner, ); writeln!(out, " return s.capacity();"); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "{} *cxxbridge1$std$vector${}$get_unchecked(::std::vector<{}> *s, ::std::size_t pos) noexcept {{", inner, instance, inner, ); writeln!(out, " return &(*s)[pos];"); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "bool cxxbridge1$std$vector${}$reserve(::std::vector<{}> *s, ::std::size_t new_cap) noexcept {{", instance, inner, ); writeln!( out, " return ::rust::if_move_constructible<{}>::reserve(*s, new_cap);", inner, ); writeln!(out, "}}"); if out.types.is_maybe_trivial(key.inner) { begin_function_definition(out); writeln!( out, "void cxxbridge1$std$vector${}$push_back(::std::vector<{}> *v, {} *value) noexcept {{", instance, inner, inner, ); writeln!(out, " v->push_back(::std::move(*value));"); writeln!(out, " ::rust::destroy(value);"); writeln!(out, "}}"); begin_function_definition(out); writeln!( out, "void cxxbridge1$std$vector${}$pop_back(::std::vector<{}> *v, {} *out) noexcept {{", instance, inner, inner, ); writeln!(out, " ::new (out) {}(::std::move(v->back()));", inner); writeln!(out, " v->pop_back();"); writeln!(out, "}}"); } out.include.memory = true; write_unique_ptr_common(out, key.outer); } ================================================ FILE: include/cxx.h ================================================ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #else #include #endif #if __cplusplus >= 201703L #include #endif #if __cplusplus >= 202002L #include #endif namespace rust { inline namespace cxxbridge1 { struct unsafe_bitcopy_t; namespace { template class impl; } #ifndef CXXBRIDGE1_RUST_STRING #define CXXBRIDGE1_RUST_STRING // https://cxx.rs/binding/string.html class String final { public: String() noexcept; String(const String &) noexcept; String(String &&) noexcept; ~String() noexcept; String(const std::string &); String(const char *); String(const char *, std::size_t); String(const char16_t *); String(const char16_t *, std::size_t); #ifdef __cpp_char8_t String(const char8_t *s); String(const char8_t *s, std::size_t len); #endif // Replace invalid Unicode data with the replacement character (U+FFFD). static String lossy(const std::string &) noexcept; static String lossy(const char *) noexcept; static String lossy(const char *, std::size_t) noexcept; static String lossy(const char16_t *) noexcept; static String lossy(const char16_t *, std::size_t) noexcept; String &operator=(const String &) & noexcept; String &operator=(String &&) & noexcept; explicit operator std::string() const; // Note: no null terminator. const char *data() const noexcept; std::size_t size() const noexcept; std::size_t length() const noexcept; bool empty() const noexcept; const char *c_str() noexcept; std::size_t capacity() const noexcept; void reserve(size_t new_cap) noexcept; using iterator = char *; iterator begin() noexcept; iterator end() noexcept; using const_iterator = const char *; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; bool operator==(const String &) const noexcept; bool operator!=(const String &) const noexcept; bool operator<(const String &) const noexcept; bool operator<=(const String &) const noexcept; bool operator>(const String &) const noexcept; bool operator>=(const String &) const noexcept; void swap(String &) noexcept; // Internal API only intended for the cxxbridge code generator. String(unsafe_bitcopy_t, const String &) noexcept; private: struct lossy_t; String(lossy_t, const char *, std::size_t) noexcept; String(lossy_t, const char16_t *, std::size_t) noexcept; friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } // Size and alignment statically verified by rust_string.rs. std::array repr; }; #endif // CXXBRIDGE1_RUST_STRING #ifndef CXXBRIDGE1_RUST_STR #define CXXBRIDGE1_RUST_STR // https://cxx.rs/binding/str.html class Str final { public: Str() noexcept; Str(const String &) noexcept; Str(const std::string &); Str(const char *); Str(const char *, std::size_t); Str &operator=(const Str &) & noexcept = default; explicit operator std::string() const; #if __cplusplus >= 201703L explicit operator std::string_view() const; #endif // Note: no null terminator. const char *data() const noexcept; std::size_t size() const noexcept; std::size_t length() const noexcept; bool empty() const noexcept; // Important in order for System V ABI to pass in registers. Str(const Str &) noexcept = default; ~Str() noexcept = default; using iterator = const char *; using const_iterator = const char *; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; bool operator==(const Str &) const noexcept; bool operator!=(const Str &) const noexcept; bool operator<(const Str &) const noexcept; bool operator<=(const Str &) const noexcept; bool operator>(const Str &) const noexcept; bool operator>=(const Str &) const noexcept; void swap(Str &) noexcept; private: class uninit; Str(uninit) noexcept; friend impl; std::array repr; }; #endif // CXXBRIDGE1_RUST_STR #ifndef CXXBRIDGE1_RUST_SLICE namespace detail { template struct copy_assignable_if {}; template <> struct copy_assignable_if { copy_assignable_if() noexcept = default; copy_assignable_if(const copy_assignable_if &) noexcept = default; copy_assignable_if &operator=(const copy_assignable_if &) & noexcept = delete; copy_assignable_if &operator=(copy_assignable_if &&) & noexcept = default; }; } // namespace detail // https://cxx.rs/binding/slice.html template class Slice final : private detail::copy_assignable_if::value> { public: using value_type = T; Slice() noexcept; Slice(T *, std::size_t count) noexcept; template explicit Slice(C &c) : Slice(c.data(), c.size()) {} Slice &operator=(const Slice &) & noexcept = default; Slice &operator=(Slice &&) & noexcept = default; T *data() const noexcept; std::size_t size() const noexcept; std::size_t length() const noexcept; bool empty() const noexcept; T &operator[](std::size_t n) const noexcept; T &at(std::size_t n) const; T &front() const noexcept; T &back() const noexcept; // Important in order for System V ABI to pass in registers. Slice(const Slice &) noexcept = default; ~Slice() noexcept = default; class iterator; iterator begin() const noexcept; iterator end() const noexcept; void swap(Slice &) noexcept; private: class uninit; Slice(uninit) noexcept; friend impl; friend void sliceInit(void *, const void *, std::size_t) noexcept; friend void *slicePtr(const void *) noexcept; friend std::size_t sliceLen(const void *) noexcept; std::array repr; }; #ifdef __cpp_deduction_guides template explicit Slice(C &c) -> Slice().data())>>; #endif // __cpp_deduction_guides template class Slice::iterator final { public: #if __cplusplus >= 202002L using iterator_category = std::contiguous_iterator_tag; #else using iterator_category = std::random_access_iterator_tag; #endif using value_type = T; using difference_type = std::ptrdiff_t; using pointer = typename std::add_pointer::type; using reference = typename std::add_lvalue_reference::type; reference operator*() const noexcept; pointer operator->() const noexcept; reference operator[](difference_type) const noexcept; iterator &operator++() noexcept; iterator operator++(int) noexcept; iterator &operator--() noexcept; iterator operator--(int) noexcept; iterator &operator+=(difference_type) noexcept; iterator &operator-=(difference_type) noexcept; iterator operator+(difference_type) const noexcept; friend inline iterator operator+(difference_type lhs, iterator rhs) noexcept { return rhs + lhs; } iterator operator-(difference_type) const noexcept; difference_type operator-(const iterator &) const noexcept; bool operator==(const iterator &) const noexcept; bool operator!=(const iterator &) const noexcept; bool operator<(const iterator &) const noexcept; bool operator<=(const iterator &) const noexcept; bool operator>(const iterator &) const noexcept; bool operator>=(const iterator &) const noexcept; private: friend class Slice; void *pos; std::size_t stride; }; #if __cplusplus >= 202002L static_assert(std::ranges::contiguous_range>); static_assert(std::contiguous_iterator::iterator>); #endif #endif // CXXBRIDGE1_RUST_SLICE #ifndef CXXBRIDGE1_RUST_BOX // https://cxx.rs/binding/box.html template class Box final { public: using element_type = T; using const_pointer = typename std::add_pointer::type>::type; using pointer = typename std::add_pointer::type; Box() = delete; Box(Box &&) noexcept; ~Box() noexcept; explicit Box(const T &); explicit Box(T &&); Box &operator=(Box &&) & noexcept; const T *operator->() const noexcept; const T &operator*() const noexcept; T *operator->() noexcept; T &operator*() noexcept; template static Box in_place(Fields &&...); void swap(Box &) noexcept; // Important: requires that `raw` came from an into_raw call. Do not pass a // pointer from `new` or any other source. static Box from_raw(T *) noexcept; T *into_raw() noexcept; /* Deprecated */ using value_type = element_type; private: class uninit; class allocation; Box(uninit) noexcept; void drop() noexcept; friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); } T *ptr; }; #endif // CXXBRIDGE1_RUST_BOX #ifndef CXXBRIDGE1_RUST_VEC // https://cxx.rs/binding/vec.html template class Vec final { public: using value_type = T; Vec() noexcept; Vec(std::initializer_list); Vec(const Vec &); Vec(Vec &&) noexcept; ~Vec() noexcept; Vec &operator=(Vec &&) & noexcept; Vec &operator=(const Vec &) &; std::size_t size() const noexcept; bool empty() const noexcept; const T *data() const noexcept; T *data() noexcept; std::size_t capacity() const noexcept; const T &operator[](std::size_t n) const noexcept; const T &at(std::size_t n) const; const T &front() const noexcept; const T &back() const noexcept; T &operator[](std::size_t n) noexcept; T &at(std::size_t n); T &front() noexcept; T &back() noexcept; void reserve(std::size_t new_cap); void push_back(const T &value); void push_back(T &&value); template void emplace_back(Args &&...args); void truncate(std::size_t len); void clear(); using iterator = typename Slice::iterator; iterator begin() noexcept; iterator end() noexcept; using const_iterator = typename Slice::iterator; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; void swap(Vec &) noexcept; // Internal API only intended for the cxxbridge code generator. Vec(unsafe_bitcopy_t, const Vec &) noexcept; private: void reserve_total(std::size_t new_cap) noexcept; void set_len(std::size_t len) noexcept; void drop() noexcept; friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } // Size and alignment statically verified by rust_vec.rs. std::array repr; }; #endif // CXXBRIDGE1_RUST_VEC #ifndef CXXBRIDGE1_RUST_FN // https://cxx.rs/binding/fn.html template class Fn; template class Fn final { public: Ret operator()(Args... args) const noexcept; Fn operator*() const noexcept; private: Ret (*trampoline)(Args..., void *fn) noexcept; void *fn; }; #endif // CXXBRIDGE1_RUST_FN #ifndef CXXBRIDGE1_RUST_ERROR #define CXXBRIDGE1_RUST_ERROR // https://cxx.rs/binding/result.html class Error final : public std::exception { public: Error(const Error &); Error(Error &&) noexcept; ~Error() noexcept override; Error &operator=(const Error &) &; Error &operator=(Error &&) & noexcept; const char *what() const noexcept override; private: Error() noexcept = default; friend impl; const char *msg; std::size_t len; }; #endif // CXXBRIDGE1_RUST_ERROR #ifndef CXXBRIDGE1_RUST_ISIZE #define CXXBRIDGE1_RUST_ISIZE #if defined(_WIN32) using isize = SSIZE_T; #else using isize = ssize_t; #endif #endif // CXXBRIDGE1_RUST_ISIZE std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); #ifndef CXXBRIDGE1_RUST_OPAQUE #define CXXBRIDGE1_RUST_OPAQUE // Base class of generated opaque Rust types. class Opaque { public: Opaque() = delete; Opaque(const Opaque &) = delete; ~Opaque() = delete; }; #endif // CXXBRIDGE1_RUST_OPAQUE template std::size_t size_of(); template std::size_t align_of(); // IsRelocatable is used in assertions that a C++ type passed by value // between Rust and C++ is soundly relocatable by Rust. // // There may be legitimate reasons to opt out of the check for support of types // that the programmer knows are soundly Rust-movable despite not being // recognized as such by the C++ type system due to a move constructor or // destructor. To opt out of the relocatability check, do either of the // following things in any header used by `include!` in the bridge. // // --- if you define the type: // struct MyType { // ... // + using IsRelocatable = std::true_type; // }; // // --- otherwise: // + template <> // + struct rust::IsRelocatable : std::true_type {}; template struct IsRelocatable; using u8 = std::uint8_t; using u16 = std::uint16_t; using u32 = std::uint32_t; using u64 = std::uint64_t; using usize = std::size_t; // see static asserts in cxx.cc using i8 = std::int8_t; using i16 = std::int16_t; using i32 = std::int32_t; using i64 = std::int64_t; using f32 = float; using f64 = double; // Snake case aliases for use in code that uses this style for type names. using string = String; using str = Str; template using slice = Slice; template using box = Box; template using vec = Vec; using error = Error; template using fn = Fn; template using is_relocatable = IsRelocatable; //////////////////////////////////////////////////////////////////////////////// /// end public API, begin implementation details #ifndef CXXBRIDGE1_PANIC #define CXXBRIDGE1_PANIC template void panic [[noreturn]] (const char *msg); #endif // CXXBRIDGE1_PANIC #ifndef CXXBRIDGE1_RUST_FN #define CXXBRIDGE1_RUST_FN template Ret Fn::operator()(Args... args) const noexcept { return (*this->trampoline)(std::forward(args)..., this->fn); } template Fn Fn::operator*() const noexcept { return *this; } #endif // CXXBRIDGE1_RUST_FN #ifndef CXXBRIDGE1_RUST_BITCOPY_T #define CXXBRIDGE1_RUST_BITCOPY_T struct unsafe_bitcopy_t final { explicit unsafe_bitcopy_t() = default; }; #endif // CXXBRIDGE1_RUST_BITCOPY_T #ifndef CXXBRIDGE1_RUST_BITCOPY #define CXXBRIDGE1_RUST_BITCOPY constexpr unsafe_bitcopy_t unsafe_bitcopy{}; #endif // CXXBRIDGE1_RUST_BITCOPY #ifndef CXXBRIDGE1_RUST_SLICE #define CXXBRIDGE1_RUST_SLICE template Slice::Slice() noexcept { sliceInit(this, reinterpret_cast(align_of()), 0); } template Slice::Slice(T *s, std::size_t count) noexcept { assert(s != nullptr || count == 0); sliceInit(this, s == nullptr && count == 0 ? reinterpret_cast(align_of()) : const_cast::type *>(s), count); } template T *Slice::data() const noexcept { return reinterpret_cast(slicePtr(this)); } template std::size_t Slice::size() const noexcept { return sliceLen(this); } template std::size_t Slice::length() const noexcept { return this->size(); } template bool Slice::empty() const noexcept { return this->size() == 0; } template T &Slice::operator[](std::size_t n) const noexcept { assert(n < this->size()); auto ptr = static_cast(slicePtr(this)) + size_of() * n; return *reinterpret_cast(ptr); } template T &Slice::at(std::size_t n) const { if (n >= this->size()) { panic("rust::Slice index out of range"); } return (*this)[n]; } template T &Slice::front() const noexcept { assert(!this->empty()); return (*this)[0]; } template T &Slice::back() const noexcept { assert(!this->empty()); return (*this)[this->size() - 1]; } template typename Slice::iterator::reference Slice::iterator::operator*() const noexcept { return *static_cast(this->pos); } template typename Slice::iterator::pointer Slice::iterator::operator->() const noexcept { return static_cast(this->pos); } template typename Slice::iterator::reference Slice::iterator::operator[]( typename Slice::iterator::difference_type n) const noexcept { auto ptr = static_cast(this->pos) + this->stride * n; return *reinterpret_cast(ptr); } template typename Slice::iterator &Slice::iterator::operator++() noexcept { this->pos = static_cast(this->pos) + this->stride; return *this; } template typename Slice::iterator Slice::iterator::operator++(int) noexcept { auto ret = iterator(*this); this->pos = static_cast(this->pos) + this->stride; return ret; } template typename Slice::iterator &Slice::iterator::operator--() noexcept { this->pos = static_cast(this->pos) - this->stride; return *this; } template typename Slice::iterator Slice::iterator::operator--(int) noexcept { auto ret = iterator(*this); this->pos = static_cast(this->pos) - this->stride; return ret; } template typename Slice::iterator &Slice::iterator::operator+=( typename Slice::iterator::difference_type n) noexcept { this->pos = static_cast(this->pos) + this->stride * n; return *this; } template typename Slice::iterator &Slice::iterator::operator-=( typename Slice::iterator::difference_type n) noexcept { this->pos = static_cast(this->pos) - this->stride * n; return *this; } template typename Slice::iterator Slice::iterator::operator+( typename Slice::iterator::difference_type n) const noexcept { auto ret = iterator(*this); ret.pos = static_cast(this->pos) + this->stride * n; return ret; } template typename Slice::iterator Slice::iterator::operator-( typename Slice::iterator::difference_type n) const noexcept { auto ret = iterator(*this); ret.pos = static_cast(this->pos) - this->stride * n; return ret; } template typename Slice::iterator::difference_type Slice::iterator::operator-(const iterator &other) const noexcept { auto diff = std::distance(static_cast(other.pos), static_cast(this->pos)); return diff / static_cast::iterator::difference_type>( this->stride); } template bool Slice::iterator::operator==(const iterator &other) const noexcept { return this->pos == other.pos; } template bool Slice::iterator::operator!=(const iterator &other) const noexcept { return this->pos != other.pos; } template bool Slice::iterator::operator<(const iterator &other) const noexcept { return this->pos < other.pos; } template bool Slice::iterator::operator<=(const iterator &other) const noexcept { return this->pos <= other.pos; } template bool Slice::iterator::operator>(const iterator &other) const noexcept { return this->pos > other.pos; } template bool Slice::iterator::operator>=(const iterator &other) const noexcept { return this->pos >= other.pos; } template typename Slice::iterator Slice::begin() const noexcept { iterator it; it.pos = slicePtr(this); it.stride = size_of(); return it; } template typename Slice::iterator Slice::end() const noexcept { iterator it = this->begin(); it.pos = static_cast(it.pos) + it.stride * this->size(); return it; } template void Slice::swap(Slice &rhs) noexcept { std::swap(*this, rhs); } #endif // CXXBRIDGE1_RUST_SLICE #ifndef CXXBRIDGE1_RUST_BOX #define CXXBRIDGE1_RUST_BOX template class Box::uninit {}; template class Box::allocation { static T *alloc() noexcept; static void dealloc(T *) noexcept; public: allocation() noexcept : ptr(alloc()) {} ~allocation() noexcept { if (this->ptr) { dealloc(this->ptr); } } T *ptr; }; template Box::Box(Box &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } template Box::Box(const T &val) { allocation alloc; ::new (alloc.ptr) T(val); this->ptr = alloc.ptr; alloc.ptr = nullptr; } template Box::Box(T &&val) { allocation alloc; ::new (alloc.ptr) T(std::move(val)); this->ptr = alloc.ptr; alloc.ptr = nullptr; } template Box::~Box() noexcept { if (this->ptr) { this->drop(); } } template Box &Box::operator=(Box &&other) & noexcept { if (this->ptr) { this->drop(); } this->ptr = other.ptr; other.ptr = nullptr; return *this; } template const T *Box::operator->() const noexcept { return this->ptr; } template const T &Box::operator*() const noexcept { return *this->ptr; } template T *Box::operator->() noexcept { return this->ptr; } template T &Box::operator*() noexcept { return *this->ptr; } template template Box Box::in_place(Fields &&...fields) { allocation alloc; auto ptr = alloc.ptr; ::new (ptr) T{std::forward(fields)...}; alloc.ptr = nullptr; return from_raw(ptr); } template void Box::swap(Box &rhs) noexcept { using std::swap; swap(this->ptr, rhs.ptr); } template Box Box::from_raw(T *raw) noexcept { Box box = uninit{}; box.ptr = raw; return box; } template T *Box::into_raw() noexcept { T *raw = this->ptr; this->ptr = nullptr; return raw; } template Box::Box(uninit) noexcept {} #endif // CXXBRIDGE1_RUST_BOX #ifndef CXXBRIDGE1_RUST_VEC #define CXXBRIDGE1_RUST_VEC template Vec::Vec(std::initializer_list init) : Vec{} { this->reserve_total(init.size()); std::move(init.begin(), init.end(), std::back_inserter(*this)); } template Vec::Vec(const Vec &other) : Vec() { this->reserve_total(other.size()); std::copy(other.begin(), other.end(), std::back_inserter(*this)); } template Vec::Vec(Vec &&other) noexcept : repr(other.repr) { new (&other) Vec(); } template Vec::~Vec() noexcept { this->drop(); } template Vec &Vec::operator=(Vec &&other) & noexcept { this->drop(); this->repr = other.repr; new (&other) Vec(); return *this; } template Vec &Vec::operator=(const Vec &other) & { if (this != &other) { this->drop(); new (this) Vec(other); } return *this; } template bool Vec::empty() const noexcept { return this->size() == 0; } template T *Vec::data() noexcept { return const_cast(const_cast *>(this)->data()); } template const T &Vec::operator[](std::size_t n) const noexcept { assert(n < this->size()); auto data = reinterpret_cast(this->data()); return *reinterpret_cast(data + n * size_of()); } template const T &Vec::at(std::size_t n) const { if (n >= this->size()) { panic("rust::Vec index out of range"); } return (*this)[n]; } template const T &Vec::front() const noexcept { assert(!this->empty()); return (*this)[0]; } template const T &Vec::back() const noexcept { assert(!this->empty()); return (*this)[this->size() - 1]; } template T &Vec::operator[](std::size_t n) noexcept { assert(n < this->size()); auto data = reinterpret_cast(this->data()); return *reinterpret_cast(data + n * size_of()); } template T &Vec::at(std::size_t n) { if (n >= this->size()) { panic("rust::Vec index out of range"); } return (*this)[n]; } template T &Vec::front() noexcept { assert(!this->empty()); return (*this)[0]; } template T &Vec::back() noexcept { assert(!this->empty()); return (*this)[this->size() - 1]; } template void Vec::reserve(std::size_t new_cap) { this->reserve_total(new_cap); } template void Vec::push_back(const T &value) { this->emplace_back(value); } template void Vec::push_back(T &&value) { this->emplace_back(std::move(value)); } template template void Vec::emplace_back(Args &&...args) { auto size = this->size(); this->reserve_total(size + 1); ::new (reinterpret_cast(reinterpret_cast(this->data()) + size * size_of())) T(std::forward(args)...); this->set_len(size + 1); } template void Vec::clear() { this->truncate(0); } template typename Vec::iterator Vec::begin() noexcept { return Slice(this->data(), this->size()).begin(); } template typename Vec::iterator Vec::end() noexcept { return Slice(this->data(), this->size()).end(); } template typename Vec::const_iterator Vec::begin() const noexcept { return this->cbegin(); } template typename Vec::const_iterator Vec::end() const noexcept { return this->cend(); } template typename Vec::const_iterator Vec::cbegin() const noexcept { return Slice(this->data(), this->size()).begin(); } template typename Vec::const_iterator Vec::cend() const noexcept { return Slice(this->data(), this->size()).end(); } template void Vec::swap(Vec &rhs) noexcept { using std::swap; swap(this->repr, rhs.repr); } // Internal API only intended for the cxxbridge code generator. template Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} #endif // CXXBRIDGE1_RUST_VEC #ifndef CXXBRIDGE1_IS_COMPLETE #define CXXBRIDGE1_IS_COMPLETE namespace detail { namespace { template struct is_complete : std::false_type {}; template struct is_complete : std::true_type {}; } // namespace } // namespace detail #endif // CXXBRIDGE1_IS_COMPLETE #ifndef CXXBRIDGE1_LAYOUT #define CXXBRIDGE1_LAYOUT class layout { template friend std::size_t size_of(); template friend std::size_t align_of(); template static typename std::enable_if::value, std::size_t>::type do_size_of() { return T::layout::size(); } template static typename std::enable_if::value, std::size_t>::type do_size_of() { return sizeof(T); } template static typename std::enable_if::value, std::size_t>::type size_of() { return do_size_of(); } template static typename std::enable_if::value, std::size_t>::type do_align_of() { return T::layout::align(); } template static typename std::enable_if::value, std::size_t>::type do_align_of() { return alignof(T); } template static typename std::enable_if::value, std::size_t>::type align_of() { return do_align_of(); } }; template std::size_t size_of() { return layout::size_of(); } template std::size_t align_of() { return layout::align_of(); } #endif // CXXBRIDGE1_LAYOUT #ifndef CXXBRIDGE1_RELOCATABLE #define CXXBRIDGE1_RELOCATABLE namespace detail { template struct make_void { using type = void; }; template using void_t = typename make_void::type; template class, typename...> struct detect : std::false_type {}; template