├── .github └── workflows │ └── cpp.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── bindgen ├── Cargo.toml ├── askama.toml └── src │ ├── bindings │ ├── cpp │ │ ├── gen_cpp │ │ │ ├── callback_interface.rs │ │ │ ├── compounds.rs │ │ │ ├── custom.rs │ │ │ ├── enum_.rs │ │ │ ├── filters │ │ │ │ └── mod.rs │ │ │ ├── miscellany.rs │ │ │ ├── mod.rs │ │ │ ├── object.rs │ │ │ ├── primitives.rs │ │ │ └── record.rs │ │ ├── mod.rs │ │ └── templates │ │ │ ├── arith_conv.cpp │ │ │ ├── arith_conv.hpp │ │ │ ├── bool_conv.cpp │ │ │ ├── bool_conv.hpp │ │ │ ├── bytes_conv.cpp │ │ │ ├── bytes_conv.hpp │ │ │ ├── callback.hpp │ │ │ ├── callback_conv.cpp │ │ │ ├── callback_conv.hpp │ │ │ ├── callback_iface_tmpl.cpp │ │ │ ├── cpp_scaffolding.cpp │ │ │ ├── custom.cpp │ │ │ ├── custom.hpp │ │ │ ├── duration_conv.hpp │ │ │ ├── duration_helper.cpp │ │ │ ├── enum.hpp │ │ │ ├── enum_conv.hpp │ │ │ ├── enum_tmpl.cpp │ │ │ ├── err.hpp │ │ │ ├── err_conv.hpp │ │ │ ├── err_tmpl.cpp │ │ │ ├── ext_typ_tmpl.cpp │ │ │ ├── fn_def.cpp │ │ │ ├── handle_map.cpp │ │ │ ├── internal_types.cpp │ │ │ ├── macros.cpp │ │ │ ├── map_conv.hpp │ │ │ ├── map_tmpl.cpp │ │ │ ├── obj.cpp │ │ │ ├── obj.hpp │ │ │ ├── obj_conv.cpp │ │ │ ├── obj_conv.hpp │ │ │ ├── opt_conv.hpp │ │ │ ├── opt_tmpl.cpp │ │ │ ├── rec.cpp │ │ │ ├── rec.hpp │ │ │ ├── rec_conv.hpp │ │ │ ├── rust_buf_stream.cpp │ │ │ ├── rust_buf_tmpl.cpp │ │ │ ├── scaffolding.hpp │ │ │ ├── scaffolding │ │ │ ├── callback.cpp │ │ │ ├── callback.hpp │ │ │ ├── custom.cpp │ │ │ ├── custom.hpp │ │ │ ├── err.cpp │ │ │ ├── err.hpp │ │ │ ├── macros.cpp │ │ │ ├── obj.cpp │ │ │ ├── obj.hpp │ │ │ └── object_map.cpp │ │ │ ├── seq_conv.hpp │ │ │ ├── seq_tmpl.cpp │ │ │ ├── str_conv.cpp │ │ │ ├── str_conv.hpp │ │ │ ├── timestamp_conv.hpp │ │ │ ├── timestamp_helper.cpp │ │ │ ├── types.cpp │ │ │ ├── wrapper.cpp │ │ │ └── wrapper.hpp │ └── mod.rs │ └── main.rs ├── build_scaffolding_lib.sh ├── cpp-tests ├── CMakeLists.txt ├── include │ ├── custom_string.hpp │ └── test_common.hpp ├── scaffolding_tests │ ├── arithmetic │ │ ├── lib_arithmetic.cpp │ │ └── lib_arithmetic.hpp │ ├── callbacks │ │ ├── lib_callbacks.cpp │ │ └── lib_callbacks.hpp │ ├── chronological │ │ ├── lib_chronological.cpp │ │ └── lib_chronological.hpp │ ├── coverall │ │ ├── lib_coverall.cpp │ │ └── lib_coverall.hpp │ ├── custom_types │ │ ├── lib_custom_types.cpp │ │ └── lib_custom_types.hpp │ ├── custom_types_builtin │ │ ├── lib_custom_types_builtin.cpp │ │ └── lib_custom_types_builtin.hpp │ ├── empty_type │ │ ├── lib_empty_type.cpp │ │ └── lib_empty_type.hpp │ ├── enum_style_test │ │ ├── lib_enum_style_test.cpp │ │ └── lib_enum_style_test.hpp │ ├── fixture_callbacks │ │ ├── lib_fixture_callbacks.cpp │ │ └── lib_fixture_callbacks.hpp │ ├── geometry │ │ ├── lib_geometry.cpp │ │ └── lib_geometry.hpp │ ├── rondpoint │ │ ├── lib_rondpoint.cpp │ │ └── lib_rondpoint.hpp │ ├── sprites │ │ ├── lib_sprites.cpp │ │ └── lib_sprites.hpp │ ├── todolist │ │ ├── lib_todolist.cpp │ │ └── lib_todolist.hpp │ └── traits │ │ ├── lib_traits.cpp │ │ └── lib_traits.hpp └── tests │ ├── arithmetic │ └── main.cpp │ ├── callbacks │ └── main.cpp │ ├── chronological │ └── main.cpp │ ├── coverall │ └── main.cpp │ ├── custom_types │ └── main.cpp │ ├── custom_types_builtin │ └── main.cpp │ ├── empty_type │ └── main.cpp │ ├── enum_style_test │ └── main.cpp │ ├── error_types_builtin │ └── main.cpp │ ├── fixture_callbacks │ └── main.cpp │ ├── geometry │ └── main.cpp │ ├── multilib │ └── main.cpp │ ├── rondpoint │ └── main.cpp │ ├── sprites │ └── main.cpp │ ├── todolist │ └── main.cpp │ ├── trait_methods │ └── main.cpp │ ├── traits │ └── main.cpp │ └── uniffi_docstring │ └── main.cpp ├── docker.sh ├── docs ├── CONFIGURATION.md ├── RELEASE.md └── SCAFFOLDING.md ├── fixtures ├── Cargo.lock ├── Cargo.toml ├── custom-types-builtin │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── custom_types_builtin.udl │ │ └── lib.rs │ └── uniffi.toml ├── empty-type │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── empty_type.udl │ │ └── lib.rs ├── enum-style-test │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── enum_style_test.udl │ │ └── lib.rs │ └── uniffi.toml ├── error-types-builtin │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── error_types_builtin.udl │ │ └── lib.rs └── src │ └── lib.rs ├── test_bindings.sh ├── test_scaffolding_cs.sh └── test_scaffolding_go.sh /.github/workflows/cpp.yml: -------------------------------------------------------------------------------- 1 | name: C++ 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | run-tests: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-22.04, windows-latest, macos-13] 18 | build_type: [Release] 19 | cpp_compiler: [g++, clang++, cl] 20 | exclude: 21 | - os: windows-latest 22 | cpp_compiler: clang++ 23 | - os: windows-latest 24 | cpp_compiler: g++ 25 | - os: ubuntu-22.04 26 | cpp_compiler: cl 27 | - os: macos-13 28 | cpp_compiler: cl 29 | 30 | steps: 31 | - uses: actions/checkout@v3 32 | 33 | - name: Set reusable strings 34 | id: strings 35 | shell: bash 36 | run: | 37 | echo "build-output-dir=${{ github.workspace }}/cpp-tests/build" >> "$GITHUB_OUTPUT" 38 | echo "build-source-dir=${{ github.workspace }}/cpp-tests" >> "$GITHUB_OUTPUT" 39 | 40 | - name: Install additional dependencies 41 | shell: bash 42 | run: | 43 | if [ "$RUNNER_OS" == "Linux" ]; then 44 | sudo apt update && sudo apt install -y valgrind 45 | fi 46 | 47 | - name: Configure CMake 48 | run: > 49 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 50 | -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} 51 | -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} 52 | -S ${{ steps.strings.outputs.build-source-dir }} 53 | 54 | - name: Build 55 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} 56 | 57 | - name: Run tests 58 | working-directory: ${{ steps.strings.outputs.build-output-dir }} 59 | run: ctest --build-config ${{ matrix.build_type }} 60 | 61 | # build-scaffolding-lib: 62 | # runs-on: ubuntu-latest 63 | # container: 64 | # image: ghcr.io/nordsecurity/uniffi-bindgen-cpp-test-runner:v0.3.0 65 | # steps: 66 | # - uses: actions/checkout@v3 67 | # 68 | # - name: Build scaffolding library 69 | # shell: bash 70 | # env: 71 | # # Github sets HOME to /github/home and breaks dependencies in Docker image.. 72 | # # https://github.com/actions/runner/issues/863 73 | # HOME: /root 74 | # run: | 75 | # source ~/.bashrc 76 | # ./build_scaffolding_lib.sh 77 | # - uses: actions/upload-artifact@v3 78 | # with: 79 | # name: scaffolding_lib 80 | # path: cpp-tests/build/libuniffi_fixtures.so 81 | # 82 | # test-scaffolding-go: 83 | # runs-on: ubuntu-latest 84 | # container: 85 | # image: ghcr.io/nordsecurity/uniffi-bindgen-cpp-test-runner:v0.3.0 86 | # needs: build-scaffolding-lib 87 | # steps: 88 | # - uses: actions/checkout@v3 89 | # with: 90 | # submodules: recursive 91 | # - uses: actions/download-artifact@v3 92 | # with: 93 | # name: scaffolding_lib 94 | # path: cpp-tests/build/ 95 | # - name: Test scaffolding Go 96 | # shell: bash 97 | # env: 98 | # HOME: /root 99 | # run: | 100 | # source ~/.bashrc 101 | # ./test_scaffolding_go.sh 102 | # 103 | # test-scaffolding-cs: 104 | # runs-on: ubuntu-latest 105 | # container: 106 | # image: ghcr.io/nordsecurity/uniffi-bindgen-cs-test-runner:v0.1.0 107 | # needs: build-scaffolding-lib 108 | # steps: 109 | # - uses: actions/checkout@v3 110 | # with: 111 | # submodules: recursive 112 | # - uses: actions/download-artifact@v3 113 | # with: 114 | # name: scaffolding_lib 115 | # path: cpp-tests/build/ 116 | # - name: Test scaffolding C# 117 | # shell: bash 118 | # env: 119 | # HOME: /root 120 | # run: | 121 | # source ~/.bashrc 122 | # ./test_scaffolding_cs.sh 123 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | cpp-tests/build 3 | .cache 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rd-party/uniffi-bindgen-go"] 2 | path = 3rd-party/uniffi-bindgen-go 3 | url = https://github.com/NordSecurity/uniffi-bindgen-go 4 | [submodule "3rd-party/uniffi-bindgen-cs"] 5 | path = 3rd-party/uniffi-bindgen-cs 6 | url = https://github.com/NordSecurity/uniffi-bindgen-cs 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### v0.7.2+v0.28.3 2 | 3 | ---- 4 | - Core: Remove `_uniffi_internal` prefix from the `throw_underlying` function 5 | 6 | #### v0.7.1+v0.28.3 7 | 8 | ---- 9 | - Core: Generate used code only in the scaffolding header 10 | 11 | #### v0.7.0+v0.28.3 12 | 13 | ---- 14 | - Core: Update bindgen to UniFFI v0.28.3 15 | 16 | #### v0.6.4+v0.25.0 17 | 18 | ---- 19 | - Core: Explicitly include `vector` in the generated wrapper header 20 | - Core: Explicitly include `algorithm` in the generated wrapper header 21 | 22 | 23 | #### v0.6.3+v0.25.0 24 | 25 | ---- 26 | - Core: Forward-declare non-flat uniffi enums #46 27 | - Core: Fix invalid allocation_size code generation when dealing with empty records #45 28 | 29 | 30 | #### v0.6.2+v0.25.0 31 | 32 | ---- 33 | - Core: Fix constructor argument ordering for `RustStream` 34 | - Core: Fix topological sorting not taking into account structurally recursive types #43 35 | 36 | #### v0.6.1+v0.25.0 37 | 38 | ---- 39 | - Core: Added header guard for internal FFI structs in scaffolding header 40 | 41 | #### v0.6.0+v0.25.0 42 | 43 | ---- 44 | - Core: Added ability to customize enum variant naming styles 45 | - Core: **BREAKING** Changed default enum variant naming style to `kEnumVariant` 46 | 47 | #### v0.5.0+v0.25.0 48 | 49 | ---- 50 | 51 | - Core: **POTENTIALLY BREAKING** changed `timestamp` type from `time_point`, to `time_point` 52 | 53 | 54 | #### v0.4.2+v0.25.0 55 | 56 | ---- 57 | 58 | - Scaffolding: Add support for custom types 59 | - Scaffolding: Allow multiple scaffolding implementations to exist in a compiled library/executable 60 | - Scaffolding: Add support for errors 61 | - Scaffolding: Add internal ref counts for object types 62 | - Scaffolding: Add support for associated enums 63 | - Core: Change the underlying type of `RustStream` and `RustStreamBuffer` to `char` from `uint8_t` 64 | 65 | #### v0.4.1+v0.25.0 66 | 67 | ---- 68 | 69 | - Scaffolding: Decorate public functions with `__declspec(dllexport)` under Windows and `__attribute__((visibility("default")))` on other platforms 70 | - Core: Make complex function arguments be passed by `const&` for non-callback functions 71 | - Core: Write enums based on variant instead of casting to uint during conversion 72 | 73 | #### v0.4.0+v0.25.0 74 | 75 | ---- 76 | 77 | - Add experimental C++ scaffolding generation option 78 | 79 | #### v0.3.0+v0.25.0 80 | 81 | ---- 82 | 83 | - Dereference optional objects in the generated bindings 84 | - **IMPORTANT**: Fix callback code generation 85 | 86 | #### v0.2.2+v0.25.0 87 | 88 | ---- 89 | 90 | - Implement checksum verifition for the generated bindings. 91 | 92 | 93 | #### v0.2.1+v0.25.0 94 | 95 | ---- 96 | 97 | - Fix incorrect macro invocation in object bindings. 98 | 99 | #### v0.2.0+v0.25.0 100 | 101 | ---- 102 | 103 | - Move bindgen config under the `bindings.cpp` section in the config. 104 | - Add virtual destructors to callback and error abstract classes. 105 | - Implement destructors for objects. 106 | - Wrap objects in `std::shared_ptr` instead of `std::unique_ptr`. 107 | - Expose access to complex enum variants in the generated bindings. 108 | - Add docstrings to the generated bindings. 109 | - Remove assignment operators and copy constructors for objects to prevent misuse. 110 | - Add generated trait methods (Display, Debug, Eq, Hash) from Rust in ojbects. 111 | - Add support for custom types. 112 | 113 | ### v0.1.0+v0.25.0 114 | 115 | ---- 116 | 117 | - Initial release. 118 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to submit changes 2 | 3 | Pull requests are welcome! 4 | 5 | # How to report issues/bugs? 6 | 7 | Create an issue on Github, we will try to get back to you ASAP. 8 | 9 | # Checkout the code 10 | 11 | ``` 12 | git clone https://github.com/NordSecurity/uniffi-bindgen-cpp.git 13 | ``` 14 | 15 | # Run tests 16 | 17 | To run tests, a `cmake` installation is required. Unlike `uniffi-rs`, there is no integration with 18 | `cargo test`. 19 | 20 | - Build `uniffi-bindgen-cpp` executable, `libuniffi_fixtures.so` shared library and produce the bindings. 21 | 22 | ``` 23 | cd cpp-tests 24 | mkdir build 25 | cd build 26 | cmake .. 27 | make 28 | ``` 29 | 30 | - Run tests 31 | 32 | ``` 33 | make test 34 | ``` 35 | 36 | # Thank you 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["bindgen", "fixtures"] 3 | 4 | resolver = "2" 5 | 6 | [workspace.dependencies] 7 | uniffi = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.28.3" } 8 | uniffi_bindgen = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.28.3", features = ["cargo-metadata"] } 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:10-bullseye 2 | 3 | LABEL org.opencontainers.image.source=https://github.com/NordSecurity/uniffi-bindgen-cpp 4 | 5 | RUN apt-get update && apt-get install -y --no-install-recommends cmake curl valgrind && apt-get clean 6 | 7 | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=1.72 8 | 9 | RUN wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz \ 10 | && tar -C /usr/local -xzf go1.22.1.linux-amd64.tar.gz \ 11 | && echo 'export PATH=/usr/local/go/bin:$PATH' >> /root/.bashrc 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uniffi-bindgen-cpp - UniFFI C++ bindings/scaffolding generator 2 | 3 | Generate [UniFFI](https://github.com/mozilla/uniffi-rs) bindings for C++. `uniffi-bindgen-cpp` lives 4 | as a separate project from `uniffi-rs`, as per 5 | [uniffi-rs #1355](https://github.com/mozilla/uniffi-rs/issues/1355). 6 | 7 | # How to install 8 | 9 | Minimum Rust version required to install `uniffi-bindgen-cpp` is `1.74`. 10 | Newer Rust versions should also work fine. 11 | 12 | ```bash 13 | cargo install uniffi-bindgen-cpp --git https://github.com/NordSecurity/uniffi-bindgen-cpp --tag v0.7.1+v0.28.3 14 | ``` 15 | 16 | # How to generate bindings 17 | 18 | ## Generating with a single UDL file 19 | 20 | ```bash 21 | uniffi-bindgen-cpp path/to/definitions.udl 22 | ``` 23 | 24 | ## Generating using a library file 25 | 26 | ```bash 27 | uniffi-bindgen-cpp --library your_rust_library.so --out-dir output_directory 28 | ``` 29 | 30 | Regardless of the generation method, these files are produced: 31 | 32 | * `path/to/definitions.hpp` 33 | * `path/to/definitions.cpp` 34 | * `path/to/definitions_scaffolding.hpp` 35 | 36 | # How to integrate bindings 37 | 38 | To integrate the bindings into your projects, simply add the generated bindings files to your project. 39 | C++20 is required to compile the bindings. 40 | 41 | # Unsupported features 42 | 43 | The following uniffi features are unsupported. 44 | 45 | * External types 46 | * Async functions 47 | 48 | # Configuration options 49 | 50 | It's possible to [configure some settings](docs/CONFIGURATION.md) by passing `--config` 51 | argument to the generator. 52 | 53 | Note: configuration is not supported when using library mode 54 | 55 | ```bash 56 | uniffi-bindgen-cpp path/to/definitions.udl --config path/to/uniffi.toml 57 | ``` 58 | 59 | # C++ Scaffolding 60 | 61 | It is possible to generate C++ uniffi scaffolding that allows bridging C++ code with any other uniffi supported language (except Rust). 62 | More documentation and limitations can be found in the [scaffolding documentation](docs/SCAFFOLDING.md) 63 | 64 | # Versioning 65 | 66 | `uniffi-bindgen-cpp` is versioned separately from `uniffi-rs`. UniFFI follows the [SemVer rules from 67 | the Cargo Book](https://doc.rust-lang.org/cargo/reference/resolver.html#semver-compatibility) 68 | which states "Versions are considered compatible if their left-most non-zero 69 | major/minor/patch component is the same". A breaking change is any modification to the C++ bindings 70 | that demands the consumer of the bindings to make corresponding changes to their code to ensure that 71 | the bindings continue to function properly. `uniffi-bindgen-cpp` is young, and it's unclear how stable 72 | the generated bindings are going to be between versions. For this reason, major version is currently 73 | 0, and most changes are probably going to bump minor version. 74 | 75 | To ensure consistent feature set across external binding generators, `uniffi-bindgen-cpp` targets 76 | a specific `uniffi-rs` version. A consumer using Go bindings (in `uniffi-bindgen-go`) and C# 77 | bindings (in `uniffi-bindgen-cs`) expects the same features to be available across multiple bindings 78 | generators. This means that the consumer should choose external binding generator versions such that 79 | each generator targets the same `uniffi-rs` version. 80 | 81 | To simplify this choice `uniffi-bindgen-cpp`, `uniffi-bindgen-cs` and `uniffi-bindgen-go` use tag naming convention 82 | as follows: `vX.Y.Z+vA.B.C`, where `X.Y.Z` is the version of the generator itself, and `A.B.C` is 83 | the version of uniffi-rs it is based on. 84 | 85 | The table shows `uniffi-rs` version history for tags that were published before tag naming convention described above was introduced. 86 | 87 | | uniffi-bindgen-cpp version | uniffi-rs version | 88 | |------------------------------------------|--------------------------------------------------| 89 | | v0.1.0 | v0.25.0 | 90 | | v0.2.0 | v0.25.0 | 91 | | v0.2.1 | v0.25.0 | 92 | | v0.2.2 | v0.25.0 | 93 | | v0.7.0 | v0.28.3 | 94 | 95 | # Documentation 96 | 97 | More documentation is available in [docs](docs) directory. 98 | 99 | # Contributing 100 | 101 | For contribution guidelines, read [CONTRIBUTING.md](CONTRIBUTING.md). 102 | -------------------------------------------------------------------------------- /bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uniffi-bindgen-cpp" 3 | version = "0.7.1+v.0.28.3" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.75" 8 | askama = "0.12.0" 9 | camino = "1.1.6" 10 | clap = { version = "4.4.2", features = ["derive"] } 11 | heck = "0.4.1" 12 | paste = "1" 13 | serde = "1" 14 | topological-sort = "0.2.2" 15 | textwrap = "0.16.0" 16 | toml = "0.5" 17 | uniffi_bindgen = { workspace = true } 18 | cargo_metadata = "0.15" 19 | -------------------------------------------------------------------------------- /bindgen/askama.toml: -------------------------------------------------------------------------------- 1 | [general] 2 | dirs = ["src/bindings/cpp/templates"] 3 | 4 | [[syntax]] 5 | name = "cpp" 6 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/callback_interface.rs: -------------------------------------------------------------------------------- 1 | use uniffi_bindgen::{interface::Literal, ComponentInterface}; 2 | 3 | use crate::bindings::cpp::{ 4 | gen_cpp::filters::callback_interface_name, gen_cpp::filters::CppCodeOracle, CodeType, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub(crate) struct CallbackInterfaceCodeType { 9 | id: String, 10 | } 11 | 12 | impl CallbackInterfaceCodeType { 13 | pub(crate) fn new(id: String) -> Self { 14 | Self { id } 15 | } 16 | } 17 | 18 | impl CodeType for CallbackInterfaceCodeType { 19 | fn type_label(&self, _ci: &ComponentInterface) -> String { 20 | format!("std::shared_ptr<{}>", self.canonical_name()) 21 | } 22 | 23 | fn canonical_name(&self) -> String { 24 | CppCodeOracle.class_name(&self.id) 25 | } 26 | 27 | fn literal(&self, _literal: &Literal, _ci: &ComponentInterface) -> String { 28 | unreachable!(); 29 | } 30 | 31 | fn initialization_fn(&self) -> Option { 32 | Some(format!( 33 | "uniffi::{}::init", 34 | callback_interface_name(&self.id).unwrap() 35 | )) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/compounds.rs: -------------------------------------------------------------------------------- 1 | use crate::bindings::cpp::CodeType; 2 | use uniffi_bindgen::{backend::Type, interface::Literal, ComponentInterface}; 3 | 4 | use crate::bindings::cpp::gen_cpp::filters::CppCodeOracle; 5 | 6 | #[derive(Debug)] 7 | pub(crate) struct OptionalCodeType { 8 | inner: Type, 9 | } 10 | 11 | impl OptionalCodeType { 12 | pub(crate) fn new(inner: Type) -> Self { 13 | Self { inner } 14 | } 15 | 16 | pub(crate) fn can_dereference(inner_type: &Type, ci: &ComponentInterface) -> bool { 17 | match inner_type { 18 | Type::Object { .. } | Type::CallbackInterface { .. } => true, 19 | Type::Enum { name, .. } => ci.is_name_used_as_error(name), 20 | _ => false, 21 | } 22 | } 23 | } 24 | 25 | impl CodeType for OptionalCodeType { 26 | fn type_label(&self, ci: &ComponentInterface) -> String { 27 | if OptionalCodeType::can_dereference(&self.inner, ci) { 28 | CppCodeOracle.find(&self.inner).type_label(ci) 29 | } else { 30 | format!( 31 | "std::optional<{}>", 32 | CppCodeOracle.find(&self.inner).type_label(ci) 33 | ) 34 | } 35 | } 36 | 37 | fn canonical_name(&self) -> String { 38 | format!( 39 | "Optional{}", 40 | CppCodeOracle.find(&self.inner).canonical_name(), 41 | ) 42 | } 43 | 44 | fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String { 45 | match literal { 46 | Literal::None => "std::nullopt".into(), 47 | _ => CppCodeOracle.find(&self.inner).literal(literal, ci), 48 | } 49 | } 50 | } 51 | 52 | #[derive(Debug)] 53 | pub(crate) struct SequenceCodeType { 54 | inner: Type, 55 | } 56 | 57 | impl SequenceCodeType { 58 | pub(crate) fn new(inner: Type) -> Self { 59 | Self { inner } 60 | } 61 | } 62 | 63 | impl CodeType for SequenceCodeType { 64 | fn type_label(&self, ci: &ComponentInterface) -> String { 65 | format!( 66 | "std::vector<{}>", 67 | CppCodeOracle.find(&self.inner).type_label(ci) 68 | ) 69 | } 70 | 71 | fn canonical_name(&self) -> String { 72 | format!( 73 | "Sequence{}", 74 | CppCodeOracle.find(&self.inner).canonical_name(), 75 | ) 76 | } 77 | 78 | fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String { 79 | match literal { 80 | Literal::EmptySequence => "{}".into(), 81 | _ => CppCodeOracle.find(&self.inner).literal(literal, ci), 82 | } 83 | } 84 | } 85 | 86 | #[derive(Debug)] 87 | pub(crate) struct MapCodeType { 88 | key: Type, 89 | value: Type, 90 | } 91 | 92 | impl MapCodeType { 93 | pub(crate) fn new(key: Type, value: Type) -> Self { 94 | Self { key, value } 95 | } 96 | 97 | fn key(&self) -> &Type { 98 | &self.key 99 | } 100 | 101 | fn value(&self) -> &Type { 102 | &self.value 103 | } 104 | } 105 | 106 | impl CodeType for MapCodeType { 107 | fn type_label(&self, ci: &ComponentInterface) -> String { 108 | format!( 109 | "std::unordered_map<{}, {}>", 110 | CppCodeOracle.find(self.key()).type_label(ci), 111 | CppCodeOracle.find(self.value()).type_label(ci), 112 | ) 113 | } 114 | 115 | fn canonical_name(&self) -> String { 116 | format!( 117 | "Map{}{}", 118 | CppCodeOracle.find(self.key()).canonical_name(), 119 | CppCodeOracle.find(self.value()).canonical_name(), 120 | ) 121 | } 122 | 123 | fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String { 124 | match literal { 125 | Literal::EmptyMap => "{}".into(), 126 | _ => CppCodeOracle.find(&self.value).literal(literal, ci), 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/custom.rs: -------------------------------------------------------------------------------- 1 | use uniffi_bindgen::ComponentInterface; 2 | 3 | use crate::bindings::cpp::CodeType; 4 | 5 | #[derive(Debug)] 6 | pub struct CustomCodeType { 7 | name: String, 8 | } 9 | 10 | impl CustomCodeType { 11 | pub fn new(name: String) -> Self { 12 | CustomCodeType { name } 13 | } 14 | } 15 | 16 | impl CodeType for CustomCodeType { 17 | fn type_label(&self, _ci: &ComponentInterface) -> String { 18 | self.name.clone() 19 | } 20 | 21 | fn canonical_name(&self) -> String { 22 | format!("Type{}", self.name) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/enum_.rs: -------------------------------------------------------------------------------- 1 | use uniffi_bindgen::{backend::Literal, ComponentInterface}; 2 | 3 | use crate::bindings::cpp::{gen_cpp::filters::CppCodeOracle, CodeType}; 4 | #[derive(Debug)] 5 | pub(crate) struct EnumCodeType { 6 | id: String, 7 | } 8 | 9 | impl EnumCodeType { 10 | pub(crate) fn new(id: String) -> Self { 11 | Self { id } 12 | } 13 | } 14 | 15 | impl CodeType for EnumCodeType { 16 | fn type_label(&self, ci: &ComponentInterface) -> String { 17 | if ci.is_name_used_as_error(&self.id) { 18 | format!("std::shared_ptr<{}>", self.canonical_name()) 19 | } else { 20 | CppCodeOracle.class_name(&self.id) 21 | } 22 | } 23 | 24 | fn canonical_name(&self) -> String { 25 | CppCodeOracle.class_name(&self.id) 26 | } 27 | 28 | fn literal(&self, _: &Literal, _ci: &ComponentInterface) -> String { 29 | unreachable!(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/miscellany.rs: -------------------------------------------------------------------------------- 1 | use crate::bindings::cpp::CodeType; 2 | use paste::paste; 3 | use uniffi_bindgen::{backend::Literal, ComponentInterface}; 4 | 5 | macro_rules! impl_code_type_for_miscellany { 6 | ($T:ty, $label:literal, $canonical_name:literal) => { 7 | paste! { 8 | #[derive(Debug)] 9 | pub(crate) struct $T; 10 | 11 | impl CodeType for $T { 12 | fn type_label(&self, _ci: &ComponentInterface) -> String { 13 | format!("{}", $label) 14 | } 15 | 16 | fn canonical_name(&self) -> String { 17 | format!("{}", $canonical_name) 18 | } 19 | 20 | fn literal(&self, _literal: &Literal, _ci: &ComponentInterface) -> String { 21 | unreachable!() 22 | } 23 | } 24 | } 25 | }; 26 | } 27 | 28 | impl_code_type_for_miscellany!( 29 | TimestampCodeType, 30 | "std::chrono::time_point", 31 | "Timestamp" 32 | ); 33 | impl_code_type_for_miscellany!( 34 | DurationCodeType, 35 | "std::chrono::duration", 36 | "Duration" 37 | ); 38 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/object.rs: -------------------------------------------------------------------------------- 1 | use uniffi_bindgen::{backend::Literal, interface::ObjectImpl, ComponentInterface}; 2 | 3 | use crate::bindings::cpp::{ 4 | gen_cpp::filters::callback_interface_name, gen_cpp::filters::CppCodeOracle, CodeType, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub(crate) struct ObjectCodeType { 9 | id: String, 10 | imp: ObjectImpl, 11 | } 12 | 13 | impl ObjectCodeType { 14 | pub(crate) fn new(id: String, imp: ObjectImpl) -> Self { 15 | Self { id, imp } 16 | } 17 | } 18 | 19 | impl CodeType for ObjectCodeType { 20 | fn type_label(&self, _ci: &ComponentInterface) -> String { 21 | format!("std::shared_ptr<{}>", self.canonical_name()) 22 | } 23 | 24 | fn canonical_name(&self) -> String { 25 | CppCodeOracle.class_name(&self.id) 26 | } 27 | 28 | fn literal(&self, _literal: &Literal, _ci: &ComponentInterface) -> String { 29 | unreachable!(); 30 | } 31 | 32 | fn initialization_fn(&self) -> Option { 33 | self.imp.has_callback_interface().then(|| { 34 | format!( 35 | "uniffi::{}::init", 36 | callback_interface_name(&self.canonical_name()).unwrap() 37 | ) 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/primitives.rs: -------------------------------------------------------------------------------- 1 | use crate::bindings::cpp::CodeType; 2 | use paste::paste; 3 | use uniffi_bindgen::{backend::Literal, interface::Radix, ComponentInterface}; 4 | 5 | fn render_literal(literal: &Literal) -> String { 6 | match literal { 7 | Literal::Boolean(v) => { 8 | if *v { 9 | "true".into() 10 | } else { 11 | "false".into() 12 | } 13 | } 14 | Literal::String(s) => format!("\"{s}\""), 15 | Literal::Int(i, radix, _) => match radix { 16 | Radix::Octal => format!("{i:o}"), 17 | Radix::Decimal => format!("{i}"), 18 | Radix::Hexadecimal => format!("{i:#x}"), 19 | }, 20 | Literal::UInt(i, radix, _) => match radix { 21 | Radix::Octal => format!("{i:o}U"), 22 | Radix::Decimal => format!("{i}U"), 23 | Radix::Hexadecimal => format!("{i:#x}U"), 24 | }, 25 | Literal::Float(string, _type_) => string.clone(), 26 | 27 | _ => unreachable!("Literal"), 28 | } 29 | } 30 | 31 | macro_rules! impl_code_type_for_primitive { 32 | ($T:ty, $cpp_name:literal, $canonical_name:literal) => { 33 | paste! { 34 | #[derive(Debug)] 35 | pub(crate) struct $T; 36 | impl CodeType for $T { 37 | fn type_label(&self, _ci: &ComponentInterface) -> String { 38 | $cpp_name.into() 39 | } 40 | 41 | fn canonical_name(&self) -> String { 42 | $canonical_name.into() 43 | } 44 | 45 | fn literal(&self, literal: &Literal, _ci: &ComponentInterface) -> String { 46 | render_literal(&literal) 47 | } 48 | } 49 | } 50 | }; 51 | } 52 | 53 | impl_code_type_for_primitive!(BooleanCodeType, "bool", "Bool"); 54 | impl_code_type_for_primitive!(StringCodeType, "std::string", "String"); 55 | impl_code_type_for_primitive!(BytesCodeType, "std::vector", "Bytes"); 56 | impl_code_type_for_primitive!(Int8CodeType, "int8_t", "Int8"); 57 | impl_code_type_for_primitive!(Int16CodeType, "int16_t", "Int16"); 58 | impl_code_type_for_primitive!(Int32CodeType, "int32_t", "Int32"); 59 | impl_code_type_for_primitive!(Int64CodeType, "int64_t", "Int64"); 60 | impl_code_type_for_primitive!(UInt8CodeType, "uint8_t", "UInt8"); 61 | impl_code_type_for_primitive!(UInt16CodeType, "uint16_t", "UInt16"); 62 | impl_code_type_for_primitive!(UInt32CodeType, "uint32_t", "UInt32"); 63 | impl_code_type_for_primitive!(UInt64CodeType, "uint64_t", "UInt64"); 64 | impl_code_type_for_primitive!(Float32CodeType, "float", "Float"); 65 | impl_code_type_for_primitive!(Float64CodeType, "double", "Double"); 66 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/gen_cpp/record.rs: -------------------------------------------------------------------------------- 1 | use uniffi_bindgen::{backend::Literal, ComponentInterface}; 2 | 3 | use crate::bindings::cpp::{gen_cpp::filters::CppCodeOracle, CodeType}; 4 | 5 | #[derive(Debug)] 6 | pub(crate) struct RecordCodeType { 7 | id: String, 8 | } 9 | 10 | impl RecordCodeType { 11 | pub(crate) fn new(id: String) -> Self { 12 | Self { id } 13 | } 14 | } 15 | 16 | impl CodeType for RecordCodeType { 17 | fn type_label(&self, _ci: &ComponentInterface) -> String { 18 | CppCodeOracle.class_name(&self.id) 19 | } 20 | 21 | fn canonical_name(&self) -> String { 22 | format!("Type{}", self.id) 23 | } 24 | 25 | fn literal(&self, _literal: &Literal, _ci: &ComponentInterface) -> String { 26 | unreachable!(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod gen_cpp; 2 | 3 | use std::{fmt::Debug, fs}; 4 | 5 | use anyhow::Result; 6 | use serde::{Deserialize, Serialize}; 7 | use uniffi_bindgen::{ 8 | backend::Literal, BindingGenerator, Component, ComponentInterface, GenerationSettings, 9 | }; 10 | 11 | use self::gen_cpp::{generate_cpp_bindings, Bindings}; 12 | 13 | pub(crate) struct CppBindingGenerator { 14 | pub scaffolding_mode: bool, 15 | } 16 | 17 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 18 | pub struct ConfigRoot { 19 | #[serde(default)] 20 | bindings: ConfigBindings, 21 | #[serde(default)] 22 | scaffolding: ConfigScaffolding, 23 | } 24 | 25 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 26 | pub struct ConfigBindings { 27 | #[serde(default)] 28 | cpp: gen_cpp::Config, 29 | } 30 | 31 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 32 | pub struct ConfigScaffolding { 33 | #[serde(default)] 34 | cpp: gen_cpp::ScaffoldingConfig, 35 | } 36 | 37 | /// A Trait to help render types in a language specific format. 38 | pub trait CodeType: Debug { 39 | /// The language specific label used to reference this type. This will be used in 40 | /// method signatures and property declarations. 41 | fn type_label(&self, ci: &ComponentInterface) -> String; 42 | 43 | /// A representation of this type label that can be used as part of another 44 | /// identifier. e.g. `read_foo()`, or `FooInternals`. 45 | /// 46 | /// This is especially useful when creating specialized objects or methods to deal 47 | /// with this type only. 48 | fn canonical_name(&self) -> String; 49 | 50 | fn literal(&self, _literal: &Literal, ci: &ComponentInterface) -> String { 51 | unimplemented!("Unimplemented for {}", self.type_label(ci)) 52 | } 53 | 54 | /// Name of the FfiConverter 55 | fn ffi_converter_name(&self) -> String { 56 | format!("FfiConverter{}", self.canonical_name()) 57 | } 58 | 59 | /// A list of imports that are needed if this type is in use. 60 | /// Classes are imported exactly once. 61 | #[allow(dead_code)] 62 | fn imports(&self) -> Option> { 63 | None 64 | } 65 | 66 | /// Function to run at startup 67 | fn initialization_fn(&self) -> Option { 68 | None 69 | } 70 | } 71 | 72 | impl BindingGenerator for CppBindingGenerator { 73 | type Config = gen_cpp::Config; 74 | 75 | fn new_config(&self, root_toml: &toml::Value) -> Result { 76 | Ok(match root_toml.get("bindings").and_then(|b| b.get("cpp")) { 77 | Some(v) => v.clone().try_into()?, 78 | None => Default::default(), 79 | }) 80 | } 81 | 82 | fn update_component_configs( 83 | &self, 84 | _settings: &GenerationSettings, 85 | _components: &mut Vec>, 86 | ) -> Result<()> { 87 | return Ok(()); 88 | } 89 | 90 | fn write_bindings( 91 | &self, 92 | settings: &GenerationSettings, 93 | components: &[uniffi_bindgen::Component], 94 | ) -> Result<()> { 95 | for Component { ci, config, .. } in components { 96 | if ci.has_async_fns() || ci.has_async_callback_interface_definition() { 97 | unimplemented!("Cpp bindgen does not support async functions!"); 98 | } 99 | 100 | if self.scaffolding_mode { 101 | unimplemented!("Cpp scaffolding is not supported yet!"); 102 | } else { 103 | let Bindings { 104 | scaffolding_header, 105 | header, 106 | source, 107 | } = generate_cpp_bindings(&ci, &config)?; 108 | 109 | let scaffolding_header_path = settings 110 | .out_dir 111 | .join(format!("{}_scaffolding.hpp", ci.namespace())); 112 | let header_path = settings.out_dir.join(format!("{}.hpp", ci.namespace())); 113 | let source_path = settings.out_dir.join(format!("{}.cpp", ci.namespace())); 114 | 115 | fs::write(&scaffolding_header_path, scaffolding_header)?; 116 | fs::write(&header_path, header)?; 117 | fs::write(&source_path, source)?; 118 | } 119 | } 120 | 121 | Ok(()) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/arith_conv.cpp: -------------------------------------------------------------------------------- 1 | {{ type_name }} {{ ffi_converter_name }}::lift({{ type_name }} val) { 2 | return val; 3 | } 4 | 5 | {{ type_name }} {{ ffi_converter_name }}::lower({{ type_name }} val) { 6 | return val; 7 | } 8 | 9 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 10 | {{ type_name }} ret; 11 | stream >> ret; 12 | 13 | return ret; 14 | } 15 | 16 | void {{ ffi_converter_name }}::write(RustStream &stream, {{ type_name }} val) { 17 | stream << val; 18 | } 19 | 20 | uint64_t {{ ffi_converter_name }}::allocation_size({{ type_name }}) { 21 | return static_cast(sizeof({{ type_name }})); 22 | } 23 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/arith_conv.hpp: -------------------------------------------------------------------------------- 1 | struct {{ typ|ffi_converter_name }} { 2 | static {{ type_name }} lift({{ type_name }}); 3 | static {{ type_name }} lower({{ type_name }}); 4 | static {{ type_name }} read(RustStream &); 5 | static void write(RustStream &, {{ type_name }}); 6 | static uint64_t allocation_size({{ type_name }}); 7 | }; 8 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/bool_conv.cpp: -------------------------------------------------------------------------------- 1 | {%- let class_name = ffi_converter_name|class_name %} 2 | bool {{ class_name }}::lift(uint8_t val) { 3 | return !!val; 4 | } 5 | 6 | uint8_t {{ class_name }}::lower(bool val) { 7 | return val; 8 | } 9 | 10 | {{ type_name }} {{ class_name }}::read(RustStream &stream) { 11 | uint8_t val; 12 | stream >> val; 13 | 14 | return val; 15 | } 16 | 17 | void {{ class_name }}::write(RustStream &stream, bool val) { 18 | stream << val; 19 | } 20 | 21 | uint64_t {{ class_name }}::allocation_size(bool) { 22 | return 1; 23 | } 24 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/bool_conv.hpp: -------------------------------------------------------------------------------- 1 | struct {{ typ|ffi_converter_name }} { 2 | static {{ type_name }} lift(uint8_t); 3 | static uint8_t lower({{ type_name }}); 4 | static {{ type_name }} read(RustStream &); 5 | static void write(RustStream &, {{ type_name }}); 6 | static uint64_t allocation_size({{ type_name }}); 7 | }; 8 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/bytes_conv.cpp: -------------------------------------------------------------------------------- 1 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 2 | auto stream = RustStream(&buf); 3 | auto ret = read(stream); 4 | 5 | rustbuffer_free(buf); 6 | 7 | return ret; 8 | } 9 | 10 | RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }} &val) { 11 | auto buf = rustbuffer_alloc(allocation_size(val)); 12 | auto stream = RustStream(&buf); 13 | 14 | write(stream, val); 15 | 16 | return buf; 17 | } 18 | 19 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 20 | {{ type_name }} ret; 21 | int32_t count; 22 | stream >> count; 23 | 24 | ret.reserve(count); 25 | 26 | for (decltype(count) i = 0; i < count; i++) { 27 | uint8_t elem; 28 | stream >> elem; 29 | ret.push_back(elem); 30 | } 31 | 32 | return ret; 33 | } 34 | 35 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &val) { 36 | stream << static_cast(val.size()); 37 | 38 | for (auto &elem : val) { 39 | stream << elem; 40 | } 41 | } 42 | 43 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &val) { 44 | return static_cast(sizeof(int32_t) + sizeof(uint8_t) * val.size()); 45 | } 46 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/bytes_conv.hpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | struct {{ ffi_converter_name|class_name }} { 3 | static {{ type_name }} lift(RustBuffer); 4 | static RustBuffer lower(const {{ type_name }} &); 5 | static {{ type_name }} read(RustStream &); 6 | static void write(RustStream &, const {{ type_name }} &); 7 | static uint64_t allocation_size(const {{ type_name }} &); 8 | }; 9 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/callback.hpp: -------------------------------------------------------------------------------- 1 | {%- let class_name = type_name|class_name %} 2 | {%- let canonical_type_name = typ|canonical_name %} 3 | {%- let trait_impl = canonical_type_name|callback_interface_name %} 4 | 5 | {% call macros::docstring_value(interface_docstring, 0) %} 6 | struct {{ interface_name }} { 7 | virtual ~{{ interface_name }}() {} 8 | 9 | {%- for method in methods.iter() %} 10 | {%- call macros::docstring(method, 4) %} 11 | virtual 12 | {% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch %} 13 | {{- method.name()|fn_name }}({% call macros::param_list(method) %}) = 0; 14 | {%- endfor %} 15 | }; 16 | 17 | namespace uniffi { 18 | struct {{ trait_impl }} { 19 | {%- for (ffi_callback, method) in vtable_methods.iter() %} 20 | static {% call macros::ffi_return_type(ffi_callback) %} {{ method.name()|var_name }}({% call macros::arg_list_ffi_decl_xx(ffi_callback) %}); 21 | {%- endfor %} 22 | 23 | static void uniffi_free(uint64_t uniffi_handle); 24 | static void init(); 25 | private: 26 | static inline {{ vtable|ffi_type_name }} vtable = {{ vtable|ffi_type_name}} { 27 | {%- for (ffi_callback, meth) in vtable_methods.iter() %} 28 | .{{ meth.name()|var_name }} = reinterpret_cast(&{{ meth.name()|var_name }}), 29 | {%- endfor %} 30 | .uniffi_free = reinterpret_cast(&uniffi_free) 31 | }; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/callback_conv.cpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | {%- let class_name = ffi_converter_name|class_name %} 3 | 4 | {{ type_name }} {{ class_name }}::lift(uint64_t handle) { 5 | return handle_map.at(handle); 6 | } 7 | 8 | uint64_t {{ class_name }}::lower(const {{type_name}}& impl) { 9 | return handle_map.insert(impl); 10 | } 11 | 12 | {{ type_name }} uniffi::{{ class_name }}::read(RustStream &stream) { 13 | uint64_t handle; 14 | stream >> handle; 15 | 16 | return lift(handle); 17 | } 18 | 19 | void uniffi::{{ class_name }}::write(RustStream &stream, const {{ type_name }} &impl) { 20 | stream << lower(impl); 21 | } 22 | 23 | uint64_t uniffi::{{ class_name }}::allocation_size(const {{ type_name }} &impl) { 24 | return static_cast(sizeof(uint64_t)); 25 | } 26 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/callback_conv.hpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | {%- let canonical_type_name = typ|canonical_name %} 3 | 4 | struct {{ typ|ffi_converter_name }} { 5 | static {{ type_name }} lift(uint64_t); 6 | static uint64_t lower(const {{ type_name }} &); 7 | static {{ type_name }} read(RustStream &); 8 | static void write(RustStream &, const {{ type_name }} &); 9 | static uint64_t allocation_size(const {{ type_name }} &); 10 | 11 | inline static HandleMap<{{ canonical_type_name }}> handle_map = {}; 12 | }; 13 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/callback_iface_tmpl.cpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | {%- let class_name = ffi_converter_name|class_name %} 3 | {%- let canonical_type_name = typ|canonical_name %} 4 | {%- let trait_impl = canonical_type_name|callback_interface_name %} 5 | 6 | {%- for (ffi_callback, method) in vtable_methods.iter() %} 7 | {% call macros::ffi_return_type(ffi_callback) %} {{ trait_impl}}::{{ method.name()|var_name }}({% call macros::arg_list_ffi_decl_xx(ffi_callback) %}) { 8 | auto obj = {{ ffi_converter_name }}::handle_map.at(uniffi_handle); 9 | 10 | auto make_call = [&]() {% match method.return_type() %}{% when Some(t) %}-> {{ t|type_name(ci) }}{% when None %}{% endmatch %} { 11 | {%- for arg in method.arguments() %} 12 | auto arg{{ loop.index0 }} = {{- arg|lift_fn }}({{ arg.name()|var_name }}); 13 | {%- endfor -%} 14 | 15 | {%- if method.return_type().is_some() %}return {% endif -%} 16 | obj->{{ method.name()|var_name }}( 17 | {%- for arg in method.arguments() %} 18 | arg{{ loop.index0 }}{%- if !loop.last %}, {% else %}{% endif %} 19 | {%- endfor -%} 20 | ); 21 | }; 22 | 23 | {% match method.return_type() %} 24 | {% when Some(t) %} 25 | auto write_value = [&]({{ t|type_name(ci) }} v) { 26 | uniffi_out_return = {{ t|lower_fn }}(v); 27 | }; 28 | {% when None %} 29 | auto write_value = [](){}; 30 | {% endmatch %} 31 | 32 | {% match method.throws_type() %} 33 | {% when Some(error) %} 34 | rust_call_trait_interface_with_error<{{ error|canonical_name }}>(out_status, make_call, write_value, {{ error|lower_fn }}); 35 | {% when None %} 36 | rust_call_trait_interface(out_status, make_call, write_value); 37 | {% endmatch %} 38 | } 39 | {%- endfor %} 40 | 41 | void {{ trait_impl }}::uniffi_free(uint64_t uniffi_handle) { 42 | {{ ffi_converter_name }}::handle_map.erase(uniffi_handle); 43 | } 44 | 45 | void {{ trait_impl }}::init() { 46 | {{ ffi_init_callback.name() }}(vtable); 47 | } 48 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/custom.cpp: -------------------------------------------------------------------------------- 1 | {%- match config.custom_types.get(name.as_str()) %} 2 | {%- when Some with (type_config) %} 3 | {%- match type_config.type_name %} 4 | {%- when Some with (type_name) %} 5 | {%- let ffi_type_name = builtin|ffi_type|ffi_type_name %} 6 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buff) { 7 | auto builtin_val = {{ builtin|lift_fn }}(buff); 8 | 9 | return {{ type_config.into_custom.render("builtin_val") }}; 10 | } 11 | 12 | RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }} &val) { 13 | auto builtin_val = {{ type_config.from_custom.render("val") }}; 14 | 15 | return {{ builtin|lower_fn }}(builtin_val); 16 | } 17 | 18 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 19 | auto builtin_val = {{ builtin|read_fn }}(stream); 20 | 21 | return {{ type_config.into_custom.render("builtin_val") }}; 22 | } 23 | 24 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &val) { 25 | auto builtin_val = {{ type_config.from_custom.render("val") }}; 26 | 27 | {{ builtin|write_fn }}(stream, builtin_val); 28 | } 29 | 30 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &val) { 31 | auto builtin_val = {{ type_config.from_custom.render("val") }}; 32 | 33 | return {{ builtin|allocation_size_fn }}(builtin_val); 34 | } 35 | {%- else %} 36 | {%- endmatch %} 37 | {%- else %} 38 | {%- endmatch %} 39 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/custom.hpp: -------------------------------------------------------------------------------- 1 | {%- match config.custom_types.get(name.as_str()) %} 2 | {%- when Some with (type_config) %} 3 | {%- match type_config.type_name %} 4 | {%- when Some with (type_name) %} 5 | struct {{ typ|ffi_converter_name }} { 6 | static {{ name }} lift(RustBuffer); 7 | static RustBuffer lower(const {{ name }} &); 8 | static {{ name }} read(RustStream &); 9 | static void write(RustStream &, const {{ name }} &); 10 | static uint64_t allocation_size(const {{ name }} &); 11 | }; 12 | {%- else %} 13 | {%- endmatch %} 14 | {%- else -%} 15 | typedef struct {{ builtin|ffi_converter_name }} {{ typ|ffi_converter_name }}; 16 | {%- endmatch %} 17 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/duration_conv.hpp: -------------------------------------------------------------------------------- 1 | struct {{ typ|ffi_converter_name }} { 2 | static {{ type_name }} lift(RustBuffer); 3 | static RustBuffer lower(const {{ type_name }} &); 4 | static {{ type_name }} read(RustStream &); 5 | static void write(RustStream &, const {{ type_name }} &); 6 | static uint64_t allocation_size(const {{ type_name }} &); 7 | }; 8 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/duration_helper.cpp: -------------------------------------------------------------------------------- 1 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 2 | auto stream = RustStream(&buf); 3 | auto val = {{ ffi_converter_name }}::read(stream); 4 | 5 | rustbuffer_free(buf); 6 | 7 | return val; 8 | } 9 | 10 | RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }} &val) { 11 | auto buf = rustbuffer_alloc(allocation_size(val)); 12 | auto stream = RustStream(&buf); 13 | 14 | {{ ffi_converter_name }}::write(stream, val); 15 | 16 | return std::move(buf); 17 | } 18 | 19 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 20 | uint64_t secs; 21 | uint32_t nanos; 22 | 23 | stream >> secs; 24 | stream >> nanos; 25 | 26 | return std::chrono::seconds(secs) + std::chrono::nanoseconds(nanos); 27 | } 28 | 29 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &val) { 30 | auto secs = std::chrono::duration_cast>(val); 31 | auto nanos = (val - secs).count(); 32 | 33 | stream << secs.count() << static_cast(nanos); 34 | } 35 | 36 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &) { 37 | return static_cast(sizeof(uint64_t) + sizeof(uint32_t)); 38 | } 39 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/enum.hpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name|class_name %} 2 | {%- if e.is_flat() %} 3 | {% call macros::docstring(e, 0) %} 4 | enum class {{ type_name }}: int32_t { 5 | {%- for variant in e.variants() %} 6 | {%- call macros::docstring(variant, 4) %} 7 | {{ variant|variant_name(config.enum_style) }} = {{ loop.index }} 8 | {%- if !loop.last %}, 9 | {%- endif %} 10 | {%- endfor %} 11 | }; 12 | {%- else %} 13 | namespace uniffi { 14 | struct {{ ffi_converter_name }}; 15 | } // namespace uniffi 16 | 17 | {%~ call macros::docstring(e, 0) %} 18 | struct {{ type_name }} { 19 | friend uniffi::{{ ffi_converter_name }}; 20 | 21 | {%- for variant in e.variants() %} 22 | {%- call macros::docstring(variant, 4) %} 23 | struct {{ variant|variant_name(config.enum_style) }} { 24 | {%- for field in variant.fields() %} 25 | {%- call macros::docstring(field, 8) %} 26 | {{ field|type_name(ci) }} {{ field.name()|var_name }} 27 | {%- match field.default_value() %} 28 | {%- when Some with (literal) %} = {{ literal|literal_cpp(field, config.enum_style, ci) }};{%- else -%}; 29 | {%- endmatch %} 30 | {%- endfor %} 31 | }; 32 | {%- endfor %} 33 | 34 | {%- for variant in e.variants() %} 35 | {{ type_name }}({{ variant|variant_name(config.enum_style) }} variant): variant(variant) {} 36 | {%- endfor %} 37 | 38 | {{ type_name }}(const {{ type_name }} &other): variant(other.variant) {} 39 | {{ type_name }}({{ type_name }} &&other): variant(std::move(other.variant)) {} 40 | 41 | {{ type_name }} &operator=(const {{ type_name }} &other) { 42 | variant = other.variant; 43 | return *this; 44 | } 45 | 46 | {{ type_name }} &operator=({{ type_name }} &&other) { 47 | variant = std::move(other.variant); 48 | return *this; 49 | } 50 | 51 | /** 52 | * Returns the variant of this enum 53 | */ 54 | const std::variant<{% for variant in e.variants() %}{{ variant|variant_name(config.enum_style) }}{% if !loop.last %}, {% endif %}{% endfor %}> &get_variant() const { 55 | return variant; 56 | } 57 | 58 | private: 59 | std::variant<{% for variant in e.variants() %}{{ variant|variant_name(config.enum_style) }}{% if !loop.last %}, {% endif %}{% endfor %}> variant; 60 | 61 | {{ type_name }}(); 62 | }; 63 | {%- endif %} 64 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/enum_conv.hpp: -------------------------------------------------------------------------------- 1 | struct {{ typ|ffi_converter_name }} { 2 | static {{ type_name }} lift(RustBuffer); 3 | static RustBuffer lower(const {{ type_name }} &); 4 | static {{ type_name }} read(RustStream &); 5 | static void write(RustStream &, const {{ type_name }} &); 6 | static uint64_t allocation_size(const {{ type_name }} &); 7 | }; 8 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/enum_tmpl.cpp: -------------------------------------------------------------------------------- 1 | {%- if e.is_flat() %} 2 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 3 | auto stream = RustStream(&buf); 4 | auto ret = {{ ffi_converter_name }}::read(stream); 5 | 6 | rustbuffer_free(buf); 7 | 8 | return std::move(ret); 9 | } 10 | 11 | RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }} &val) { 12 | auto buf = rustbuffer_alloc({{ ffi_converter_name }}::allocation_size(val)); 13 | auto stream = RustStream(&buf); 14 | 15 | {{ ffi_converter_name }}::write(stream, val); 16 | 17 | return std::move(buf); 18 | } 19 | 20 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 21 | int32_t variant; 22 | stream >> variant; 23 | 24 | switch (variant) { 25 | {% for variant in e.variants() %} 26 | case {{ loop.index }}: 27 | return {{ type_name }}::{{ variant|variant_name(config.enum_style) }}; 28 | {% endfor %} 29 | default: 30 | throw std::runtime_error("No matching {{ type_name }} variant"); 31 | } 32 | } 33 | 34 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &val) { 35 | switch (val) { 36 | {% for variant in e.variants() %} 37 | case {{ type_name }}::{{ variant|variant_name(config.enum_style) }}: 38 | stream << static_cast({{ loop.index }}); 39 | break; 40 | {% endfor %} 41 | default: 42 | throw std::runtime_error("No matching {{ type_name }} variant"); 43 | } 44 | } 45 | 46 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name|class_name }} &) { 47 | return static_cast(sizeof(int32_t)); 48 | } 49 | {%- else %} 50 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 51 | auto stream = RustStream(&buf); 52 | auto ret = {{ ffi_converter_name }}::read(stream); 53 | 54 | rustbuffer_free(buf); 55 | 56 | return std::move(ret); 57 | } 58 | 59 | RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }} &val) { 60 | auto buf = rustbuffer_alloc({{ ffi_converter_name }}::allocation_size(val)); 61 | auto stream = RustStream(&buf); 62 | 63 | {{ ffi_converter_name }}::write(stream, val); 64 | 65 | return std::move(buf); 66 | } 67 | 68 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 69 | int32_t variant_id; 70 | stream >> variant_id; 71 | 72 | switch (variant_id) { 73 | {% for variant in e.variants() %} 74 | case {{ loop.index }}: 75 | return {{ type_name }}::{{ variant|variant_name(config.enum_style) }} { 76 | {%- for field in variant.fields() %} 77 | .{{field.name()|var_name}} = {{ field|read_fn }}(stream), 78 | {%- endfor %} 79 | }; 80 | {% endfor %} 81 | default: 82 | throw std::runtime_error("No matching {{ type_name }} variant"); 83 | } 84 | } 85 | 86 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &val) { 87 | int32_t variant_id = static_cast(val.variant.index() + 1); 88 | 89 | stream << variant_id; 90 | 91 | std::visit([&](auto &&arg) { 92 | using T = std::decay_t; 93 | {%- for variant in e.variants() %} 94 | {% if !loop.first %}else {% endif %}if constexpr (std::is_same_v) { 95 | {%- for field in variant.fields() %} 96 | {{ field|write_fn }}(stream, {{ field.as_type()|deref(ci) }}arg.{{ field.name()|var_name }}); 97 | {%- endfor %} 98 | } 99 | {%- endfor %} 100 | {%- if e.variants().len() != 0 %} 101 | else { 102 | static_assert(always_false_v, "non-exhaustive {{ type_name }} visitor"); 103 | } 104 | {%- endif %} 105 | }, val.variant); 106 | } 107 | 108 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name|class_name }} &val) { 109 | uint64_t size = sizeof(int32_t); 110 | 111 | size += std::visit([&](auto &&arg) { 112 | using T = std::decay_t; 113 | {%- for variant in e.variants() %} 114 | {% if !loop.first %}else {% endif %}if constexpr (std::is_same_v) { 115 | uint64_t size = 0; 116 | {%- for field in variant.fields() %} 117 | size += {{ field|allocation_size_fn }}({{ field.as_type()|deref(ci) }}arg.{{ field.name()|var_name }}); 118 | {%- endfor %} 119 | return size; 120 | } 121 | {%- endfor %} 122 | {%- if e.variants().len() != 0 %} 123 | else { 124 | static_assert(always_false_v, "non-exhaustive {{ type_name }} visitor"); 125 | } 126 | {%- endif %} 127 | }, val.variant); 128 | 129 | return size; 130 | } 131 | {%- endif %} 132 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/err.hpp: -------------------------------------------------------------------------------- 1 | {%- let class_name = typ|canonical_name %} 2 | {%- let ffi_converter_name = typ|ffi_converter_name|class_name %} 3 | namespace uniffi { 4 | struct {{ ffi_converter_name }}; 5 | } // namespace uniffi 6 | 7 | {%~ call macros::docstring(e, 0) %} 8 | struct {{ class_name }}: std::runtime_error { 9 | friend uniffi::{{ ffi_converter_name }}; 10 | 11 | {{ class_name }}() : std::runtime_error("") {} 12 | {{ class_name }}(const std::string &what_arg) : std::runtime_error(what_arg) {} 13 | 14 | virtual ~{{ class_name }}() = default; 15 | 16 | virtual void throw_underlying() { 17 | throw *this; 18 | } 19 | 20 | protected: 21 | virtual int32_t get_variant_idx() const { 22 | return 0; 23 | }; 24 | }; 25 | 26 | {%- if e.variants().len() != 0 %} 27 | /** 28 | * Contains variants of {{ class_name }} 29 | */ 30 | namespace {{ class_name|to_lower_snake_case }} { 31 | {%- for variant in e.variants() %} 32 | {% call macros::docstring(variant, 0) %} 33 | struct {{ variant.name()|class_name }}: {{ class_name }} { 34 | {%- for field in variant.fields() %} 35 | {{ field|type_name(ci) }} {% call macros::field_name(field, loop.index) %} 36 | {%- match field.default_value() %} 37 | {% when Some with (literal) %} = {{ literal|literal_cpp(field, config.enum_style, ci) }};{% else %}; 38 | {%- endmatch %} 39 | {%- endfor %} 40 | 41 | {{ variant.name()|class_name }}() : {{ class_name }}("") {} 42 | {{ variant.name()|class_name }}(const std::string &what_arg) : {{ class_name }}(what_arg) {} 43 | 44 | void throw_underlying() override { 45 | throw *this; 46 | } 47 | 48 | protected: 49 | int32_t get_variant_idx() const override { 50 | return {{ loop.index }}; 51 | } 52 | }; 53 | {%- endfor %} 54 | } // namespace {{ class_name|to_lower_snake_case }} 55 | {%- endif %} 56 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/err_conv.hpp: -------------------------------------------------------------------------------- 1 | {%- let class_name = typ|canonical_name %} 2 | struct {{ typ|ffi_converter_name }} { 3 | static {{ type_name }} lift(RustBuffer buf); 4 | static RustBuffer lower(const {{ class_name }} &); 5 | static {{ type_name }} read(RustStream &stream); 6 | static void write(RustStream &stream, const {{ class_name }} &); 7 | static uint64_t allocation_size(const {{ class_name }} &); 8 | }; 9 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/err_tmpl.cpp: -------------------------------------------------------------------------------- 1 | {%- let class_name = typ|canonical_name %} 2 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 3 | auto stream = RustStream(&buf); 4 | auto ret = {{ ffi_converter_name }}::read(stream); 5 | 6 | rustbuffer_free(buf); 7 | 8 | return ret; 9 | } 10 | 11 | RustBuffer {{ ffi_converter_name }}::lower(const {{ class_name }} &val) { 12 | auto buf = rustbuffer_alloc(allocation_size(val)); 13 | auto stream = RustStream(&buf); 14 | 15 | {{ ffi_converter_name }}::write(stream, val); 16 | 17 | return std::move(buf); 18 | } 19 | 20 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 21 | int32_t v; 22 | stream >> v; 23 | 24 | switch (v) { 25 | {%- for variant in e.variants() %} 26 | {%- if e.is_flat() %} 27 | case {{ loop.index }}: 28 | return std::make_shared<{{ class_name|to_lower_snake_case }}::{{ variant.name()|class_name }}>({{ Type::String.borrow()|read_fn }}(stream)); 29 | {% else %} 30 | case {{ loop.index }}: 31 | { 32 | {{ class_name|to_lower_snake_case }}::{{ variant.name() }} var; 33 | {%- for field in variant.fields() %} 34 | var.{% call macros::field_name(field, loop.index) %} = {{ field|read_fn }}(stream); 35 | {%- endfor %} 36 | return std::make_shared<{{ class_name|to_lower_snake_case }}::{{ variant.name() }}>(var); 37 | } 38 | {%- endif %} 39 | {%- endfor %} 40 | default: 41 | throw std::runtime_error("Unexpected error variant"); 42 | } 43 | } 44 | 45 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ class_name }} &val) { 46 | stream << val.get_variant_idx(); 47 | 48 | {%- if e.is_flat() %} 49 | {{ Type::String.borrow()|write_fn }}(stream, val.what()); 50 | {%- else %} 51 | switch (val.get_variant_idx()) { 52 | {%- for variant in e.variants() %} 53 | case {{ loop.index }}: 54 | { 55 | auto var = static_cast(val); 56 | {%- for field in variant.fields() %} 57 | {{ field|write_fn }}(stream, {{ field.as_type()|deref(ci) }}var.{% call macros::field_name(field, loop.index) %}); 58 | {%- endfor %} 59 | break; 60 | } 61 | {%- endfor %} 62 | } 63 | {%- endif %} 64 | } 65 | 66 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ class_name }} &val) { 67 | {%- if e.is_flat() %} 68 | return static_cast(sizeof(int32_t)); 69 | {%- else %} 70 | switch (val.get_variant_idx()) { 71 | {%- for variant in e.variants() %} 72 | case {{ loop.index }}: 73 | { 74 | auto var = static_cast(val); 75 | return static_cast(sizeof(int32_t) 76 | {%- for field in variant.fields() %} 77 | + {{ field|allocation_size_fn }}({{ field.as_type()|deref(ci) }}var.{% call macros::field_name(field, loop.index) %}) 78 | {%- endfor %}); 79 | } 80 | {%- endfor %} 81 | default: 82 | throw std::runtime_error("Unexpected error variant"); 83 | } 84 | {%- endif %} 85 | } 86 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/ext_typ_tmpl.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NordSecurity/uniffi-bindgen-cpp/6a933082dda03de66d3330c865ff91f55dd2204a/bindgen/src/bindings/cpp/templates/ext_typ_tmpl.cpp -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/fn_def.cpp: -------------------------------------------------------------------------------- 1 | {%- match func.return_type() %} 2 | {%- when Some with (return_type) %} 3 | {{ return_type|type_name(ci) }} {{ func.name()|fn_name }}({% call macros::param_list(func) %}) { 4 | auto ret = {% call macros::rust_call(func) %}; 5 | 6 | return uniffi::{{ return_type|lift_fn }}(ret); 7 | } 8 | {%- when None -%} 9 | void {{ func.name()|fn_name }}({% call macros::param_list(func) %}) { 10 | {% call macros::rust_call(func) %}; 11 | } 12 | {%- endmatch -%} 13 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/handle_map.cpp: -------------------------------------------------------------------------------- 1 | 2 | template struct HandleMap { 3 | HandleMap() = default; 4 | 5 | std::shared_ptr at(uint64_t handle) { 6 | std::lock_guard guard(this->mutex); 7 | 8 | return this->map.at(handle); 9 | } 10 | 11 | uint64_t insert(std::shared_ptr impl) { 12 | std::lock_guard guard(this->mutex); 13 | 14 | auto handle = this->cur_handle; 15 | 16 | this->map.insert({ handle, impl }); 17 | this->cur_handle += 1; 18 | 19 | return handle; 20 | } 21 | 22 | void erase(uint64_t handle) { 23 | // We store the object here to avoid re-entrant locking 24 | std::shared_ptr cleanup; 25 | { 26 | std::lock_guard guard(this->mutex); 27 | auto it = this->map.find(handle); 28 | if (it != this->map.end()) { 29 | cleanup = it->second; 30 | this->map.erase(it); 31 | } 32 | } 33 | } 34 | private: 35 | HandleMap(const HandleMap &) = delete; 36 | HandleMap(HandleMap &&) = delete; 37 | 38 | HandleMap &operator=(const HandleMap &) = delete; 39 | HandleMap &operator=(HandleMap &&) = delete; 40 | 41 | std::mutex mutex; 42 | uint64_t cur_handle = 0; 43 | std::map> map; 44 | }; 45 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/internal_types.cpp: -------------------------------------------------------------------------------- 1 | {%- import "macros.cpp" as macros %} 2 | 3 | {%- for typ in ci.iter_types() %} 4 | {%- let type_name = typ|type_name(ci) %} 5 | {%- let ffi_converter_name = typ|ffi_converter_name %} 6 | {%- let canonical_type_name = typ|canonical_name %} 7 | {%- let contains_object_references = ci.item_contains_object_references(typ) %} 8 | {%- let namespace = ci.namespace() %} 9 | 10 | {%- match typ %} 11 | 12 | {%- when Type::Boolean %} 13 | {% include "bool_conv.cpp" %} 14 | {%- when Type::UInt8 %} 15 | {% include "arith_conv.cpp" %} 16 | {%- when Type::Int8 %} 17 | {% include "arith_conv.cpp" %} 18 | {%- when Type::UInt16 %} 19 | {% include "arith_conv.cpp" %} 20 | {%- when Type::Int16 %} 21 | {% include "arith_conv.cpp" %} 22 | {%- when Type::UInt32 %} 23 | {% include "arith_conv.cpp" %} 24 | {%- when Type::Int32 %} 25 | {% include "arith_conv.cpp" %} 26 | {%- when Type::UInt64 %} 27 | {% include "arith_conv.cpp" %} 28 | {%- when Type::Int64 %} 29 | {% include "arith_conv.cpp" %} 30 | {%- when Type::Float32 %} 31 | {% include "arith_conv.cpp" %} 32 | {%- when Type::Float64 %} 33 | {% include "arith_conv.cpp" %} 34 | {%- when Type::Bytes %} 35 | {% include "bytes_conv.cpp" %} 36 | {%- when Type::String %} 37 | {% include "str_conv.cpp" %} 38 | {%- else %} 39 | {%- endmatch %} 40 | {%- endfor %} 41 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/macros.cpp: -------------------------------------------------------------------------------- 1 | {% macro rust_call(func) -%} 2 | uniffi::rust_call( 3 | {{ func.ffi_func().name() }}, 4 | {%- match func.throws_type() %} 5 | {% when Some with (e) %} 6 | uniffi::{{ e|ffi_error_converter_name }}::lift 7 | {%- else %} 8 | nullptr 9 | {%- endmatch %} 10 | {%- if !func.arguments().is_empty() %}, {% else %}{% endif %} 11 | {%- call arg_list_lowered(func) -%}) 12 | {%- endmacro %} 13 | 14 | {% macro rust_call_with_prefix(prefix, func) -%} 15 | uniffi::rust_call( 16 | {{ func.ffi_func().name() }}, 17 | {%- match func.throws_type() %} 18 | {% when Some with (e) %} 19 | uniffi::{{ e|ffi_error_converter_name }}::lift, 20 | {%- else %} 21 | nullptr, 22 | {%- endmatch %} 23 | {{ prefix }} 24 | {%- if !func.arguments().is_empty() %}, {% else %}{% endif %} 25 | {%- call arg_list_lowered(func) -%}) 26 | {%- endmacro %} 27 | 28 | {% macro param_list(func) %} 29 | {%- for arg in func.arguments() -%} 30 | {{ arg|parameter(ci) }} 31 | {%- if !loop.last -%}, {% endif -%} 32 | {% endfor -%} 33 | {% endmacro %} 34 | 35 | {% macro field_name(field, field_num) %} 36 | {%- if field.name().is_empty() -%} 37 | v{{- field_num -}} 38 | {%- else -%} 39 | {{ field.name()|var_name }} 40 | {%- endif -%} 41 | {%- endmacro %} 42 | 43 | {% macro arg_list_lowered(func) %} 44 | {%- for arg in func.arguments() -%} 45 | uniffi::{{ arg|lower_fn }}({{ arg.name()|var_name }}) 46 | {%- if !loop.last -%}, {% endif -%} 47 | {% endfor -%} 48 | {% endmacro %} 49 | 50 | {%- macro arg_list_ffi_decl_xx(func) %} 51 | {%- for arg in func.arguments() %} 52 | {{- arg.type_().borrow()|ffi_type_name }} {{ arg.name()|var_name -}}{%- if !loop.last || func.has_rust_call_status_arg() -%},{%- endif -%} 53 | {%- endfor %} 54 | {%- if func.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif %} 55 | {%- endmacro -%} 56 | 57 | {%- macro ffi_return_type(func) %} 58 | {%- match func.return_type() %} 59 | {%- when Some(return_type) %}{{ return_type|ffi_type_name }} 60 | {%- when None %}{{ "void" }} 61 | {%- endmatch %} 62 | {%- endmacro %} 63 | 64 | {%- macro docstring_value(maybe_docstring, indent_spaces) %} 65 | {%- match maybe_docstring %} 66 | {%- when Some(docstring) %} 67 | {{ docstring|docstring(indent_spaces) }} 68 | {%- else %} 69 | {%- endmatch %} 70 | {%- endmacro %} 71 | 72 | {%- macro docstring(defn, indent_spaces) %} 73 | {%- call docstring_value(defn.docstring(), indent_spaces) %} 74 | {%- endmacro %} 75 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/map_conv.hpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | struct {{ ffi_converter_name|class_name }} { 3 | static {{ type_name }} lift(RustBuffer); 4 | static RustBuffer lower(const {{ type_name }} &); 5 | static {{ type_name }} read(RustStream &); 6 | static void write(RustStream &, const {{ type_name }} &); 7 | static uint64_t allocation_size(const {{ type_name }} &); 8 | }; 9 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/map_tmpl.cpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | {%- let class_name = ffi_converter_name|class_name %} 3 | {{ type_name }} {{ class_name }}::lift(RustBuffer buf) { 4 | auto stream = RustStream(&buf); 5 | auto ret = read(stream); 6 | 7 | rustbuffer_free(buf); 8 | 9 | return ret; 10 | } 11 | 12 | RustBuffer {{ class_name }}::lower(const {{ type_name }} &val) { 13 | auto buf = rustbuffer_alloc(allocation_size(val)); 14 | auto stream = RustStream(&buf); 15 | 16 | write(stream, val); 17 | 18 | return buf; 19 | } 20 | 21 | {{ type_name }} {{ class_name }}::read(RustStream &stream) { 22 | {{ type_name }} ret; 23 | int32_t count; 24 | stream >> count; 25 | 26 | ret.reserve(count); 27 | 28 | for (decltype(count) i = 0; i < count; i++) { 29 | ret.insert({ {{ key_type|read_fn }}(stream), {{ value_type|read_fn }}(stream) }); 30 | } 31 | 32 | return ret; 33 | } 34 | 35 | void {{ class_name }}::write(RustStream &stream, const {{ type_name }} &val) { 36 | stream << static_cast(val.size()); 37 | 38 | for (auto &entry : val) { 39 | {{ key_type|write_fn }}(stream, {{ key_type.as_type()|deref(ci) }}entry.first); 40 | {{ value_type|write_fn }}(stream, {{ value_type.as_type()|deref(ci) }}entry.second); 41 | } 42 | } 43 | 44 | uint64_t {{ class_name }}::allocation_size(const {{ type_name }} &val) { 45 | uint64_t size = sizeof(int32_t); 46 | 47 | for (auto &entry : val) { 48 | size += {{ key_type|allocation_size_fn }}({{ key_type.as_type()|deref(ci) }}entry.first); 49 | size += {{ value_type|allocation_size_fn }}({{ value_type.as_type()|deref(ci) }}entry.second); 50 | } 51 | 52 | return size; 53 | } 54 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/obj.cpp: -------------------------------------------------------------------------------- 1 | {%- let obj = ci|get_object_definition(name) %} 2 | {%- let (interface_name, impl_class_name) = obj|object_names %} 3 | {%- let class_name = type_name|class_name %} 4 | {%- let ffi_converter_name = typ|ffi_converter_name %} 5 | {%- let canonical_type_name = typ|canonical_name %} 6 | 7 | {%- if obj.has_callback_interface() %} 8 | {%- let vtable = obj.vtable().expect("trait interface should have a vtable") %} 9 | {%- let vtable_methods = obj.vtable_methods() %} 10 | {%- let ffi_init_callback = obj.ffi_init_callback() %} 11 | namespace uniffi { 12 | {% include "callback_iface_tmpl.cpp" %} 13 | } // namespace uniffi 14 | {%- endif %} 15 | 16 | 17 | {{ impl_class_name }}::{{ impl_class_name }}(void *ptr): instance(ptr) {} 18 | 19 | {{ impl_class_name }}::{{ impl_class_name }}(const {{ impl_class_name }} &other) : instance(nullptr) { 20 | if (other.instance) { 21 | instance = other._uniffi_internal_clone_pointer(); 22 | } 23 | } 24 | 25 | {% if ci.is_name_used_as_error(name) %} 26 | void {{ impl_class_name }}::throw_underlying() { 27 | throw *this; 28 | } 29 | {% endif %} 30 | 31 | {% match obj.primary_constructor() -%} 32 | {%- when Some with (ctor) %} 33 | {{ type_name }} {{ impl_class_name }}::init({% call macros::param_list(ctor) %}) { 34 | return {{ type_name }}( 35 | new {{ impl_class_name }}({%- call macros::rust_call(ctor) -%}) 36 | ); 37 | } 38 | {% else -%} 39 | {% endmatch -%} 40 | 41 | {% for ctor in obj.alternate_constructors() %} 42 | {{ type_name }} {{ impl_class_name }}::{{ ctor.name() }}({% call macros::param_list(ctor) %}) { 43 | return {{ type_name }}(new {{ impl_class_name }}({% call macros::rust_call(ctor) %})); 44 | } 45 | {% endfor %} 46 | 47 | {%- for method in obj.methods() %} 48 | {% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch -%} 49 | {{ impl_class_name }}::{{ method.name()|fn_name }}({% call macros::param_list(method) %}) { 50 | auto ptr = this->_uniffi_internal_clone_pointer(); 51 | {%- match method.return_type() %} 52 | {% when Some with (return_type) %} 53 | return uniffi::{{ return_type|lift_fn }}({% call macros::rust_call_with_prefix("ptr", method) %}); 54 | {%- else %} 55 | {% call macros::rust_call_with_prefix("ptr", method) -%}; 56 | {%- endmatch %} 57 | } 58 | {%- endfor %} 59 | 60 | {{ impl_class_name }}::~{{ impl_class_name }}() { 61 | uniffi::rust_call( 62 | {{ obj.ffi_object_free().name() }}, 63 | nullptr, 64 | this->instance 65 | ); 66 | } 67 | 68 | void *{{ impl_class_name }}::_uniffi_internal_clone_pointer() const { 69 | return uniffi::rust_call( 70 | {{ obj.ffi_object_clone().name() }}, 71 | nullptr, 72 | this->instance 73 | ); 74 | } 75 | 76 | {%- for method in obj.uniffi_traits() %} 77 | {% match method %} 78 | {% when UniffiTrait::Display { fmt } %} 79 | std::string {{ impl_class_name }}::to_string() const { 80 | return uniffi::{{ Type::String.borrow()|lift_fn }}({% call macros::rust_call_with_prefix("this->_uniffi_internal_clone_pointer()", fmt) %}); 81 | } 82 | {% when UniffiTrait::Debug { fmt } %} 83 | std::string {{ impl_class_name }}::to_debug_string() const { 84 | return uniffi::{{ Type::String.borrow()|lift_fn }}({% call macros::rust_call_with_prefix("this->_uniffi_internal_clone_pointer()", fmt) %}); 85 | } 86 | {% when UniffiTrait::Eq { eq, ne } %} 87 | bool {{ impl_class_name }}::eq(const {{ type_name }} &other) const { 88 | return uniffi::{{ Type::Boolean.borrow()|lift_fn }}({% call macros::rust_call_with_prefix("this->_uniffi_internal_clone_pointer()", eq) %}); 89 | } 90 | bool {{ impl_class_name }}::ne(const {{ type_name }} &other) const { 91 | return uniffi::{{ Type::Boolean.borrow()|lift_fn }}({% call macros::rust_call_with_prefix("this->_uniffi_internal_clone_pointer()", ne) %}); 92 | } 93 | {% when UniffiTrait::Hash { hash } %} 94 | uint64_t {{ impl_class_name }}::hash() const { 95 | return uniffi::{{ Type::UInt64.borrow()|lift_fn }}({% call macros::rust_call_with_prefix("this->_uniffi_internal_clone_pointer()", hash) %}); 96 | } 97 | {% endmatch %} 98 | {%- endfor %} 99 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/obj.hpp: -------------------------------------------------------------------------------- 1 | {%- let obj = ci|get_object_definition(name) %} 2 | {%- let (interface_name, impl_class_name) = obj|object_names %} 3 | {%- let class_name = type_name|class_name %} 4 | {%- let ffi_converter_name = typ|ffi_converter_name %} 5 | {%- let canonical_type_name = typ|canonical_name %} 6 | {%- if obj.has_callback_interface() %} 7 | {%- let vtable = obj.vtable().expect("trait interface should have a vtable") %} 8 | {%- let vtable_methods = obj.vtable_methods() %} 9 | {%- let methods = obj.methods() %} 10 | {%- let ffi_init_callback = obj.ffi_init_callback() %} 11 | {%- let interface_docstring = obj.docstring() %} 12 | {% include "callback.hpp" %} 13 | {%- endif %} 14 | 15 | namespace uniffi { 16 | struct {{ ffi_converter_name|class_name }}; 17 | } // namespace uniffi 18 | 19 | {%~ call macros::docstring(obj, 0) %} 20 | struct {{ impl_class_name }} 21 | {# 22 | Since an interface being a callback interface or an error is mutually exclusive, 23 | we don't need to complex branching for multiple inheritance 24 | #} 25 | {% if obj.has_callback_interface() %} : public {{ interface_name }} {% endif %} 26 | {% if ci.is_name_used_as_error(name) %} : public std::exception {% endif %} 27 | { 28 | friend uniffi::{{ ffi_converter_name|class_name }}; 29 | 30 | {{ impl_class_name }}() = delete; 31 | 32 | {{ impl_class_name }}({{ impl_class_name }} &&) = delete; 33 | 34 | {{ impl_class_name }} &operator=(const {{ impl_class_name }} &) = delete; 35 | {{ impl_class_name }} &operator=({{ impl_class_name }} &&) = delete; 36 | 37 | ~{{ impl_class_name }}(); 38 | 39 | {%- match obj.primary_constructor() %} 40 | {%- when Some with (ctor) %} 41 | {%- call macros::docstring(ctor, 4) %} 42 | static {{ type_name }} init({% call macros::param_list(ctor) %}); 43 | {%- else %} 44 | {%- endmatch %} 45 | 46 | {%- for ctor in obj.alternate_constructors() %} 47 | {%- call macros::docstring(ctor, 4) %} 48 | static {{ type_name }} {{ ctor.name() }}({% call macros::param_list(ctor) %}); 49 | {%- endfor %} 50 | 51 | {%- for method in obj.methods() %} 52 | {%- call macros::docstring(method, 4) %} 53 | {% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch %} 54 | {{- method.name()|fn_name }}({% call macros::param_list(method) %}); 55 | {%- endfor %} 56 | 57 | {%- for method in obj.uniffi_traits() %} 58 | {%- match method %} 59 | {%- when UniffiTrait::Display { fmt } %} 60 | /** 61 | * Returns a string representation of the object, internally calls Rust's `Display` trait. 62 | */ 63 | std::string to_string() const; 64 | {%- when UniffiTrait::Debug { fmt } %} 65 | /** 66 | * Returns a string representation of the object, internally calls Rust's `Debug` trait. 67 | */ 68 | std::string to_debug_string() const; 69 | {%- when UniffiTrait::Eq { eq, ne } %} 70 | /** 71 | * Equality check, internally calls Rust's `Eq` trait. 72 | */ 73 | bool eq(const {{ type_name }} &other) const; 74 | /** 75 | * Inequality check, internally calls Rust's `Ne` trait. 76 | */ 77 | bool ne(const {{ type_name }} &other) const; 78 | {%- when UniffiTrait::Hash { hash } %} 79 | /** 80 | * Returns a hash of the object, internally calls Rust's `Hash` trait. 81 | */ 82 | uint64_t hash() const; 83 | {%- endmatch %} 84 | {%- endfor %} 85 | 86 | {% if ci.is_name_used_as_error(name) %} 87 | void throw_underlying(); 88 | {%- endif -%} 89 | private: 90 | {{ impl_class_name }}(const {{ impl_class_name }} &); 91 | 92 | {{ impl_class_name }}(void *); 93 | 94 | void *_uniffi_internal_clone_pointer() const; 95 | 96 | void *instance = nullptr; 97 | }; 98 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/obj_conv.cpp: -------------------------------------------------------------------------------- 1 | {%- let obj = ci|get_object_definition(name) %} 2 | {%- let (interface_name, impl_class_name) = obj|object_names %} 3 | 4 | {%- let is_error = ci.is_name_used_as_error(name) %} 5 | {%- if is_error %} 6 | {{ type_name }} {{ typ|ffi_error_converter_name}}::lift(RustBuffer buf) { 7 | auto stream = RustStream(&buf); 8 | auto val = {{ ffi_converter_name }}::read(stream); 9 | rustbuffer_free(buf); 10 | 11 | return val; 12 | } 13 | {% endif %} 14 | 15 | {{ type_name }} {{ ffi_converter_name }}::lift(void *ptr) { 16 | return {{ type_name }}(new {{ impl_class_name }}(ptr)); 17 | } 18 | 19 | void *{{ ffi_converter_name }}::lower(const {{ type_name }} &obj) { 20 | {%- if obj.has_callback_interface() %} 21 | auto ptr = handle_map.insert(obj); 22 | return reinterpret_cast(ptr); 23 | {%- else %} 24 | return reinterpret_cast<{{ impl_class_name}}*>(obj.get())->_uniffi_internal_clone_pointer(); 25 | {%- endif %} 26 | } 27 | 28 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 29 | std::uintptr_t ptr; 30 | stream >> ptr; 31 | 32 | return {{ ffi_converter_name}}::lift(reinterpret_cast(ptr)); 33 | } 34 | 35 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &obj) { 36 | stream << reinterpret_cast({{ ffi_converter_name }}::lower(obj)); 37 | } 38 | 39 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &) { 40 | return 8; 41 | } 42 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/obj_conv.hpp: -------------------------------------------------------------------------------- 1 | {%- let obj = ci|get_object_definition(name) %} 2 | {%- let canonical_type_name = typ|canonical_name %} 3 | {%- let trait_impl = canonical_type_name|callback_interface_name %} 4 | 5 | {%- let is_error = ci.is_name_used_as_error(name) %} 6 | {%- if is_error %} 7 | struct {{ typ|ffi_error_converter_name}} { 8 | static {{ type_name }} lift(RustBuffer); 9 | }; 10 | {% endif %} 11 | 12 | struct {{ typ|ffi_converter_name }} { 13 | static {{ type_name }} lift(void *); 14 | static void *lower(const {{ type_name }} &); 15 | static {{ type_name }} read(RustStream &); 16 | static void write(RustStream &, const {{ type_name }} &); 17 | static uint64_t allocation_size(const {{ type_name }} &); 18 | private: 19 | {%- if obj.has_callback_interface() %} 20 | friend struct {{ trait_impl }}; 21 | inline static HandleMap<{{ canonical_type_name }}> handle_map = {}; 22 | {%- endif %} 23 | }; 24 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/opt_conv.hpp: -------------------------------------------------------------------------------- 1 | struct {{ typ|ffi_converter_name }} { 2 | static {{ type_name }} lift(RustBuffer buf); 3 | static RustBuffer lower(const {{ type_name }}& val); 4 | static {{ type_name }} read(RustStream &stream); 5 | static void write(RustStream &stream, const {{ type_name }}& value); 6 | static uint64_t allocation_size(const {{ type_name }} &val); 7 | }; 8 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/opt_tmpl.cpp: -------------------------------------------------------------------------------- 1 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 2 | auto stream = RustStream(&buf); 3 | auto ret = {{ ffi_converter_name }}::read(stream); 4 | 5 | rustbuffer_free(buf); 6 | 7 | return ret; 8 | } 9 | 10 | RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }}& val) { 11 | auto buf = rustbuffer_alloc({{ ffi_converter_name }}::allocation_size(val)); 12 | auto stream = RustStream(&buf); 13 | 14 | {{ ffi_converter_name }}::write(stream, val); 15 | 16 | return buf; 17 | } 18 | 19 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 20 | char has_value; 21 | 22 | stream.get(has_value); 23 | 24 | {%- if typ|can_dereference_optional(ci) %} 25 | if (has_value) { 26 | return {{ inner_type|read_fn }}(stream); 27 | } else { 28 | return nullptr; 29 | } 30 | {%- else %} 31 | if (has_value) { 32 | return std::make_optional({{ inner_type|read_fn }}(stream)); 33 | } else { 34 | return std::nullopt; 35 | } 36 | {%- endif %} 37 | } 38 | 39 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }}& value) { 40 | stream.put(static_cast(!!value)); 41 | 42 | if (value) { 43 | {%- if typ|can_dereference_optional(ci) %} 44 | {{ inner_type|write_fn }}(stream, {{ inner_type.as_type()|deref(ci) }}value); 45 | {%- else %} 46 | {{ inner_type|write_fn }}(stream, value.value()); 47 | {%- endif %} 48 | } 49 | } 50 | 51 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &val) { 52 | uint64_t ret = 1; 53 | 54 | if (val) { 55 | {%- if typ|can_dereference_optional(ci) %} 56 | ret += {{ inner_type|allocation_size_fn }}({{ inner_type.as_type()|deref(ci) }}val); 57 | {%- else %} 58 | ret += {{ inner_type|allocation_size_fn }}(val.value()); 59 | {%- endif %} 60 | } 61 | 62 | return ret; 63 | } 64 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/rec.cpp: -------------------------------------------------------------------------------- 1 | {%- let rec = ci|get_record_definition(name) %} 2 | {%- let class_name = type_name|class_name %} 3 | {{ class_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 4 | auto stream = RustStream(&buf); 5 | auto ret = {{ ffi_converter_name }}::read(stream); 6 | 7 | rustbuffer_free(buf); 8 | 9 | return std::move(ret); 10 | } 11 | 12 | RustBuffer {{ ffi_converter_name }}::lower(const {{ class_name }} &val) { 13 | auto buf = rustbuffer_alloc(allocation_size(val)); 14 | auto stream = RustStream(&buf); 15 | 16 | {{ ffi_converter_name }}::write(stream, val); 17 | 18 | return std::move(buf); 19 | } 20 | 21 | {{ class_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 22 | return { 23 | {%- for field in rec.fields() %} 24 | {{ field|read_fn }}(stream){% if !loop.last %},{% endif %} 25 | {%- endfor %} 26 | }; 27 | } 28 | 29 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ class_name }} &val) { 30 | {%- for field in rec.fields() %} 31 | {{ field|write_fn }}(stream, {{ field.as_type()|deref(ci) }}val.{{ field.name()|var_name }}); 32 | {%- endfor %} 33 | } 34 | 35 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ class_name }} &val) { 36 | {% if rec.fields().is_empty() %} 37 | return 0; 38 | {% else %} 39 | return {% for field in rec.fields() %} 40 | {{ field|allocation_size_fn}}({{ field.as_type()|deref(ci) }}val.{{ field.name()|var_name() }}){% if !loop.last %} +{% else -%};{%- endif %} 41 | {%- endfor %} 42 | {% endif %} 43 | } 44 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/rec.hpp: -------------------------------------------------------------------------------- 1 | {%- let rec = ci|get_record_definition(name) %} 2 | {% call macros::docstring(rec, 0) %} 3 | struct {{ type_name }} { 4 | {%- for field in rec.fields() %} 5 | {%- call macros::docstring(field, 4) %} 6 | {{ field|type_name(ci) }} {{ field.name()|var_name }} 7 | {%- match field.default_value() %} 8 | {%- when Some with (literal) %} = {{ literal|literal_cpp(field, config.enum_style, ci) }};{%- else -%}; 9 | {%- endmatch %} 10 | {%- endfor %} 11 | }; 12 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/rec_conv.hpp: -------------------------------------------------------------------------------- 1 | {%- let class_name = type_name|class_name %} 2 | struct {{ typ|ffi_converter_name }} { 3 | static {{ class_name }} lift(RustBuffer); 4 | static RustBuffer lower(const {{ class_name }} &); 5 | static {{ class_name }} read(RustStream &); 6 | static void write(RustStream &, const {{ class_name }} &); 7 | static uint64_t allocation_size(const {{ class_name }} &); 8 | }; 9 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/rust_buf_stream.cpp: -------------------------------------------------------------------------------- 1 | struct RustStreamBuffer: std::basic_streambuf { 2 | RustStreamBuffer(RustBuffer *buf) { 3 | char* data = reinterpret_cast(buf->data); 4 | this->setg(data, data, data + buf->len); 5 | this->setp(data, data + buf->capacity); 6 | } 7 | ~RustStreamBuffer() = default; 8 | 9 | private: 10 | RustStreamBuffer() = delete; 11 | RustStreamBuffer(const RustStreamBuffer &) = delete; 12 | RustStreamBuffer(RustStreamBuffer &&) = delete; 13 | 14 | RustStreamBuffer &operator=(const RustStreamBuffer &) = delete; 15 | RustStreamBuffer &operator=(RustStreamBuffer &&) = delete; 16 | }; 17 | 18 | struct RustStream: std::basic_iostream { 19 | RustStream(RustBuffer *buf): 20 | std::basic_iostream(&streambuf), streambuf(RustStreamBuffer(buf)) { } 21 | 22 | template >> 23 | RustStream &operator>>(T &val) { 24 | read(reinterpret_cast(&val), sizeof(T)); 25 | 26 | if (std::endian::native != std::endian::big) { 27 | auto bytes = reinterpret_cast(&val); 28 | 29 | std::reverse(bytes, bytes + sizeof(T)); 30 | } 31 | 32 | return *this; 33 | } 34 | 35 | template >> 36 | RustStream &operator<<(T val) { 37 | if (std::endian::native != std::endian::big) { 38 | auto bytes = reinterpret_cast(&val); 39 | 40 | std::reverse(bytes, bytes + sizeof(T)); 41 | } 42 | 43 | write(reinterpret_cast(&val), sizeof(T)); 44 | 45 | return *this; 46 | } 47 | private: 48 | RustStreamBuffer streambuf; 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/rust_buf_tmpl.cpp: -------------------------------------------------------------------------------- 1 | RustBuffer rustbuffer_alloc(uint64_t len) { 2 | RustCallStatus status = { 0 }; 3 | auto buffer = {{ ci.ffi_rustbuffer_alloc().name() }}(len, &status); 4 | 5 | check_rust_call(status, nullptr); 6 | 7 | return buffer; 8 | } 9 | 10 | RustBuffer rustbuffer_from_bytes(const ForeignBytes &bytes) { 11 | RustCallStatus status = { 0 }; 12 | auto buffer = {{ ci.ffi_rustbuffer_from_bytes().name() }}(bytes, &status); 13 | 14 | check_rust_call(status, nullptr); 15 | 16 | return buffer; 17 | } 18 | 19 | void rustbuffer_free(RustBuffer buf) { 20 | RustCallStatus status = { 0 }; 21 | 22 | {{ ci.ffi_rustbuffer_free().name() }}(std::move(buf), &status); 23 | check_rust_call(status, nullptr); 24 | } 25 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | {%- import "macros.cpp" as macros %} 4 | 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #ifndef UNIFFI_CPP_INTERNALSTRUCTS 12 | #define UNIFFI_CPP_INTERNALSTRUCTS 13 | struct ForeignBytes { 14 | int32_t len; 15 | uint8_t *data; 16 | }; 17 | 18 | struct RustBuffer { 19 | uint64_t capacity; 20 | uint64_t len; 21 | uint8_t *data; 22 | }; 23 | 24 | struct RustCallStatus { 25 | int8_t code; 26 | RustBuffer error_buf; 27 | }; 28 | 29 | #endif 30 | 31 | {%- for def in self.scaffolding_definitions() %} 32 | {%- match def %} 33 | {%- when FfiDefinition::CallbackFunction(callback) %} 34 | {% call macros::ffi_return_type(callback) %} {{ callback.name()}}( 35 | {% call macros::arg_list_ffi_decl_xx(callback) %} 36 | ); 37 | {%- when FfiDefinition::Struct(ffi_struct) %} 38 | struct {{ ffi_struct.name()|ffi_struct_name }} { 39 | {%- for field in ffi_struct.fields() %} 40 | {{ field.type_().borrow()|ffi_type_name }} {{ field.name()|var_name }}; 41 | {%- endfor %} 42 | }; 43 | {%- when FfiDefinition::Function(func) %} 44 | {% match func.return_type() -%} 45 | {% when Some with (return_type) %}{{ return_type|ffi_type_name }} {% when None %}void {% endmatch %}{{ func.name() }}( 46 | {%- for arg in func.arguments() %} 47 | {{- arg.type_().borrow()|ffi_type_name }} {{ arg.name() }}{% if !loop.last || func.has_rust_call_status_arg() %}, {% endif -%} 48 | {% endfor %} 49 | {%- if func.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif -%} 50 | ); 51 | {%- endmatch %} 52 | {%- endfor %} 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/callback.cpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | {%- let type_name = typ|type_name(ci) %} 3 | {%- let class_name = type_name|class_name %} 4 | {%- let canonical_type_name = typ|canonical_name %} 5 | {%- let iface = ci|get_callback_interface_definition(name) %} 6 | namespace { 7 | class {{ iface.name() }}Proxy: public {{ iface.name() }} { 8 | public: 9 | {{ iface.name() }}Proxy(uint64_t handle): handle(handle) { } 10 | 11 | ~{{ iface.name() }}Proxy() override { 12 | ForeignCallback *callback_stub = reinterpret_cast({{ ffi_converter_name|class_name }}::fn_handle.load()); 13 | 14 | RustBuffer out_buf = { 15 | .capacity = 0, 16 | .len = 0, 17 | .data = nullptr, 18 | }; 19 | 20 | callback_stub(this->handle, 0, nullptr, 0, &out_buf); 21 | 22 | rustbuffer_free(out_buf); 23 | } 24 | 25 | {% for m in iface.methods() %} 26 | {%- match m.return_type() -%} 27 | {% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% when None %}void {% endmatch %}{{ m.name() }}( 28 | {%- for arg in m.arguments() %} 29 | {{- arg.as_type().borrow()|type_name(ci) }} {{ arg.name() }}{% if !loop.last %}, {% endif -%} 30 | {% endfor %}) override { 31 | ForeignCallback *callback_stub = reinterpret_cast({{ ffi_converter_name|class_name }}::fn_handle.load()); 32 | if (callback_stub == nullptr) { 33 | throw std::runtime_error("callback stub not initialized, this indicates a bug in the generated code"); 34 | } 35 | 36 | RustBuffer out_buf = { 37 | .capacity = 0, 38 | .len = 0, 39 | .data = nullptr, 40 | }; 41 | 42 | int32_t size = 0; 43 | {% for arg in m.arguments() %} 44 | size += {{ arg|allocation_size_fn }}({{ arg.name() }}); 45 | {% endfor %} 46 | RustBuffer in_buf = rustbuffer_alloc(size); 47 | RustStream in_stream(&in_buf); 48 | 49 | {% for arg in m.arguments() %} 50 | {{ arg|write_fn }}(in_stream, {{ arg.name() }}); 51 | {% endfor %} 52 | 53 | auto ret = callback_stub(this->handle, {{ loop.index }}, in_buf.data, size, &out_buf); 54 | rustbuffer_free(in_buf); 55 | 56 | if (ret == UNIFFI_CALL_STATUS_OK) { 57 | {% match m.return_type() %} 58 | {% when Some with (return_type) %} 59 | RustStream out_stream(&out_buf); 60 | auto result = {{ return_type|read_fn }}(out_stream); 61 | rustbuffer_free(out_buf); 62 | 63 | return result; 64 | {% else %} 65 | rustbuffer_free(out_buf); 66 | {% endmatch %} 67 | } 68 | else if (ret == UNIFFI_CALL_STATUS_ERROR) { 69 | RustStream out_stream(&out_buf); 70 | int32_t v; 71 | out_stream >> v; 72 | {%- if m.throws() %} 73 | switch (v) { 74 | {%- let err_type = m.throws_type().unwrap()|type_name(ci) %} 75 | {%- let err_enum = ci.get_enum_definition(err_type).unwrap() %} 76 | {%- for variant in err_enum.variants() %} 77 | {%- let converter_name = err_enum|ffi_converter_name %} 78 | case {{ loop.index }}: 79 | { 80 | auto result = {{ converter_name }}{{ variant.name() }}::read(out_stream, v); 81 | rustbuffer_free(out_buf); 82 | 83 | throw result; 84 | } 85 | {%- endfor %} 86 | default: 87 | rustbuffer_free(out_buf); 88 | throw std::runtime_error("Unexpected error variant: " + std::to_string(v)); 89 | } 90 | {%- endif %} 91 | rustbuffer_free(out_buf); 92 | throw std::runtime_error("Callback returned an error"); 93 | } 94 | else if (ret == UNIFFI_CALL_STATUS_PANIC) { 95 | auto result = FfiConverterString::lift(out_buf); 96 | 97 | throw std::runtime_error(result); 98 | } 99 | else { 100 | rustbuffer_free(out_buf); 101 | throw std::runtime_error("Unknown error code: " + std::to_string(ret)); 102 | } 103 | } 104 | {% endfor %} 105 | private: 106 | uint64_t handle; 107 | }; 108 | } 109 | 110 | {{ type_name }} {{ ffi_converter_name|class_name }}::lift(uint64_t handle) { 111 | return std::make_shared<{{ iface.name() }}Proxy>(handle); 112 | } 113 | 114 | uint64_t {{ ffi_converter_name|class_name }}::lower(const {{ type_name }} &obj) { 115 | return {{ ffi_converter_name|class_name }}::fn_handle.load(); 116 | } 117 | 118 | {{ type_name }} {{ ffi_converter_name|class_name }}::read(RustStream &stream) { 119 | uint64_t handle; 120 | stream >> handle; 121 | 122 | return {{ ffi_converter_name|class_name }}::lift(handle); 123 | } 124 | 125 | void {{ ffi_converter_name|class_name }}::write(RustStream &stream, const {{ type_name }} &obj) { 126 | stream << {{ ffi_converter_name|class_name }}::lower(obj); 127 | } 128 | 129 | int32_t {{ ffi_converter_name|class_name }}::allocation_size(const {{ type_name }} &obj) { 130 | return static_cast(sizeof(uint64_t)); 131 | } 132 | 133 | std::atomic {{ ffi_converter_name|class_name }}::fn_handle = 0; 134 | 135 | 136 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/callback.hpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | {%- let type_name = typ|type_name(ci) %} 3 | {%- let class_name = type_name|class_name %} 4 | {%- let canonical_type_name = typ|canonical_name %} 5 | struct {{ ffi_converter_name|class_name }} { 6 | static {{ type_name }} lift(uint64_t); 7 | static uint64_t lower(const {{ type_name }} &); 8 | static {{ type_name }} read(RustStream &); 9 | static void write(RustStream &, const {{ type_name }} &); 10 | static int32_t allocation_size(const {{ type_name }} &); 11 | 12 | static std::atomic fn_handle; 13 | }; 14 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/custom.cpp: -------------------------------------------------------------------------------- 1 | {%- let converter = typ|ffi_converter_name -%} 2 | {%- let custom_converter = "UniffiCustomTypeConverter{}"|format(typ|type_name(ci)) -%} 3 | {{ type_name }} {{ converter }}::lift(RustBuffer buff) { 4 | auto stream = RustStream(&buff); 5 | auto builtin_val = {{ builtin|read_fn }}(stream); 6 | 7 | return {{ custom_converter }}::into_custom(builtin_val); 8 | } 9 | 10 | RustBuffer {{ converter }}::lower(const {{ type_name }} &val) { 11 | auto buf = rustbuffer_alloc(allocation_size(val)); 12 | auto stream = RustStream(&buf); 13 | auto builtin_val = {{ custom_converter }}::from_custom(val); 14 | 15 | {{ builtin|write_fn }}(stream, builtin_val); 16 | 17 | return std::move(buf); 18 | } 19 | 20 | {{ type_name }} {{ converter }}::read(RustStream &stream) { 21 | auto builtin_val = {{ builtin|read_fn }}(stream); 22 | 23 | return {{ custom_converter }}::into_custom(builtin_val); 24 | } 25 | 26 | void {{ converter }}::write(RustStream &stream, const {{ type_name }} &val) { 27 | auto builtin_val = {{ custom_converter }}::from_custom(val); 28 | 29 | {{ builtin|write_fn }}(stream, builtin_val); 30 | } 31 | 32 | int32_t {{ converter }}::allocation_size(const {{ type_name }} &val) { 33 | auto builtin_val = {{ custom_converter }}::from_custom(val); 34 | 35 | return {{ builtin|allocation_size_fn }}(builtin_val); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/custom.hpp: -------------------------------------------------------------------------------- 1 | {% let type_name = typ|type_name(ci) %} 2 | struct {{ typ|ffi_converter_name }} { 3 | static {{ type_name }} lift(RustBuffer); 4 | static RustBuffer lower(const {{ type_name }} &); 5 | static {{ type_name }} read(RustStream &); 6 | static void write(RustStream &, const {{ type_name }} &); 7 | static uint64_t allocation_size(const {{ type_name }} &); 8 | }; 9 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/err.cpp: -------------------------------------------------------------------------------- 1 | {%- let type_name = e|type_name(ci) %} 2 | {%- let class_name = type_name|class_name %} 3 | {%- let ffi_converter_name = typ|ffi_converter_name %} 4 | {%- let namespace = type_name|to_lower_snake_case %} 5 | 6 | {{ namespace }}::{{ variant.name() }} {{ ffi_converter_name }}{{ variant.name() }}::lift(RustBuffer buf, int32_t v) { 7 | auto stream = RustStream(&buf); 8 | auto ret = {{ ffi_converter_name }}{{ variant.name() }}::read(stream, v); 9 | 10 | rustbuffer_free(buf); 11 | 12 | return ret; 13 | } 14 | 15 | RustBuffer {{ ffi_converter_name }}{{ variant.name() }}::lower(const {{ namespace }}::{{ variant.name() }} &val) { 16 | auto buf = rustbuffer_alloc(allocation_size(val)); 17 | auto stream = RustStream(&buf); 18 | 19 | {{ ffi_converter_name }}{{ variant.name() }}::write(stream, val); 20 | 21 | return std::move(buf); 22 | } 23 | 24 | {{ namespace }}::{{ variant.name() }} {{ ffi_converter_name }}{{ variant.name() }}::read(RustStream &stream, int32_t v) { 25 | if (v != {{ loop.index }}) { 26 | throw std::runtime_error("Unexpected error variant"); 27 | } 28 | 29 | {%- if e.is_flat() %} 30 | return {{ namespace }}::{{ variant.name() }}(); 31 | {%- else %} 32 | {{ namespace }}::{{ variant.name() }} var; 33 | {%- for field in variant.fields() %} 34 | var.{{ field.name()|var_name }} = {{ field|read_fn }}(stream); 35 | {%- endfor %} 36 | return var; 37 | {%- endif %} 38 | } 39 | 40 | void {{ ffi_converter_name }}{{ variant.name() }}::write(RustStream &stream, const {{ namespace }}::{{ variant.name() }} &val) { 41 | stream << int32_t({{ loop.index }}); 42 | 43 | {%- if e.is_flat() %} 44 | {{ Type::String.borrow()|write_fn }}(stream, val.what()); 45 | {%- else %} 46 | {%- for field in variant.fields() %} 47 | {{ field|write_fn }}(stream, val.{{ field.name()|var_name }}); 48 | {%- endfor %} 49 | {%- endif %} 50 | } 51 | 52 | int32_t {{ ffi_converter_name }}{{ variant.name() }}::allocation_size(const {{ namespace }}::{{ variant.name() }} &val) { 53 | int32_t size = sizeof(int32_t); 54 | {%- if e.is_flat() %} 55 | size += {{ Type::String.borrow()|allocation_size_fn }}(val.what()); 56 | {%- else %} 57 | {%- for field in variant.fields() %} 58 | size += {{ field|allocation_size_fn }}(val.{{ field.name()|var_name }}); 59 | {%- endfor %} 60 | {%- endif %} 61 | 62 | return size; 63 | } 64 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/err.hpp: -------------------------------------------------------------------------------- 1 | {%- let type_name = e|type_name(ci) %} 2 | {%- let class_name = type_name|class_name %} 3 | {%- let ffi_converter_name = typ|ffi_converter_name %} 4 | {%- let namespace = type_name|to_lower_snake_case %} 5 | struct {{ ffi_converter_name }}{{ variant.name() }} { 6 | static {{ namespace }}::{{ variant.name() }} lift(RustBuffer buf, int32_t v); 7 | static RustBuffer lower(const {{ namespace }}::{{ variant.name() }} &); 8 | static {{ namespace }}::{{ variant.name() }} read(RustStream &stream, int32_t v); 9 | static void write(RustStream &stream, const {{ namespace }}::{{ variant.name() }} &); 10 | static int32_t allocation_size(const {{ namespace }}::{{ variant.name() }} &); 11 | }; 12 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp: -------------------------------------------------------------------------------- 1 | {% macro fn_prologue(ci, func, ffi_func) -%} 2 | {%- if ffi_func.has_rust_call_status_arg() %} 3 | out_status->code = UNIFFI_CALL_STATUS_OK; 4 | {%- endif %} 5 | try { 6 | {% endmacro %} 7 | 8 | {% macro fn_epilogue(ci, func, ffi_func) -%} 9 | {%- if ffi_func.has_rust_call_status_arg() %} 10 | {%- if func.throws() %} 11 | {%- let err_type = func.throws_type().unwrap()|type_name(ci) %} 12 | {%- let err_enum = ci.get_enum_definition(err_type).unwrap() %} 13 | {%- for variant in err_enum.variants() %} 14 | {%- let converter_name = err_enum|ffi_converter_name %} 15 | } catch (const {{ err_type|to_lower_snake_case }}::{{ variant.name() }} &e) { 16 | out_status->code = UNIFFI_CALL_STATUS_ERROR; 17 | out_status->error_buf = {{ converter_name }}{{ variant.name() }}::lower(e); 18 | {%- endfor %} 19 | {%- endif %} 20 | } catch (const std::exception &e) { 21 | out_status->code = UNIFFI_CALL_STATUS_PANIC; 22 | out_status->error_buf = {{ Type::String.borrow()|ffi_converter_name }}::lower(e.what()); 23 | } catch (...) { 24 | out_status->code = UNIFFI_CALL_STATUS_PANIC; 25 | } 26 | {% match func.return_type() %} 27 | {% when Some with (return_type) %} 28 | return {}; 29 | {% else %} 30 | {% endmatch %} 31 | {%- endif %} 32 | {% endmacro %} 33 | 34 | {% macro invoke_native_fn(scaffolding_fn, namespace) %} 35 | {% match func.return_type() %} 36 | {% when Some with (return_type) %} 37 | auto ret = {{ namespace }}::{{ scaffolding_fn.name() }}( 38 | {%- for arg in scaffolding_fn.arguments() %} 39 | {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} 40 | {% endfor %}); 41 | return {{ return_type|lower_fn }}(ret); 42 | {% when None %} 43 | {{ namespace }}::{{ scaffolding_fn.name() }}( 44 | {%- for arg in scaffolding_fn.arguments() %} 45 | {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} 46 | {% endfor %}); 47 | {% endmatch %} 48 | {% endmacro %} 49 | 50 | {% macro invoke_native_fn_obj(scaffolding_fn) %} 51 | {% match func.return_type() %} 52 | {% when Some with (return_type) %} 53 | auto ret = obj->{{ scaffolding_fn.name() }}( 54 | {% if scaffolding_fn.takes_self_by_arc() %}obj{% if !scaffolding_fn.arguments().is_empty() %},{% endif %}{% endif %} 55 | {%- for arg in scaffolding_fn.arguments() %} 56 | {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} 57 | {% endfor %}); 58 | return {{ return_type|lower_fn }}(ret); 59 | {% when None %} 60 | obj->{{ scaffolding_fn.name() }}( 61 | {% if scaffolding_fn.takes_self_by_arc() %}obj{% if !scaffolding_fn.arguments().is_empty() %},{% endif %}{% endif %} 62 | {%- for arg in scaffolding_fn.arguments() %} 63 | {{- arg|lift_fn }}({{ arg.name()|var_name }}){% if !loop.last %}, {% endif -%} 64 | {% endfor %}); 65 | {% endmatch %} 66 | {% endmacro %} 67 | 68 | {% macro fn_definition(ffi_func) %} 69 | {% match ffi_func.return_type() -%} 70 | {% when Some with (return_type) %}{{ return_type|ffi_type_name }} {% when None %}void {% endmatch %}{{ ffi_func.name() }}( 71 | {%- for arg in ffi_func.arguments() %} 72 | {{- arg.type_().borrow()|ffi_type_name }} {{ arg.name() }}{% if !loop.last || ffi_func.has_rust_call_status_arg() %}, {% endif -%} 73 | {% endfor %} 74 | {%- if ffi_func.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif -%}) 75 | {% endmacro %} 76 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp: -------------------------------------------------------------------------------- 1 | {{ type_name }} {{ ffi_converter_name }}::lift(void *ptr) { 2 | return {{ name }}_map.at((uint64_t)ptr); 3 | } 4 | 5 | void *{{ ffi_converter_name }}::lower(const {{ type_name }} &obj) { 6 | auto ret = {{ name }}_map.insert(obj); 7 | return (void *)ret; 8 | } 9 | 10 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 11 | std::uintptr_t ptr; 12 | stream >> ptr; 13 | 14 | return {{ name }}_map.at(ptr); 15 | } 16 | 17 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &obj) { 18 | {{ name }}_map.insert(obj); 19 | stream << (uint64_t)obj.get(); 20 | } 21 | 22 | int32_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &) { 23 | return 8; 24 | } 25 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp: -------------------------------------------------------------------------------- 1 | {%- let type_name = typ|type_name(ci) %} 2 | struct {{ typ|ffi_converter_name }} { 3 | static {{ type_name }} lift(void *); 4 | static void *lower(const {{ type_name }} &); 5 | static {{ type_name }} read(RustStream &); 6 | static void write(RustStream &, const {{ type_name }} &); 7 | static int32_t allocation_size(const {{ type_name }} &); 8 | }; 9 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/scaffolding/object_map.cpp: -------------------------------------------------------------------------------- 1 | template struct ObjectMap { 2 | ObjectMap() = default; 3 | 4 | std::shared_ptr at(uint64_t ptr) { 5 | std::lock_guard guard(this->mutex); 6 | 7 | return this->map.at(ptr).ptr; 8 | } 9 | 10 | uint64_t insert(std::shared_ptr impl) { 11 | std::lock_guard guard(this->mutex); 12 | auto key = (uint64_t)impl.get(); 13 | 14 | if (this->map.contains(key)) { 15 | this->map.at(key).ref_count += 1; 16 | } else { 17 | this->map.insert({ key, {impl, 1} }); 18 | } 19 | 20 | return key; 21 | } 22 | 23 | void erase(uint64_t ptr) { 24 | std::lock_guard guard(this->mutex); 25 | 26 | auto &wrapper = this->map.at(ptr); 27 | wrapper.ref_count -= 1; 28 | 29 | if (wrapper.ref_count == 0) { 30 | this->map.erase(ptr); 31 | } 32 | } 33 | private: 34 | ObjectMap(const ObjectMap &) = delete; 35 | ObjectMap(ObjectMap &&) = delete; 36 | 37 | ObjectMap &operator=(const ObjectMap &) = delete; 38 | ObjectMap &operator=(ObjectMap &&) = delete; 39 | 40 | struct PtrWrapper { 41 | std::shared_ptr ptr = nullptr; 42 | uint64_t ref_count = 0; 43 | }; 44 | 45 | std::mutex mutex; 46 | std::map map; 47 | }; 48 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/seq_conv.hpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | struct {{ ffi_converter_name|class_name }} { 3 | static {{ type_name }} lift(RustBuffer); 4 | static RustBuffer lower(const {{ type_name }} &); 5 | static {{ type_name }} read(RustStream &); 6 | static void write(RustStream &, const {{ type_name }} &); 7 | static uint64_t allocation_size(const {{ type_name }} &); 8 | }; 9 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/seq_tmpl.cpp: -------------------------------------------------------------------------------- 1 | {%- let ffi_converter_name = typ|ffi_converter_name %} 2 | {%- let class_name = ffi_converter_name|class_name %} 3 | {{ type_name }} {{ class_name }}::lift(RustBuffer buf) { 4 | auto stream = RustStream(&buf); 5 | auto ret = read(stream); 6 | 7 | rustbuffer_free(buf); 8 | 9 | return ret; 10 | } 11 | 12 | RustBuffer {{ class_name }}::lower(const {{ type_name }} &val) { 13 | auto buf = rustbuffer_alloc(allocation_size(val)); 14 | auto stream = RustStream(&buf); 15 | 16 | write(stream, val); 17 | 18 | return buf; 19 | } 20 | 21 | {{ type_name }} {{ class_name }}::read(RustStream &stream) { 22 | {{ type_name }} ret; 23 | int32_t count; 24 | stream >> count; 25 | 26 | ret.reserve(count); 27 | 28 | for (decltype(count) i = 0; i < count; i++) { 29 | ret.push_back({{ inner_type|read_fn }}(stream)); 30 | } 31 | 32 | return ret; 33 | } 34 | 35 | void {{ class_name }}::write(RustStream &stream, const {{ type_name }} &val) { 36 | stream << static_cast(val.size()); 37 | 38 | for (auto &elem : val) { 39 | {{ inner_type|write_fn }}(stream, {{ inner_type.as_type()|deref(ci) }}elem); 40 | } 41 | } 42 | 43 | uint64_t {{ class_name }}::allocation_size(const {{ type_name }} &val) { 44 | uint64_t size = sizeof(int32_t); 45 | 46 | for (auto &elem : val) { 47 | size += {{ inner_type|allocation_size_fn }}({{inner_type.as_type()|deref(ci) }}elem); 48 | } 49 | 50 | return size; 51 | } 52 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/str_conv.cpp: -------------------------------------------------------------------------------- 1 | std::string {{ ffi_converter_name }}::lift(RustBuffer buf) { 2 | auto string = std::string(reinterpret_cast(buf.data), buf.len); 3 | 4 | rustbuffer_free(buf); 5 | 6 | return string; 7 | } 8 | 9 | RustBuffer {{ ffi_converter_name }}::lower(const std::string &val) { 10 | auto len = static_cast(val.length()); 11 | auto bytes = ForeignBytes { len, reinterpret_cast(const_cast(val.data())) }; 12 | 13 | return rustbuffer_from_bytes(bytes); 14 | } 15 | 16 | std::string {{ ffi_converter_name }}::read(RustStream &stream) { 17 | int32_t len; 18 | std::string string; 19 | 20 | stream >> len; 21 | 22 | string.resize(len); 23 | stream.read(string.data(), len); 24 | 25 | return string; 26 | } 27 | 28 | void {{ ffi_converter_name }}::write(RustStream &stream, const std::string &val) { 29 | stream << static_cast(val.length()); 30 | stream.write(val.data(), val.length()); 31 | } 32 | 33 | uint64_t {{ ffi_converter_name }}::allocation_size(const std::string &val) { 34 | return static_cast(sizeof(int32_t) + val.length()); 35 | } 36 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/str_conv.hpp: -------------------------------------------------------------------------------- 1 | struct {{ typ|ffi_converter_name }} { 2 | static std::string lift(RustBuffer buf); 3 | static RustBuffer lower(const std::string &); 4 | static std::string read(RustStream &); 5 | static void write(RustStream &, const std::string &); 6 | static uint64_t allocation_size(const std::string &); 7 | }; 8 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/timestamp_conv.hpp: -------------------------------------------------------------------------------- 1 | struct {{ typ|ffi_converter_name }} { 2 | static {{ type_name }} lift(RustBuffer); 3 | static RustBuffer lower(const {{ type_name }} &); 4 | static {{ type_name }} read(RustStream &); 5 | static void write(RustStream &, const {{ type_name }} &); 6 | static uint64_t allocation_size(const {{ type_name }} &); 7 | }; 8 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/timestamp_helper.cpp: -------------------------------------------------------------------------------- 1 | {{ type_name }} {{ ffi_converter_name }}::lift(RustBuffer buf) { 2 | auto stream = RustStream(&buf); 3 | auto val = {{ ffi_converter_name }}::read(stream); 4 | 5 | rustbuffer_free(buf); 6 | 7 | return val; 8 | } 9 | 10 | RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }} &val) { 11 | auto buf = rustbuffer_alloc(allocation_size(val)); 12 | auto stream = RustStream(&buf); 13 | 14 | {{ ffi_converter_name }}::write(stream, val); 15 | 16 | return std::move(buf); 17 | } 18 | 19 | {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { 20 | int64_t secs; 21 | uint32_t nanos; 22 | 23 | stream >> secs; 24 | stream >> nanos; 25 | 26 | auto sign = secs < 0 ? -1 : 1; 27 | 28 | auto duration = std::chrono::duration_cast<{{ type_name }}::duration>(std::chrono::seconds(secs) + (sign * std::chrono::nanoseconds(nanos))); 29 | 30 | return {{ type_name }}(duration); 31 | } 32 | 33 | void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &val) { 34 | auto duration = val.time_since_epoch(); 35 | auto secs = std::chrono::duration_cast>(duration); 36 | auto nanos = (duration - secs).count(); 37 | 38 | auto sign = secs.count() < 0 ? -1 : 1; 39 | 40 | stream << secs.count() << static_cast(sign * nanos); 41 | } 42 | 43 | uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &) { 44 | return static_cast(sizeof(int64_t) + sizeof(uint32_t)); 45 | } 46 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/types.cpp: -------------------------------------------------------------------------------- 1 | {%- import "macros.cpp" as macros %} 2 | 3 | {%- for typ in ci.iter_types() %} 4 | {%- let type_name = typ|type_name(ci) %} 5 | {%- let ffi_converter_name = typ|ffi_converter_name %} 6 | {%- let canonical_type_name = typ|canonical_name %} 7 | {%- let contains_object_references = ci.item_contains_object_references(typ) %} 8 | {%- let namespace = ci.namespace() %} 9 | 10 | {%- match typ %} 11 | {%- when Type::Object { module_path, name, imp } %} 12 | {% include "obj.cpp" %} 13 | {%- else %} 14 | {%- endmatch %} 15 | {% endfor ~%} 16 | 17 | namespace uniffi { 18 | {%- for typ in ci.iter_types() %} 19 | {%- let type_name = typ|type_name(ci) %} 20 | {%- let ffi_converter_name = typ|ffi_converter_name %} 21 | {%- let canonical_type_name = typ|canonical_name %} 22 | {%- let contains_object_references = ci.item_contains_object_references(typ) %} 23 | {%- let namespace = ci.namespace() %} 24 | 25 | {%- match typ %} 26 | {%- when Type::Enum { name, module_path } %} 27 | {%- let e = ci|get_enum_definition(name) %} 28 | {%- if ci.is_name_used_as_error(name) %} 29 | {% include "err_tmpl.cpp" %} 30 | {%- else %} 31 | {% include "enum_tmpl.cpp" %} 32 | {%- endif %} 33 | {%- when Type::Object { module_path, name, imp } %} 34 | {% include "obj_conv.cpp" %} 35 | {%- when Type::Record { module_path, name } %} 36 | {% include "rec.cpp" %} 37 | {%- when Type::Optional { inner_type } %} 38 | {% include "opt_tmpl.cpp" %} 39 | {%- when Type::Sequence { inner_type } %} 40 | {% include "seq_tmpl.cpp" %} 41 | {%- when Type::Map { key_type, value_type } %} 42 | {% include "map_tmpl.cpp" %} 43 | {%- when Type::CallbackInterface { module_path, name } %} 44 | {%- let cbi = ci|get_callback_interface_definition(name) %} 45 | {%- let ffi_init_callback = cbi.ffi_init_callback() %} 46 | {%- let interface_name = name %} 47 | {%- let methods = cbi.methods() %} 48 | {%- let vtable = cbi.vtable() %} 49 | {%- let vtable_methods = cbi.vtable_methods() %} 50 | {% include "callback_conv.cpp" %} 51 | {% include "callback_iface_tmpl.cpp" %} 52 | {%- when Type::Timestamp %} 53 | {% include "timestamp_helper.cpp" %} 54 | {%- when Type::Duration %} 55 | {% include "duration_helper.cpp" %} 56 | {%- when Type::External { module_path, name, namespace, kind, tagged } %} 57 | {% include "ext_typ_tmpl.cpp" %} 58 | {%- when Type::Custom { module_path, name, builtin } %} 59 | {%- include "custom.cpp" %} 60 | {%- else %} 61 | {%- endmatch %} 62 | {% endfor ~%} 63 | 64 | } 65 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/wrapper.cpp: -------------------------------------------------------------------------------- 1 | /* This file was generated by uniffi-bindgen-cpp. */ 2 | {%- let namespace = ci.namespace() %} 3 | {%- import "macros.cpp" as macros %} 4 | #include 5 | 6 | #include "{{ ci.namespace() }}.hpp" 7 | 8 | namespace {{ namespace }} { 9 | namespace uniffi { 10 | template inline constexpr bool always_false_v = false; 11 | 12 | namespace { 13 | void ensure_initialized() { 14 | auto bindings_contract_version = {{ ci.uniffi_contract_version() }}; 15 | auto scaffolding_contract_version = {{ ci.ffi_uniffi_contract_version().name() }}(); 16 | 17 | if (bindings_contract_version != scaffolding_contract_version) { 18 | throw std::runtime_error("UniFFI contract version mismatch: try cleaning and rebuilding your project"); 19 | } 20 | 21 | {%- for (name, expected_checksum) in ci.iter_checksums() %} 22 | if ({{ name }}() != {{ expected_checksum }}) { 23 | throw std::runtime_error("UniFFI API checksum mismatch: try cleaning and rebuilding your project"); 24 | } 25 | {%- endfor %} 26 | 27 | {% for fn in self.initialization_fns() -%} 28 | {{ fn }}(); 29 | {% endfor -%} 30 | } 31 | 32 | // Note: we need this indirection here and can't inline this code in the rust_call function 33 | // as it's a templated function 34 | void initialize() { 35 | static std::once_flag init_flag; 36 | std::call_once(init_flag, ensure_initialized); 37 | } 38 | } 39 | 40 | template 41 | void check_rust_call(const RustCallStatus &status, F error_cb) { 42 | switch (status.code) { 43 | case 0: 44 | return; 45 | 46 | case 1: 47 | if constexpr (!std::is_null_pointer_v) { 48 | error_cb(status.error_buf)->throw_underlying(); 49 | } 50 | break; 51 | 52 | case 2: 53 | if (status.error_buf.len > 0) { 54 | throw std::runtime_error({{ Type::String.borrow()|lift_fn }}(status.error_buf)); 55 | } 56 | 57 | throw std::runtime_error("A Rust panic has occurred"); 58 | } 59 | 60 | throw std::runtime_error("Unexpected Rust call status"); 61 | } 62 | 63 | template > 64 | R rust_call(F f, EF error_cb, Args... args) { 65 | initialize(); 66 | 67 | RustCallStatus status = { 0 }; 68 | 69 | if constexpr (std::is_void_v) { 70 | f(args..., &status); 71 | check_rust_call(status, error_cb); 72 | } else { 73 | auto ret = f(args..., &status); 74 | check_rust_call(status, error_cb); 75 | 76 | return ret; 77 | } 78 | } 79 | 80 | template 81 | void rust_call_trait_interface(RustCallStatus* status, F make_call, W write_value) { 82 | initialize(); 83 | 84 | constexpr bool has_return_type = std::negation>>::value; 85 | 86 | try { 87 | if constexpr(has_return_type) { 88 | write_value(make_call()); 89 | } else { 90 | make_call(); 91 | } 92 | } catch (std::exception &e) { 93 | status->code = 2; 94 | status->error_buf = {{ Type::String.borrow()|lower_fn }}(e.what()); 95 | } 96 | } 97 | 98 | template 99 | void rust_call_trait_interface_with_error(RustCallStatus* status, F make_call, W write_value, EF error_cb) { 100 | initialize(); 101 | 102 | constexpr bool has_return_type = std::negation>>::value; 103 | 104 | try { 105 | try { 106 | if constexpr(has_return_type) { 107 | write_value(make_call()); 108 | } else { 109 | make_call(); 110 | } 111 | } catch (E &e) { 112 | status->code = 1; 113 | status->error_buf = error_cb(e); 114 | } 115 | } catch (std::exception &e) { 116 | status->code = 2; 117 | status->error_buf = {{ Type::String.borrow()|lower_fn }}(e.what()); 118 | } 119 | } 120 | 121 | 122 | {% include "rust_buf_tmpl.cpp" %} 123 | 124 | {{ internal_type_helper_code }} 125 | } // namespace uniffi 126 | 127 | {{ type_helper_code }} 128 | 129 | {%- for func in ci.function_definitions() %} 130 | {% include "fn_def.cpp" %} 131 | {% endfor -%} 132 | } // namespace {{ namespace }} 133 | -------------------------------------------------------------------------------- /bindgen/src/bindings/cpp/templates/wrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | {%- for include in self.includes() %} 21 | #include <{{ include }}> 22 | {%- endfor %} 23 | 24 | 25 | {%~ let namespace = ci.namespace() %} 26 | #include "{{ namespace }}_scaffolding.hpp" 27 | 28 | {%- import "macros.cpp" as macros %} 29 | {% call macros::docstring_value(ci.namespace_docstring(), 0) %} 30 | namespace {{ namespace }} { 31 | {%- for typ in ci.iter_types() %} 32 | {%- let type_name = typ|type_name(ci) %} 33 | {%- match typ %} 34 | {%- when Type::Enum { module_path, name } %} 35 | {%- let e = ci|get_enum_definition(name) %} 36 | {%- if ci.is_name_used_as_error(name) || !e.is_flat() %} 37 | struct {{ name }}; 38 | {%- else %} 39 | enum class {{ name }}; 40 | {%- endif %} 41 | {%- when Type::Record { module_path, name } %} 42 | struct {{ name }}; 43 | {%- when Type::Object { module_path, name, imp } %} 44 | struct {{ name }}; 45 | {%- when Type::CallbackInterface { module_path, name } %} 46 | struct {{ name }}; 47 | {%- when Type::Custom { module_path, name, builtin } %} 48 | {%- match config.custom_types.get(name.as_str()) %} 49 | {%- when None %} 50 | typedef {{ builtin|type_name(ci) }} {{ name }}; 51 | {%- when Some with (type_config) %} 52 | {%- match type_config.type_name %} 53 | {%- when Some with (type_name) %} 54 | typedef {{ type_name }} {{ name }}; 55 | {%- else %} 56 | {%- endmatch %} 57 | {%- endmatch %} 58 | {%- else %} 59 | {%- endmatch %} 60 | {%- endfor %} 61 | 62 | {%- for typ in self.sorted_types(ci.iter_types()) %} 63 | {%- let type_name = typ|type_name(ci) %} 64 | {%- match typ %} 65 | {%- when Type::Enum { module_path, name } %} 66 | {%- let e = ci|get_enum_definition(name) %} 67 | {%- if ci.is_name_used_as_error(name) %} 68 | {% include "err.hpp" %} 69 | {%- else %} 70 | {% include "enum.hpp" %} 71 | {%- endif %} 72 | {%- when Type::Record { module_path, name } %} 73 | {% include "rec.hpp" %} 74 | {%- when Type::CallbackInterface { module_path, name } %} 75 | {%- let cbi = ci|get_callback_interface_definition(name) %} 76 | {%- let ffi_init_callback = cbi.ffi_init_callback() %} 77 | {%- let interface_name = name %} 78 | {%- let methods = cbi.methods() %} 79 | {%- let vtable = cbi.vtable() %} 80 | {%- let vtable_methods = cbi.vtable_methods() %} 81 | {%- let interface_docstring = cbi.docstring() %} 82 | {% include "callback.hpp" %} 83 | {%- when Type::Object { module_path, name, imp } %} 84 | {% include "obj.hpp" %} 85 | {%- else %} 86 | {%- endmatch %} 87 | {%- endfor %} 88 | 89 | namespace uniffi { 90 | {%- include "rust_buf_stream.cpp" %} 91 | 92 | RustBuffer rustbuffer_alloc(uint64_t); 93 | RustBuffer rustbuffer_from_bytes(const ForeignBytes &); 94 | void rustbuffer_free(RustBuffer); 95 | 96 | {%- include "handle_map.cpp" %} 97 | 98 | {%- for typ in ci.iter_types() %} 99 | {%- let type_name = typ|type_name(ci) %} 100 | {%- match typ %} 101 | {%- when Type::Boolean %} 102 | {% include "bool_conv.hpp" %} 103 | {%- when Type::UInt8 %} 104 | {% include "arith_conv.hpp" %} 105 | {%- when Type::Int8 %} 106 | {% include "arith_conv.hpp" %} 107 | {%- when Type::UInt16 %} 108 | {% include "arith_conv.hpp" %} 109 | {%- when Type::Int16 %} 110 | {% include "arith_conv.hpp" %} 111 | {%- when Type::UInt32 %} 112 | {% include "arith_conv.hpp" %} 113 | {%- when Type::Int32 %} 114 | {% include "arith_conv.hpp" %} 115 | {%- when Type::UInt64 %} 116 | {% include "arith_conv.hpp" %} 117 | {%- when Type::Int64 %} 118 | {% include "arith_conv.hpp" %} 119 | {%- when Type::Float32 %} 120 | {% include "arith_conv.hpp" %} 121 | {%- when Type::Float64 %} 122 | {% include "arith_conv.hpp" %} 123 | {%- when Type::Bytes %} 124 | {% include "bytes_conv.hpp" %} 125 | {%- when Type::String %} 126 | {% include "str_conv.hpp" %} 127 | {%- when Type::Timestamp %} 128 | {% include "timestamp_conv.hpp" %} 129 | {%- when Type::Duration %} 130 | {% include "duration_conv.hpp" %} 131 | {%- when Type::Enum { module_path, name } %} 132 | {%- let e = ci|get_enum_definition(name) %} 133 | {%- if ci.is_name_used_as_error(name) %} 134 | {% include "err_conv.hpp" %} 135 | {%- else %} 136 | {% include "enum_conv.hpp" %} 137 | {%- endif %} 138 | {%- when Type::Object { module_path, name, imp } %} 139 | {% include "obj_conv.hpp" %} 140 | {%- when Type::Record { module_path, name } %} 141 | {% include "rec_conv.hpp" %} 142 | {%- when Type::Optional { inner_type } %} 143 | {% include "opt_conv.hpp" %} 144 | {%- when Type::Sequence { inner_type } %} 145 | {% include "seq_conv.hpp" %} 146 | {%- when Type::Map { key_type, value_type } %} 147 | {% include "map_conv.hpp" %} 148 | {%- when Type::CallbackInterface { module_path, name } %} 149 | {% include "callback_conv.hpp" %} 150 | {%- when Type::Custom { module_path, name, builtin } %} 151 | {% include "custom.hpp" %} 152 | {%- else %} 153 | {%- endmatch %} 154 | {%- endfor %} 155 | } // namespace uniffi 156 | 157 | {%~ for func in ci.function_definitions() %} 158 | {%- call macros::docstring(func, 0) %} 159 | {%- match func.return_type() %} 160 | {%- when Some with (return_type) %} 161 | {{ return_type|type_name(ci) }} {{ func.name()|fn_name }}({% call macros::param_list(func) %}); 162 | {%- when None %} 163 | void {{ func.name()|fn_name }}({% call macros::param_list(func) %}); 164 | {%- endmatch %} 165 | {%- endfor %} 166 | } // namespace {{ namespace }} 167 | -------------------------------------------------------------------------------- /bindgen/src/bindings/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod cpp; 2 | -------------------------------------------------------------------------------- /bindgen/src/main.rs: -------------------------------------------------------------------------------- 1 | mod bindings; 2 | 3 | use anyhow::Context; 4 | use camino::Utf8PathBuf; 5 | use clap::Parser; 6 | 7 | use bindings::cpp::CppBindingGenerator; 8 | 9 | #[derive(Parser)] 10 | struct Args { 11 | #[clap(long, short)] 12 | config: Option, 13 | #[clap(long, short)] 14 | out_dir: Option, 15 | #[clap(long)] 16 | lib_file: Option, 17 | #[clap(long = "library", conflicts_with_all = ["config", "lib_file"], requires = "out_dir")] 18 | library_mode: bool, 19 | #[clap(long = "scaffolding")] 20 | scaffolding_mode: bool, 21 | #[clap(long = "crate")] 22 | crate_name: Option, 23 | source: Utf8PathBuf, 24 | } 25 | 26 | fn main() { 27 | let args = Args::parse(); 28 | 29 | if args.library_mode { 30 | let config_supplier = { 31 | use uniffi_bindgen::cargo_metadata::CrateConfigSupplier; 32 | let cmd = ::cargo_metadata::MetadataCommand::new(); 33 | let metadata = cmd.exec().context("error running cargo metadata").unwrap(); 34 | CrateConfigSupplier::from(metadata) 35 | }; 36 | 37 | uniffi_bindgen::library_mode::generate_bindings( 38 | &args.source, 39 | args.crate_name, 40 | &CppBindingGenerator { 41 | scaffolding_mode: args.scaffolding_mode, 42 | }, 43 | &config_supplier, 44 | args.config.as_deref(), 45 | &args.out_dir.unwrap(), 46 | false, 47 | ) 48 | .context("Failed to generate bindings using library mode") 49 | .unwrap(); 50 | } else { 51 | uniffi_bindgen::generate_external_bindings( 52 | &CppBindingGenerator { 53 | scaffolding_mode: args.scaffolding_mode, 54 | }, 55 | args.source, 56 | args.config.as_deref(), 57 | args.out_dir, 58 | args.lib_file, 59 | args.crate_name.as_deref(), 60 | false, 61 | ) 62 | .context("Failed to generate external bindings") 63 | .unwrap(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /build_scaffolding_lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | cargo build --release --package uniffi-bindgen-cpp 5 | 6 | mkdir -p cpp-tests/build 7 | cd cpp-tests/build 8 | cmake -DCMAKE_BUILD_TYPE=Debug .. 9 | make uniffi_fixtures 10 | -------------------------------------------------------------------------------- /cpp-tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(binding-tests VERSION 1.0.0 LANGUAGES CXX) 3 | set(CMAKE_CXX_STANDARD 20) 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | set(CMAKE_CXX_EXTENSIONS OFF) 6 | 7 | include(CTest) 8 | enable_testing() 9 | 10 | find_package(Threads REQUIRED) 11 | 12 | set(BINDINGS_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../target/debug) 13 | set(BINDINGS_SRC_DIR ${BINDINGS_BUILD_DIR}/bindings) 14 | 15 | if (WIN32) 16 | set(UNIFFI_FIXTURES_LIB uniffi_bindgen_cpp_fixtures.dll) 17 | set(ADDITIONAL_LIBS ws2_32 userenv advapi32 ntdll crypt32 Bcrypt) 18 | elseif(UNIX AND NOT APPLE) 19 | set(UNIFFI_FIXTURES_LIB libuniffi_bindgen_cpp_fixtures.so) 20 | set(ADDITIONAL_LIBS) 21 | else() 22 | set(UNIFFI_FIXTURES_LIB libuniffi_bindgen_cpp_fixtures.dylib) 23 | set(ADDITIONAL_LIBS) 24 | endif() 25 | 26 | find_program(VALGRIND "valgrind") 27 | macro(memcheck_test TEST_NAME) 28 | if (VALGRIND) 29 | add_test(NAME ${TEST_NAME}-test-memcheck 30 | COMMAND valgrind 31 | --error-exitcode=1 32 | --tool=memcheck 33 | --leak-check=full 34 | --errors-for-leak-kinds=definite 35 | --show-leak-kinds=definite $) 36 | endif() 37 | endmacro(memcheck_test) 38 | 39 | # Add a bindings test case 40 | macro(test_case TEST_NAME) 41 | add_executable(${TEST_NAME}-test tests/${TEST_NAME}/main.cpp ${BINDINGS_SRC_DIR}/${TEST_NAME}.cpp) 42 | 43 | target_include_directories(${TEST_NAME}-test PRIVATE ${BINDINGS_SRC_DIR} include) 44 | target_link_directories(${TEST_NAME}-test PRIVATE ${BINDINGS_BUILD_DIR}) 45 | target_link_libraries(${TEST_NAME}-test uniffi_bindgen_cpp_fixtures Threads::Threads ${ADDITIONAL_LIBS}) 46 | target_compile_definitions(${TEST_NAME}-test PRIVATE UNIFFI_BINDING_DIR="${BINDINGS_SRC_DIR}") 47 | 48 | add_test(NAME ${TEST_NAME}-test COMMAND ${TEST_NAME}-test) 49 | memcheck_test(${TEST_NAME}-test) 50 | 51 | add_dependencies(${TEST_NAME}-test bindings) 52 | 53 | list(APPEND BINDING_FILES ${BINDINGS_SRC_DIR}/${TEST_NAME}.cpp) 54 | 55 | endmacro(test_case) 56 | 57 | test_case(arithmetic) 58 | test_case(callbacks) 59 | test_case(fixture_callbacks) 60 | test_case(chronological) 61 | test_case(custom_types) 62 | test_case(geometry) 63 | test_case(rondpoint) 64 | test_case(sprites) 65 | test_case(todolist) 66 | test_case(traits) 67 | test_case(coverall) 68 | test_case(uniffi_docstring) 69 | test_case(trait_methods) 70 | test_case(custom_types_builtin) 71 | test_case(enum_style_test) 72 | test_case(empty_type) 73 | test_case(error_types_builtin) 74 | 75 | # Special multilib test that only needs to compile to check for potential symbol clashes 76 | add_executable(multilib-test 77 | tests/multilib/main.cpp 78 | ${BINDINGS_SRC_DIR}/arithmetic.cpp 79 | ${BINDINGS_SRC_DIR}/coverall.cpp 80 | ${BINDINGS_SRC_DIR}/sprites.cpp 81 | ) 82 | target_include_directories(multilib-test PRIVATE ${BINDINGS_SRC_DIR} include) 83 | target_link_directories(multilib-test PRIVATE ${BINDINGS_BUILD_DIR}) 84 | target_link_libraries(multilib-test uniffi_bindgen_cpp_fixtures Threads::Threads ${ADDITIONAL_LIBS}) 85 | add_dependencies(multilib-test bindings) 86 | 87 | add_custom_target(libs ALL 88 | BYPRODUCTS ${BINDING_FILES} 89 | COMMAND cargo build 90 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../ 91 | COMMENT "Generating libs" 92 | ) 93 | 94 | add_custom_target(bindings ALL 95 | DEPENDS libs 96 | COMMAND ./uniffi-bindgen-cpp --library ${UNIFFI_FIXTURES_LIB} --out-dir bindings 97 | WORKING_DIRECTORY ${BINDINGS_BUILD_DIR} 98 | COMMENT "Generating bindings" 99 | ) 100 | -------------------------------------------------------------------------------- /cpp-tests/include/custom_string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct CustomString { 6 | std::string string; 7 | 8 | CustomString(const std::string &string): string(string) { } 9 | 10 | std::string to_string() const { 11 | return this->string; 12 | } 13 | }; -------------------------------------------------------------------------------- /cpp-tests/include/test_common.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define ASSERT_EQ(a, b) assert((a) == (b)) 7 | #define ASSERT_NE(a, b) assert((a) != (b)) 8 | #define ASSERT_TRUE(a) assert((a)) 9 | #define ASSERT_FALSE(a) assert(!(a)) 10 | 11 | #define EXPECT_EXCEPTION(expr, exception) \ 12 | try { \ 13 | expr; \ 14 | assert(false && "Didn't throw an exception"); \ 15 | } catch (exception const &) { \ 16 | } catch (...) { \ 17 | assert(false && "Exception didn't match" #exception); \ 18 | } 19 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_arithmetic.hpp" 2 | 3 | uint64_t arithmetic::add(uint64_t a, uint64_t b) { 4 | return a + b; 5 | } 6 | 7 | uint64_t arithmetic::sub(uint64_t a, uint64_t b) { 8 | if (a < b) { 9 | throw arithmetic_error::IntegerOverflow(); 10 | } 11 | 12 | return a - b; 13 | } 14 | 15 | uint64_t arithmetic::div(uint64_t a, uint64_t b) { 16 | if (b == 0) { 17 | throw std::runtime_error("division by zero"); 18 | } 19 | 20 | return a / b; 21 | } 22 | 23 | int8_t arithmetic::equal(uint64_t a, uint64_t b) { 24 | return a == b; 25 | } 26 | 27 | #include 28 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/arithmetic/lib_arithmetic.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace { 5 | namespace arithmetic { 6 | namespace arithmetic_error { 7 | class IntegerOverflow: public std::runtime_error { 8 | public: 9 | IntegerOverflow(): std::runtime_error("Integer overflow") { } 10 | }; 11 | }; 12 | 13 | uint64_t add(uint64_t a, uint64_t b); 14 | uint64_t sub(uint64_t a, uint64_t b); 15 | uint64_t div(uint64_t a, uint64_t b); 16 | int8_t equal(uint64_t a, uint64_t b); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/callbacks/lib_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_callbacks.hpp" 2 | 3 | std::string callbacks::Telephone::call(std::shared_ptr answerer) { 4 | try { 5 | return answerer->answer(); 6 | } catch (telephone_error::Busy& e) { 7 | throw e; 8 | } catch (std::runtime_error&) { 9 | throw telephone_error::InternalTelephoneError(); 10 | } 11 | } 12 | 13 | #include 14 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/callbacks/lib_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | namespace callbacks { 7 | namespace telephone_error { 8 | class Busy : public std::runtime_error { 9 | public: 10 | Busy() : std::runtime_error("I'm busy") {} 11 | }; 12 | 13 | class InternalTelephoneError : public std::runtime_error { 14 | public: 15 | InternalTelephoneError() : std::runtime_error("Internal telephone error") {} 16 | }; 17 | } 18 | 19 | class CallAnswerer { 20 | public: 21 | virtual std::string answer() = 0; 22 | 23 | virtual ~CallAnswerer() = default; 24 | }; 25 | 26 | class Telephone { 27 | public: 28 | Telephone() = default; 29 | 30 | std::string call(std::shared_ptr answerer); 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/chronological/lib_chronological.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_chronological.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | chronological::timestamp chronological::return_timestamp(chronological::timestamp a) { 8 | return a; 9 | } 10 | 11 | chronological::duration chronological::return_duration(chronological::duration a) { 12 | return a; 13 | } 14 | 15 | std::string chronological::to_string_timestamp(chronological::timestamp a) { 16 | using time_point = std::chrono::system_clock::time_point; 17 | std::time_t time = std::chrono::system_clock::to_time_t(time_point {std::chrono::duration_cast(a.time_since_epoch())}); 18 | auto ns = std::chrono::duration_cast(a.time_since_epoch()).count() % 1000000000; 19 | if (ns < 0) { 20 | ns += 1000000000; 21 | time -= 1; 22 | } 23 | 24 | std::tm tm = *std::gmtime(&time); 25 | std::stringstream ss; 26 | ss << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S"); 27 | ss<< "." << std::setfill('0') << std::setw(9) << ns; 28 | ss << "Z"; 29 | 30 | return ss.str(); 31 | } 32 | 33 | chronological::timestamp chronological::get_pre_epoch_timestamp() { 34 | return std::chrono::time_point::min(); 35 | } 36 | 37 | chronological::timestamp chronological::add(chronological::timestamp a, chronological::duration b) { 38 | return std::chrono::time_point_cast(a + b); 39 | } 40 | 41 | chronological::duration chronological::diff(chronological::timestamp a, chronological::timestamp b) { 42 | if (a < b) { 43 | throw chronological_error::TimeDiffError(); 44 | } 45 | return a - b; 46 | } 47 | 48 | chronological::timestamp chronological::now() { 49 | return std::chrono::system_clock::now(); 50 | } 51 | 52 | bool chronological::equal(chronological::timestamp a, chronological::timestamp b) { 53 | return a == b; 54 | } 55 | 56 | bool chronological::optional(std::optional a, std::optional b) { 57 | return a.has_value() && b.has_value(); 58 | } 59 | 60 | uint64_t chronological::get_seconds_before_unix_epoch(chronological::timestamp a) { 61 | return std::chrono::duration_cast(a.time_since_epoch()).count(); 62 | } 63 | 64 | chronological::timestamp chronological::set_seconds_before_unix_epoch(uint64_t seconds) { 65 | return std::chrono::time_point(std::chrono::seconds(seconds)); 66 | } 67 | 68 | #include 69 | 70 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/chronological/lib_chronological.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace { 7 | namespace chronological { 8 | typedef std::chrono::time_point timestamp; 9 | typedef std::chrono::duration duration; 10 | 11 | namespace chronological_error { 12 | class TimeOverflow: public std::runtime_error { 13 | public: 14 | TimeOverflow(): std::runtime_error("Time overflow") { } 15 | }; 16 | 17 | class TimeDiffError: public std::runtime_error { 18 | public: 19 | TimeDiffError(): std::runtime_error("Time Diff error") { } 20 | }; 21 | }; 22 | 23 | timestamp return_timestamp(timestamp a); 24 | duration return_duration(duration a); 25 | std::string to_string_timestamp(timestamp a); 26 | timestamp get_pre_epoch_timestamp(); 27 | timestamp add(timestamp a, duration b); 28 | duration diff(timestamp a, timestamp b); 29 | timestamp now(); 30 | bool equal(timestamp a, timestamp b); 31 | bool optional(std::optional a, std::optional b); 32 | uint64_t get_seconds_before_unix_epoch(timestamp a); 33 | timestamp set_seconds_before_unix_epoch(uint64_t seconds); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/custom_types/lib_custom_types.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_custom_types.hpp" 2 | 3 | custom_types::CustomTypesDemo custom_types::get_custom_types_demo(std::optional demo) { 4 | return demo.value_or(custom_types::CustomTypesDemo{ 5 | .url = "http://example.com/", 6 | .handle = 123, 7 | .time_interval_ms = 456000, 8 | .time_interval_sec_dbl = 456.0, 9 | .time_interval_sec_flt = 777.0f, 10 | }); 11 | } 12 | 13 | custom_types::ExampleCustomType custom_types::get_example_custom_type() { 14 | return "abadidea"; 15 | } 16 | 17 | #include 18 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/custom_types/lib_custom_types.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | namespace custom_types { 7 | typedef std::string ExampleCustomType; 8 | struct UniffiCustomTypeConverterExampleCustomType { 9 | static ExampleCustomType into_custom(std::string val) { 10 | return val; 11 | } 12 | 13 | static std::string from_custom(ExampleCustomType val) { 14 | return val; 15 | } 16 | }; 17 | 18 | typedef std::string Url; 19 | struct UniffiCustomTypeConverterUrl { 20 | static Url into_custom(std::string val) { 21 | return val; 22 | } 23 | 24 | static std::string from_custom(Url val) { 25 | return val; 26 | } 27 | }; 28 | 29 | typedef int64_t Handle; 30 | struct UniffiCustomTypeConverterHandle { 31 | static Handle into_custom(int64_t val) { 32 | return val; 33 | } 34 | 35 | static int64_t from_custom(Handle val) { 36 | return val; 37 | } 38 | }; 39 | 40 | typedef int64_t TimeIntervalMs; 41 | struct UniffiCustomTypeConverterTimeIntervalMs { 42 | static TimeIntervalMs into_custom(int64_t val) { 43 | return val; 44 | } 45 | 46 | static int64_t from_custom(TimeIntervalMs val) { 47 | return val; 48 | } 49 | }; 50 | 51 | typedef double TimeIntervalSecDbl; 52 | struct UniffiCustomTypeConverterTimeIntervalSecDbl { 53 | static TimeIntervalSecDbl into_custom(double val) { 54 | return val; 55 | } 56 | 57 | static double from_custom(TimeIntervalSecDbl val) { 58 | return val; 59 | } 60 | }; 61 | 62 | typedef float TimeIntervalSecFlt; 63 | struct UniffiCustomTypeConverterTimeIntervalSecFlt { 64 | static TimeIntervalSecFlt into_custom(float val) { 65 | return val; 66 | } 67 | 68 | static float from_custom(TimeIntervalSecFlt val) { 69 | return val; 70 | } 71 | }; 72 | 73 | struct CustomTypesDemo { 74 | Url url; 75 | Handle handle; 76 | TimeIntervalMs time_interval_ms; 77 | TimeIntervalSecDbl time_interval_sec_dbl; 78 | TimeIntervalSecFlt time_interval_sec_flt; 79 | }; 80 | 81 | CustomTypesDemo get_custom_types_demo(std::optional demo); 82 | ExampleCustomType get_example_custom_type(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/custom_types_builtin/lib_custom_types_builtin.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_custom_types_builtin.hpp" 2 | 3 | #include 4 | 5 | custom_types_builtin::CustomTypesBuiltin custom_types_builtin::get_custom_types_builtin() { 6 | auto custom_string = custom_types_builtin::CustomString{"Custom string"}; 7 | auto array = std::vector{"Hello, world!"}; 8 | auto bytes = std::vector{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 9 | auto table = std::unordered_map{{"hello", "world"}}; 10 | 11 | auto result = custom_types_builtin::CustomTypesBuiltin{ 12 | .string = "Hello, world!", 13 | .custom_string = custom_string, 14 | .array = array, 15 | .bytes = bytes, 16 | .table = table, 17 | .boolean = true, 18 | .int8 = std::numeric_limits::max(), 19 | .int16 = std::numeric_limits::max(), 20 | .int32 = std::numeric_limits::max(), 21 | .int64 = std::numeric_limits::max(), 22 | .uint8 = std::numeric_limits::max(), 23 | .uint16 = std::numeric_limits::max(), 24 | .uint32 = std::numeric_limits::max(), 25 | .uint64 = std::numeric_limits::max(), 26 | .flt = std::numeric_limits::max(), 27 | .dbl = std::numeric_limits::max() 28 | }; 29 | 30 | return result; 31 | } 32 | 33 | custom_types_builtin::CustomTypesBuiltin custom_types_builtin::return_custom_types_builtin(custom_types_builtin::CustomTypesBuiltin type) { 34 | return type; 35 | } 36 | 37 | #include 38 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/custom_types_builtin/lib_custom_types_builtin.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DEFINE_CUSTOM_TYPE(custom, underlying) \ 7 | typedef underlying custom; \ 8 | struct UniffiCustomTypeConverter##custom { \ 9 | static custom into_custom(underlying val) { return val; } \ 10 | static underlying from_custom(custom val) { return val; } \ 11 | }; 12 | 13 | namespace { 14 | namespace custom_types_builtin { 15 | typedef std::unordered_map StringMap; 16 | 17 | DEFINE_CUSTOM_TYPE(MyString, std::string) 18 | DEFINE_CUSTOM_TYPE(CustomString, std::string) 19 | DEFINE_CUSTOM_TYPE(Array, std::vector) 20 | DEFINE_CUSTOM_TYPE(Bytes, std::vector) 21 | DEFINE_CUSTOM_TYPE(Table, StringMap) 22 | DEFINE_CUSTOM_TYPE(Boolean, bool) 23 | DEFINE_CUSTOM_TYPE(Int8, int8_t) 24 | DEFINE_CUSTOM_TYPE(Int16, int16_t) 25 | DEFINE_CUSTOM_TYPE(Int32, int32_t) 26 | DEFINE_CUSTOM_TYPE(Int64, int64_t) 27 | DEFINE_CUSTOM_TYPE(UInt8, uint8_t) 28 | DEFINE_CUSTOM_TYPE(UInt16, uint16_t) 29 | DEFINE_CUSTOM_TYPE(UInt32, uint32_t) 30 | DEFINE_CUSTOM_TYPE(UInt64, uint64_t) 31 | DEFINE_CUSTOM_TYPE(Float, float) 32 | DEFINE_CUSTOM_TYPE(Double, double) 33 | 34 | struct CustomTypesBuiltin { 35 | MyString string; 36 | CustomString custom_string; 37 | Array array; 38 | Bytes bytes; 39 | Table table; 40 | Boolean boolean; 41 | Int8 int8; 42 | Int16 int16; 43 | Int32 int32; 44 | Int64 int64; 45 | UInt8 uint8; 46 | UInt16 uint16; 47 | UInt32 uint32; 48 | UInt64 uint64; 49 | Float flt; 50 | Double dbl; 51 | }; 52 | 53 | CustomTypesBuiltin get_custom_types_builtin(); 54 | CustomTypesBuiltin return_custom_types_builtin(CustomTypesBuiltin type); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/empty_type/lib_empty_type.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | empty_type::Empty empty_type::get_empty_type() { 4 | return Empty {}; 5 | } 6 | 7 | void empty_type::send_empty_type(Empty e) {} 8 | 9 | #include 10 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/empty_type/lib_empty_type.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace { 4 | namespace empty_type { 5 | struct Empty {}; 6 | 7 | Empty get_empty_type(); 8 | 9 | void send_empty_type(Empty e); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/enum_style_test/lib_enum_style_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | enum_style_test::SimpleEnum enum_style_test::get_simple_enum() { 4 | return enum_style_test::SimpleEnum::VARIANT_ONE; 5 | } 6 | 7 | void enum_style_test::set_simple_enum(enum_style_test::SimpleEnum) { 8 | } 9 | 10 | enum_style_test::ComplexEnum enum_style_test::get_complex_enum() { 11 | return enum_style_test::ComplexEnum { enum_style_test::ComplexEnum::VARIANT_ONE { 1 } }; 12 | } 13 | 14 | void enum_style_test::set_complex_enum(enum_style_test::ComplexEnum) { 15 | } 16 | 17 | 18 | #include 19 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/enum_style_test/lib_enum_style_test.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace { 5 | namespace enum_style_test { 6 | enum class SimpleEnum: int32_t { 7 | VARIANT_ONE = 1, 8 | VARIANT_TWO = 2, 9 | VARIANT_THREE = 3 10 | }; 11 | 12 | 13 | class ComplexEnum { 14 | public: 15 | struct VARIANT_ONE { uint32_t num; }; 16 | struct VARIANT_TWO { float flt; }; 17 | 18 | ComplexEnum(ComplexEnum::VARIANT_ONE variant) { this->variant = variant; } 19 | ComplexEnum(ComplexEnum::VARIANT_TWO variant) { this->variant = variant; } 20 | 21 | std::variant variant; 22 | }; 23 | 24 | SimpleEnum get_simple_enum(); 25 | void set_simple_enum(SimpleEnum e); 26 | 27 | ComplexEnum get_complex_enum(); 28 | void set_complex_enum(ComplexEnum e); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_fixture_callbacks.hpp" 2 | 3 | bool fixture_callbacks::RustGetters::get_bool(std::shared_ptr foreign, bool v, bool argument_two) { 4 | return foreign->get_bool(v, argument_two); 5 | } 6 | 7 | std::string fixture_callbacks::RustGetters::get_string(std::shared_ptr foreign, std::string v, bool argument_two) { 8 | try { 9 | return foreign->get_string(v, argument_two); 10 | } catch (const fixture_callbacks::simple_error::BadArgument &e) { 11 | throw e; 12 | } catch (const std::runtime_error &e) { 13 | throw fixture_callbacks::simple_error::UnexpectedError(e.what()); 14 | } 15 | } 16 | 17 | std::optional fixture_callbacks::RustGetters::get_option(std::shared_ptr foreign, std::optional v, bool argument_two) { 18 | try { 19 | return foreign->get_option(v, argument_two); 20 | } catch (const fixture_callbacks::complex_error::ReallyBadArgument &e) { 21 | throw e; 22 | } catch (const fixture_callbacks::complex_error::UnexpectedErrorWithReason &e) { 23 | throw fixture_callbacks::complex_error::UnexpectedErrorWithReason(e.reason); 24 | } catch (const std::runtime_error &e) { 25 | throw fixture_callbacks::complex_error::UnexpectedErrorWithReason(e.what()); 26 | } 27 | } 28 | 29 | std::vector fixture_callbacks::RustGetters::get_list(std::shared_ptr foreign, std::vector v, bool argument_two) { 30 | return foreign->get_list(v, argument_two); 31 | } 32 | 33 | std::optional fixture_callbacks::RustGetters::get_string_optional_callback(std::shared_ptr foreign, std::string v, bool argument_two) { 34 | if (foreign) { 35 | try { 36 | return foreign->get_string(v, argument_two); 37 | } catch (const fixture_callbacks::simple_error::BadArgument &e) { 38 | throw e; 39 | } catch (const std::runtime_error&) { 40 | throw fixture_callbacks::simple_error::UnexpectedError(); 41 | } 42 | } 43 | 44 | return std::nullopt; 45 | } 46 | 47 | void fixture_callbacks::RustGetters::get_nothing(std::shared_ptr foreign, std::string v) { 48 | try { 49 | return foreign->get_nothing(v); 50 | } catch (const fixture_callbacks::simple_error::BadArgument &e) { 51 | throw e; 52 | } catch (const std::runtime_error &e) { 53 | throw fixture_callbacks::simple_error::UnexpectedError(e.what()); 54 | } 55 | } 56 | 57 | std::string fixture_callbacks::RustStringifier::from_simple_type(uint32_t v) { 58 | return this->foreign->from_simple_type(v); 59 | } 60 | 61 | std::string fixture_callbacks::RustStringifier::from_complex_type(std::optional>> values) { 62 | return this->foreign->from_complex_type(values); 63 | } 64 | 65 | 66 | #include 67 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/fixture_callbacks/lib_fixture_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace { 9 | namespace fixture_callbacks { 10 | class SimpleError: public std::runtime_error { 11 | public: 12 | SimpleError() : std::runtime_error("") {} 13 | SimpleError(const std::string &what_arg) : std::runtime_error(what_arg) {} 14 | }; 15 | 16 | namespace simple_error { 17 | class BadArgument : public SimpleError { 18 | public: 19 | BadArgument() : SimpleError("BadArgument") {} 20 | BadArgument(const std::string &what_arg) : SimpleError(what_arg) {} 21 | }; 22 | 23 | class UnexpectedError : public SimpleError { 24 | public: 25 | UnexpectedError() : SimpleError("UnexpectedError") {} 26 | UnexpectedError(const std::string &what_arg) : SimpleError(what_arg) {} 27 | }; 28 | } 29 | 30 | class ComplexError: public std::runtime_error { 31 | public: 32 | ComplexError() : std::runtime_error("") {} 33 | ComplexError(const std::string &what_arg) : std::runtime_error(what_arg) {} 34 | }; 35 | 36 | namespace complex_error { 37 | class ReallyBadArgument : public ComplexError { 38 | public: 39 | ReallyBadArgument() : ComplexError("ReallyBadArgument") {} 40 | ReallyBadArgument(const std::string &what_arg) : ComplexError(what_arg) {} 41 | 42 | uint32_t code; 43 | }; 44 | 45 | class UnexpectedErrorWithReason : public ComplexError { 46 | public: 47 | UnexpectedErrorWithReason() : ComplexError("InternalTelephoneError") {} 48 | UnexpectedErrorWithReason(const std::string &what_arg) : ComplexError(what_arg), reason(what_arg) {} 49 | 50 | std::string reason; 51 | }; 52 | } 53 | 54 | class ForeignGetters { 55 | public: 56 | virtual ~ForeignGetters() = default; 57 | 58 | virtual bool get_bool(bool v, bool argument_two) = 0; 59 | virtual std::string get_string(std::string v, bool argument_two) = 0; 60 | virtual std::optional get_option(std::optional v, bool argument_two) = 0; 61 | virtual std::vector get_list(std::vector v, bool argument_two) = 0; 62 | virtual void get_nothing(std::string v) = 0; 63 | }; 64 | 65 | class RustGetters { 66 | public: 67 | RustGetters() = default; 68 | 69 | bool get_bool(std::shared_ptr foreign, bool v, bool argument_two); 70 | std::string get_string(std::shared_ptr foreign, std::string v, bool argument_two); 71 | std::optional get_option(std::shared_ptr foreign, std::optional v, bool argument_two); 72 | std::vector get_list(std::shared_ptr foreign, std::vector v, bool argument_two); 73 | std::optional get_string_optional_callback(std::shared_ptr callback, std::string v, bool argument_two); 74 | void get_nothing(std::shared_ptr foreign, std::string v); 75 | }; 76 | 77 | class StoredForeignStringifier { 78 | public: 79 | virtual ~StoredForeignStringifier() = default; 80 | 81 | virtual std::string from_simple_type(int32_t v) = 0; 82 | virtual std::string from_complex_type(std::optional>> values) = 0; 83 | }; 84 | 85 | class RustStringifier { 86 | public: 87 | RustStringifier(std::shared_ptr foreign) : foreign(foreign) {} 88 | 89 | std::string from_simple_type(uint32_t v); 90 | std::string from_complex_type(std::optional>> values); 91 | private: 92 | std::shared_ptr foreign; 93 | }; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/geometry/lib_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_geometry.hpp" 2 | 3 | double geometry::gradient(geometry::Line line) { 4 | return (line.end.coord_y - line.start.coord_y) / (line.end.coord_x - line.start.coord_x); 5 | } 6 | 7 | std::optional geometry::intersection(geometry::Line line1, geometry::Line line2) { 8 | double m1 = gradient(line1); 9 | double m2 = gradient(line2); 10 | double c1 = line1.start.coord_y - m1 * line1.start.coord_x; 11 | double c2 = line2.start.coord_y - m2 * line2.start.coord_x; 12 | if (m1 == m2) { 13 | return std::nullopt; 14 | } 15 | double x = (c2 - c1) / (m1 - m2); 16 | double y = m1 * x + c1; 17 | return geometry::Point{x, y}; 18 | } 19 | 20 | #include -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/geometry/lib_geometry.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace { 4 | namespace geometry { 5 | struct Point { 6 | double coord_x; 7 | double coord_y; 8 | }; 9 | 10 | struct Line { 11 | Point start; 12 | Point end; 13 | }; 14 | 15 | double gradient(Line line); 16 | std::optional intersection(Line line1, Line line2); 17 | } 18 | } -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/sprites/lib_sprites.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_sprites.hpp" 2 | 3 | sprites::Point sprites::translate(Point position, Vector direction) { 4 | return sprites::Point { position.x + direction.dx, position.y + direction.dy }; 5 | } 6 | 7 | sprites::Sprite::Sprite(std::optional initial_position) : position { initial_position.value_or(Point { 0.0, 0.0 }) } {} 8 | 9 | sprites::Sprite::Sprite(Point reference, Vector direction) : position { translate(reference, direction) } {} 10 | 11 | sprites::Point sprites::Sprite::get_position() { 12 | return position; 13 | } 14 | 15 | void sprites::Sprite::move_to(Point point) { 16 | position = point; 17 | } 18 | 19 | void sprites::Sprite::move_by(Vector vector) { 20 | position = translate(position, vector); 21 | } 22 | 23 | #include 24 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/sprites/lib_sprites.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace { 4 | namespace sprites { 5 | struct Point { 6 | double x; 7 | double y; 8 | }; 9 | 10 | struct Vector { 11 | double dx; 12 | double dy; 13 | }; 14 | 15 | struct Sprite { 16 | Point position; 17 | 18 | Sprite(std::optional initial_position); 19 | Sprite(Point reference, Vector direction); 20 | 21 | Point get_position(); 22 | void move_to(Point point); 23 | void move_by(Vector vector); 24 | }; 25 | 26 | Point translate(Point point, Vector vector); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/todolist/lib_todolist.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_todolist.hpp" 2 | 3 | #include 4 | 5 | std::shared_ptr todolist::get_default_list() { 6 | std::lock_guard lock(todolist::default_list_mutex); 7 | return todolist::default_list; 8 | } 9 | 10 | void todolist::set_default_list(std::shared_ptr list) { 11 | std::lock_guard lock(todolist::default_list_mutex); 12 | todolist::default_list = list; 13 | } 14 | 15 | todolist::TodoEntry todolist::create_entry_with(const std::string &todo) { 16 | if (todo.empty()) { 17 | throw todolist::todo_error::EmptyString("Cannot add empty string as entry"); 18 | } 19 | 20 | return todolist::TodoEntry{todo}; 21 | } 22 | 23 | void todolist::TodoList::add_item(const std::string &todo) { 24 | if (todo.empty()) { 25 | throw todolist::todo_error::EmptyString("Cannot add empty string as item"); 26 | } 27 | 28 | std::lock_guard lock(this->items_mutex); 29 | if (std::find(this->items.begin(), this->items.end(), todo) != this->items.end()) { 30 | throw todolist::todo_error::DuplicateTodo("Item already exists"); 31 | } 32 | 33 | this->items.push_back(todo); 34 | } 35 | 36 | void todolist::TodoList::add_entry(const todolist::TodoEntry &entry) { 37 | this->add_item(entry.text); 38 | } 39 | 40 | std::vector todolist::TodoList::get_entries() { 41 | std::lock_guard lock(this->items_mutex); 42 | std::vector entries; 43 | for (const auto &item : this->items) { 44 | entries.push_back(todolist::TodoEntry{item}); 45 | } 46 | return entries; 47 | } 48 | 49 | std::vector todolist::TodoList::get_items() { 50 | std::lock_guard lock(this->items_mutex); 51 | return this->items; 52 | } 53 | 54 | void todolist::TodoList::add_entries(const std::vector &entries) { 55 | for (const auto &entry : entries) { 56 | this->add_entry(entry); 57 | } 58 | } 59 | 60 | void todolist::TodoList::add_items(const std::vector &items) { 61 | for (const auto &item : items) { 62 | this->add_item(item); 63 | } 64 | } 65 | 66 | todolist::TodoEntry todolist::TodoList::get_last_entry() { 67 | std::lock_guard lock(this->items_mutex); 68 | if (this->items.empty()) { 69 | throw todolist::todo_error::EmptyTodoList("List is empty"); 70 | } 71 | 72 | return todolist::TodoEntry{this->items.back()}; 73 | } 74 | 75 | std::string todolist::TodoList::get_last() { 76 | return this->get_last_entry().text; 77 | } 78 | 79 | std::string todolist::TodoList::get_first() { 80 | std::lock_guard lock(this->items_mutex); 81 | if (this->items.empty()) { 82 | throw todolist::todo_error::EmptyTodoList("List is empty"); 83 | } 84 | 85 | return this->items.front(); 86 | } 87 | 88 | void todolist::TodoList::clear_item(const std::string &todo) { 89 | std::lock_guard lock(this->items_mutex); 90 | auto it = std::find(this->items.begin(), this->items.end(), todo); 91 | if (it == this->items.end()) { 92 | throw todolist::todo_error::TodoDoesNotExist("Item not found"); 93 | } 94 | 95 | this->items.erase(it); 96 | } 97 | 98 | void todolist::TodoList::make_default(const std::shared_ptr &self) { 99 | todolist::set_default_list(self); 100 | } 101 | 102 | #include 103 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/todolist/lib_todolist.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace { 8 | namespace todolist { 9 | class TodoError: public std::runtime_error { 10 | public: 11 | TodoError() : std::runtime_error("") {} 12 | TodoError(const std::string &what_arg) : std::runtime_error(what_arg) {} 13 | }; 14 | 15 | namespace todo_error { 16 | class TodoDoesNotExist: public TodoError { 17 | public: 18 | TodoDoesNotExist() : TodoError("") {} 19 | TodoDoesNotExist(const std::string &what_arg) : TodoError(what_arg) {} 20 | }; 21 | 22 | class EmptyTodoList: public TodoError { 23 | public: 24 | EmptyTodoList() : TodoError("") {} 25 | EmptyTodoList(const std::string &what_arg) : TodoError(what_arg) {} 26 | }; 27 | 28 | class DuplicateTodo: public TodoError { 29 | public: 30 | DuplicateTodo() : TodoError("") {} 31 | DuplicateTodo(const std::string &what_arg) : TodoError(what_arg) {} 32 | }; 33 | 34 | class EmptyString: public TodoError { 35 | public: 36 | EmptyString() : TodoError("") {} 37 | EmptyString(const std::string &what_arg) : TodoError(what_arg) {} 38 | }; 39 | 40 | class DeligatedError: public TodoError { 41 | public: 42 | DeligatedError() : TodoError("") {} 43 | DeligatedError(const std::string &what_arg) : TodoError(what_arg) {} 44 | }; 45 | } 46 | 47 | struct TodoEntry { 48 | std::string text; 49 | }; 50 | 51 | class TodoList { 52 | public: 53 | TodoList() = default; 54 | 55 | void add_item(const std::string &item); 56 | void add_entry(const TodoEntry &entry); 57 | std::vector get_entries(); 58 | std::vector get_items(); 59 | void add_entries(const std::vector &entries); 60 | void add_items(const std::vector &items); 61 | TodoEntry get_last_entry(); 62 | std::string get_last(); 63 | std::string get_first(); 64 | void clear_item(const std::string &item); 65 | void make_default(const std::shared_ptr &self); 66 | private: 67 | std::vector items; 68 | std::mutex items_mutex; 69 | }; 70 | 71 | std::shared_ptr get_default_list(); 72 | void set_default_list(std::shared_ptr list); 73 | 74 | TodoEntry create_entry_with(const std::string &text); 75 | 76 | static std::shared_ptr default_list = nullptr; 77 | static std::mutex default_list_mutex; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/traits/lib_traits.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_traits.hpp" 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /cpp-tests/scaffolding_tests/traits/lib_traits.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | namespace traits { 7 | class Button { 8 | public: 9 | virtual ~Button() = default; 10 | virtual std::string name() = 0; 11 | }; 12 | 13 | class GoButton: public Button { 14 | public: 15 | std::string name() override { 16 | return "go"; 17 | } 18 | }; 19 | 20 | class StopButton: public Button { 21 | public: 22 | std::string name() override { 23 | return "stop"; 24 | } 25 | }; 26 | 27 | std::vector> get_buttons() { 28 | return {std::make_shared(), std::make_shared()}; 29 | } 30 | 31 | std::shared_ptr