├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── EXPLANATIONS.md ├── LICENSE ├── README.md ├── cglue-bindgen ├── Cargo.toml ├── README.md └── src │ ├── codegen │ ├── c.rs │ ├── cpp.rs │ └── mod.rs │ ├── config.rs │ ├── main.rs │ └── types.rs ├── cglue-gen ├── Cargo.toml ├── build.rs └── src │ ├── ext │ ├── core │ │ ├── clone.rs │ │ ├── convert.rs │ │ ├── fmt.rs │ │ ├── future.rs │ │ └── mod.rs │ ├── futures │ │ ├── mod.rs │ │ ├── sink.rs │ │ └── stream.rs │ └── mod.rs │ ├── forward.rs │ ├── func.rs │ ├── generics.rs │ ├── lib.rs │ ├── trait_groups.rs │ ├── traits.rs │ └── util.rs ├── cglue-macro ├── Cargo.toml └── src │ └── lib.rs ├── cglue ├── Cargo.toml ├── build.rs └── src │ ├── arc.rs │ ├── boxed.rs │ ├── callback.rs │ ├── forward.rs │ ├── from2.rs │ ├── iter.rs │ ├── lib.rs │ ├── option.rs │ ├── repr_cstring.rs │ ├── result.rs │ ├── slice.rs │ ├── task │ └── mod.rs │ ├── tests │ ├── arc │ │ └── mod.rs │ ├── ext │ │ ├── as_ref.rs │ │ ├── clone.rs │ │ ├── fmt.rs │ │ ├── future.rs │ │ ├── futures.rs │ │ └── mod.rs │ ├── extra │ │ ├── custom_impl.rs │ │ ├── forward.rs │ │ ├── mod.rs │ │ └── wrap_default.rs │ ├── generics │ │ ├── associated.rs │ │ ├── associated_ref.rs │ │ ├── generic_associated.rs │ │ ├── generic_structs.rs │ │ ├── groups.rs │ │ ├── mod.rs │ │ └── param.rs │ ├── mod.rs │ └── simple │ │ ├── bounded.rs │ │ ├── consuming.rs │ │ ├── hrtb.rs │ │ ├── mod.rs │ │ ├── structs.rs │ │ ├── trait_defs.rs │ │ ├── trait_groups.rs │ │ └── traits.rs │ ├── trait_group.rs │ ├── trait_group │ └── specify.rs │ ├── tuple.rs │ └── vec.rs ├── examples ├── c-user-bin │ ├── .gitignore │ ├── Makefile │ ├── bindgen.sh │ ├── cbindgen.toml │ ├── cglue.toml │ └── main.c ├── c-user-prefixed-bin │ ├── .gitignore │ ├── Makefile │ ├── bindgen.sh │ ├── cbindgen.toml │ ├── cglue.toml │ └── main.c ├── cpp-plugin-lib │ ├── .gitignore │ ├── Makefile │ ├── bindgen.sh │ ├── cbindgen.toml │ ├── cglue.toml │ └── main.cpp ├── cpp-user-bin │ ├── .gitignore │ ├── Makefile │ ├── bindgen.sh │ ├── cbindgen.toml │ ├── cglue.toml │ └── main.cpp ├── plugin-api │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── plugin-lib │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── pregen-headers │ ├── bindgen.sh │ ├── bindings.h │ ├── bindings.hpp │ ├── cbindgen.toml │ └── cglue.toml └── user-bin │ ├── Cargo.toml │ └── src │ └── main.rs └── version-hack ├── Cargo.toml └── src └── lib.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | CARGO_NET_GIT_FETCH_WITH_CLI: true 8 | 9 | jobs: 10 | 11 | build-base: 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest, windows-latest] 16 | toolchain: ["1.56.0", "stable"] 17 | include: 18 | - os: macos-14 19 | toolchain: "stable" 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: actions-rs/toolchain@v1 23 | with: 24 | toolchain: ${{ matrix.toolchain }} 25 | override: true 26 | 27 | - name: Build without examples 28 | run: | 29 | cd cglue 30 | cargo build --verbose 31 | 32 | build-extra-features: 33 | runs-on: ${{ matrix.os }} 34 | strategy: 35 | matrix: 36 | os: [macos-14, ubuntu-latest, windows-latest] 37 | # 1.57 is needed for const panic 38 | # panic in const is used to verify waker layouts 39 | toolchain: ["1.57.0", "stable"] 40 | features: [ 41 | "--features task", 42 | "--features layout_checks" 43 | ] 44 | steps: 45 | - uses: actions/checkout@v2 46 | - uses: actions-rs/toolchain@v1 47 | with: 48 | toolchain: ${{ matrix.toolchain }} 49 | override: true 50 | 51 | - name: Build without examples 52 | run: | 53 | cd cglue 54 | cargo build --verbose ${{ matrix.features }} 55 | 56 | build-with-layouts: 57 | runs-on: ${{ matrix.os }} 58 | strategy: 59 | matrix: 60 | os: [ubuntu-latest, windows-latest] 61 | toolchain: ["1.56.0", "stable"] 62 | include: 63 | - os: macos-14 64 | toolchain: "stable" 65 | steps: 66 | - uses: actions/checkout@v2 67 | - uses: actions-rs/toolchain@v1 68 | with: 69 | toolchain: ${{ matrix.toolchain }} 70 | override: true 71 | 72 | - name: Build 73 | run: cargo build --workspace --verbose 74 | 75 | - name: Build examples 76 | run: cargo build --workspace --examples --verbose 77 | 78 | build-nightly: 79 | runs-on: ${{ matrix.os }} 80 | strategy: 81 | matrix: 82 | os: [macos-14, ubuntu-latest, windows-latest] 83 | toolchain: ["1.64.0", "stable", "nightly-2022-08-08"] 84 | steps: 85 | - uses: actions/checkout@v2 86 | - uses: actions-rs/toolchain@v1 87 | with: 88 | toolchain: ${{ matrix.toolchain }} 89 | override: true 90 | 91 | - name: Build 92 | run: cargo build --workspace --all-features --verbose 93 | env: 94 | RUSTC_BOOTSTRAP: try_default 95 | RUSTFLAGS: ${{ matrix.toolchain != 'stable' && '--cfg __cglue_force_no_unwind_abi' || '' }} 96 | 97 | - name: Build examples 98 | run: cargo build --workspace --all-features --examples --verbose 99 | env: 100 | RUSTC_BOOTSTRAP: try_default 101 | RUSTFLAGS: ${{ matrix.toolchain != 'stable' && '--cfg __cglue_force_no_unwind_abi' || '' }} 102 | 103 | build-cross-aarch64: 104 | runs-on: ubuntu-latest 105 | strategy: 106 | matrix: 107 | toolchain: ["1.56.0", "stable"] 108 | steps: 109 | - uses: actions/checkout@v2 110 | - uses: actions-rs/toolchain@v1 111 | with: 112 | toolchain: ${{ matrix.toolchain }} 113 | target: aarch64-unknown-linux-gnu 114 | override: true 115 | - uses: actions-rs/cargo@v1 116 | with: 117 | use-cross: true 118 | command: build 119 | args: --target aarch64-unknown-linux-gnu --verbose -p cglue 120 | 121 | test: 122 | runs-on: ${{ matrix.os }} 123 | strategy: 124 | matrix: 125 | os: [ubuntu-latest, windows-latest] 126 | toolchain: ["1.56.0", "stable"] 127 | include: 128 | - os: macos-14 129 | toolchain: "stable" 130 | steps: 131 | - uses: actions/checkout@v2 132 | - uses: actions-rs/toolchain@v1 133 | with: 134 | toolchain: ${{ matrix.toolchain }} 135 | override: true 136 | 137 | - name: Run all tests 138 | run: cargo test --verbose -p cglue 139 | env: 140 | RUSTFLAGS: ${{ matrix.toolchain != 'stable' && '--cfg __cglue_force_no_unwind_abi' || '' }} 141 | 142 | test-all-features: 143 | runs-on: ${{ matrix.os }} 144 | strategy: 145 | matrix: 146 | os: [macos-14, ubuntu-latest, windows-latest] 147 | toolchain: ["1.64.0", "stable", "nightly-2022-08-08"] 148 | steps: 149 | - uses: actions/checkout@v2 150 | - uses: actions-rs/toolchain@v1 151 | with: 152 | toolchain: ${{ matrix.toolchain }} 153 | override: true 154 | 155 | - name: Run all tests 156 | run: cargo test --workspace --all-features --verbose 157 | env: 158 | RUSTC_BOOTSTRAP: try_default 159 | RUSTFLAGS: ${{ matrix.toolchain != 'stable' && '--cfg __cglue_force_no_unwind_abi' || '' }} 160 | 161 | lint: 162 | runs-on: ubuntu-latest 163 | strategy: 164 | matrix: 165 | features: ["--all-features", "", "-p cglue --features task", "-p cglue --features task"] 166 | steps: 167 | - uses: actions/checkout@v2 168 | - run: rustup component add clippy 169 | - name: Check formatting 170 | run: cargo fmt -- --check 171 | - uses: actions-rs/clippy-check@v1 172 | with: 173 | token: ${{ secrets.GITHUB_TOKEN }} 174 | args: --all-targets ${{ matrix.features }} 175 | env: 176 | RUSTC_BOOTSTRAP: try_default 177 | 178 | miri: 179 | runs-on: ubuntu-latest 180 | strategy: 181 | matrix: 182 | toolchain: ["nightly-2024-11-22"] 183 | steps: 184 | - uses: actions/checkout@v2 185 | - uses: actions-rs/toolchain@v1 186 | with: 187 | toolchain: ${{ matrix.toolchain }} 188 | override: true 189 | - run: rustup component add miri 190 | - name: Install locked xargo 191 | run: | 192 | cargo install xargo --locked --version 0.3.26 193 | - name: Run miri 194 | run: | 195 | cd cglue 196 | MIRIFLAGS="-Zmiri-symbolic-alignment-check -Zmiri-retag-fields=all -Zmiri-symbolic-alignment-check -Zmiri-strict-provenance -Zmiri-tree-borrows" RUSTFLAGS="--cfg __cglue_force_no_unwind_abi" cargo miri test --features rust_void 197 | 198 | c-examples: 199 | runs-on: ubuntu-latest 200 | strategy: 201 | matrix: 202 | toolchain: ["1.56.0", "stable"] 203 | steps: 204 | - uses: actions/checkout@v2 205 | - uses: actions-rs/toolchain@v1 206 | with: 207 | toolchain: ${{ matrix.toolchain }} 208 | override: true 209 | - name: Install nightly for expansion 210 | run: rustup toolchain install nightly 211 | - name: Install cbindgen 212 | run: cargo +nightly install cbindgen 213 | - name: Set up GCC 214 | uses: egor-tensin/setup-gcc@v1 215 | with: 216 | version: latest 217 | platform: x64 218 | - name: Build the plugins 219 | run: cargo build --release -p plugin-api -p plugin-lib -p cglue-bindgen 220 | env: 221 | RUSTFLAGS: ${{ matrix.toolchain != 'stable' && '--cfg __cglue_force_no_unwind_abi' || '' }} 222 | - name: Build C++ plugin library 223 | run: | 224 | cd examples/cpp-plugin-lib/ 225 | make 226 | - name: Build and test C binary 227 | run: | 228 | cd examples/c-user-bin/ 229 | make 230 | ./main.out << EOF 231 | plugin_lib 232 | asdf 233 | 23 234 | asd 235 | 24 236 | EOF 237 | - name: Build and test C prefix binary 238 | run: | 239 | cd examples/c-user-prefixed-bin/ 240 | make 241 | ./main.out << EOF 242 | plugin_lib 243 | asdf 244 | 23 245 | asd 246 | 24 247 | EOF 248 | - name: Build and test C++ binary 249 | run: | 250 | cd examples/cpp-user-bin/ 251 | make 252 | ./main.out << EOF 253 | plugin_lib 254 | asdf 255 | 23 256 | asd 257 | 24 258 | EOF 259 | - name: Build and test C binary against C++ library 260 | run: | 261 | cd examples/c-user-bin/ 262 | make 263 | ./main.out << EOF 264 | plugin_cpp 265 | asdf 266 | 23 267 | asd 268 | 24 269 | EOF 270 | - name: Build and test C++ binary against C++ library 271 | run: | 272 | cd examples/cpp-user-bin/ 273 | make 274 | ./main.out << EOF 275 | plugin_cpp 276 | asdf 277 | 23 278 | asd 279 | 24 280 | EOF 281 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CGlue changelog 2 | 3 | ## Changes in 0.3.5: 4 | 5 | ### [Support `C-unwind` ABI](https://github.com/h33p/cglue/blob/7ccbdb762785609409a5dd0d16b315fee2dcf2ee) 6 | 7 | Adds support for `extern "C-unwind"` ABI through `#[unwind_abi]` attribute and `unwind_abi_default`/`unwind_abi_ext` feature flags: 8 | 9 | - `unwind_abi_default` will default all functions to support unwinding, which can be overridden with `#[no_unwind_abi]` attribute. 10 | - `unwind_abi_ext` will add `#[unwind_abi]` to all functions in the builtin ext system. 11 | 12 | ### [Add `CWaker`](https://github.com/h33p/cglue/blob/5302a750bdd483b01b5ea7de5ab4f8ad2036f8d3) 13 | 14 | Exposes `CWaker` as a static stable ABI waker type. 15 | 16 | ## Changes in 0.3.0: 17 | 18 | ### [Stabilize `task` feature](https://github.com/h33p/cglue/blob/e6458ae5542daa489561495fb6c613307bb80001/cglue/src/task/mod.rs) 19 | 20 | Enable with `task` feature. Provides C equivalents for `Waker`, `RawWaker`, `RawWakerVtable`. 21 | 22 | ### [Trait alias within trait groups](https://github.com/h33p/cglue/commit/35b0da5bdf6cfe6ecaeedf07ba795d618113477f) 23 | 24 | Enables specifying different generic instantiations of the same trait in the same group. Example: 25 | 26 | ```rust 27 | cglue_trait_group!(TestGroupGen, TT, { TT = TTUsize, TT = TTUSixtyFour }); 28 | cglue_impl_group!(SA, TestGroupGen, { TT = TTUsize }); 29 | ``` 30 | 31 | ### [Support traits with associated types](https://github.com/h33p/cglue/commit/5d26c373bd49e935dc65b4434bb6593e2109b8fc) 32 | 33 | The following now compiles: 34 | 35 | ```rust 36 | #[cglue_trait] 37 | pub trait WithAssoc { 38 | type AssocTy: Clone; 39 | 40 | fn with_assoc(&self, assoc: &Self::AssocTy) -> T; 41 | } 42 | ``` 43 | 44 | ### [Add Future support + Pin Self parameters](https://github.com/h33p/cglue/commit/6c1662a6db80390690361804626d31b72834ea3c) 45 | 46 | The following now works: 47 | 48 | ```rust 49 | async fn hii() -> u64 { 50 | 42 51 | } 52 | 53 | let obj = trait_obj!(hii() as Future); 54 | 55 | assert_eq!(pollster::block_on(obj), 42); 56 | ``` 57 | 58 | ### [Add futures Stream+Sink support](https://github.com/h33p/cglue/commit/609bc203fb4a96541a64be1a8098b9e93a631e4e) 59 | 60 | The following now works: 61 | 62 | ```rust 63 | let items = [42, 43, 42]; 64 | 65 | let obj = trait_obj!(futures::stream::iter(items) as Stream); 66 | 67 | assert_eq!(pollster::block_on(obj.collect::>()), items); 68 | ``` 69 | 70 | ### [Fix #17](https://github.com/h33p/cglue/commit/2d917012db0caa738eb70a89cb1c16f3ec622fb6) 71 | 72 | ## Changes in 0.2.14: 73 | 74 | [Add unstable task feature](https://github.com/h33p/cglue/commit/9fbee903963b1b407ca218609e43a65cfd1eb219) 75 | 76 | [Automatically wrap 'Into' arguments](https://github.com/h33p/cglue/commit/081c590f4eb97b1be10eaeaa9cbf87e7278ea8de) 77 | 78 | ## Changes in 0.2.12: 79 | 80 | [Initial support for GAT lifetimes](https://github.com/h33p/cglue/commit/1a8098181896bb730d276aea59464d577e5d8927) 81 | 82 | ## Changes in 0.2.11: 83 | 84 | [Fix a safety bug with slices](https://github.com/h33p/cglue/commit/cbb25367cf7867f07b0a2c7718f894de437f22eb) 85 | 86 | ## Changes in 0.2.10: 87 | 88 | [Rename Tup to CTup](https://github.com/h33p/cglue/commit/0c4eeabc9196a7796216a57d40b684f2e68f4d58) 89 | 90 | ## Changes in 0.2.9 (yanked): 91 | 92 | [Add C tuples](https://github.com/h33p/cglue/commit/41a4c7d2ad3991bd17d2572a52809586f57724a1) 93 | 94 | ## Changes in 0.2.8: 95 | 96 | [Allow to provide custom implemenetations for generics functions](https://github.com/h33p/cglue/commit/d9b5a765f4c5a8c63f4a4755bb50e85b4d2ca928) 97 | 98 | Re-export `custom_impl` macro. 99 | 100 | ## Changes in 0.2.7: 101 | 102 | [Add more helpers to CVec](https://github.com/h33p/cglue/commit/5658698ba2c343f746d4cba14a774f02ed642e78) 103 | 104 | [Parse different expressions in the cast macros](https://github.com/h33p/cglue/commit/ef9f650d6c4e309f51ae996c9672473942809621) 105 | 106 | [Expose CArcSome](https://github.com/h33p/cglue/commit/849ca7c11901c732541e4d0641cb35501ed125d6): 107 | 108 | - This is equivalent to `Arc`, and is essentially a pre-null-checked version of `CArc`. 109 | 110 | ## Changes in 0.2.5: 111 | 112 | [fix no\_std build](https://github.com/h33p/cglue/commit/6f99749708f3a38825ec107b9a64c38870010d40). 113 | 114 | ## Changes in 0.2.4: 115 | 116 | [Make cglue generated exports easier to import](https://github.com/h33p/cglue/commit/788fbce9c584e699a56bd5a16d405d52e2119714): 117 | 118 | - `cglue_##trait_or_groupname` module is exposed as public that contains all types that are being re-exported to parent module. 119 | 120 | - In the future, these types may not be re-exported anymore, and code generator may rely on `cglue_##trait_or_groupname` to exist in scope for cleaner code generation. 121 | 122 | [Add boxed slice, CVec, and add more serde support](https://github.com/h33p/cglue/commit/fd549808f6f3bb0477bd394831d4e8dd599c757c). 123 | 124 | [Compatible with official abi\_stable](https://github.com/h33p/cglue/commit/7d9147df560412a49ab767928a2c6fcbc72bff2b): 125 | 126 | - Users should now use `cglue::trait_group::c_void` as the `c_void` type. 127 | 128 | - Technically breaks semver, but it is not possible to do anything with `c_void` anyways. 129 | 130 | ## Changes in 0.2.3: 131 | 132 | [Make formatting traits FFI-safe](https://github.com/h33p/cglue/commit/dd86a2145bceb48075f560f69c10686e71634756): 133 | 134 | - All standard fmt traits are exposed. 135 | 136 | - Only Debug and Display are in the prefix. 137 | 138 | - Not full formatting functionality is preserved across FFI boundary. 139 | 140 | [Add extra customization to C function impls](https://github.com/h33p/cglue/blob/dd86a2145bceb48075f560f69c10686e71634756/cglue-gen/src/ext/core/fmt.rs#L21). 141 | 142 | ### Changes cglue-bindgen 0.2.2: 143 | 144 | [Make C++ generator to be C++11 friendly](https://github.com/h33p/cglue/commit/6457ad9e0ffb945822f76d8ad08c60743841dd7b). 145 | 146 | ## Changes in 0.2.2: 147 | 148 | Fix no\_std compilation. 149 | 150 | ## Changes in 0.2.0: 151 | 152 | [Rework code generation](https://github.com/h33p/cglue/commit/e589a0115ec343c4538804d3d8ef76ca101a112a): 153 | 154 | - Make code generator emit C functions that take in a single object that contains both the object and the return context. This simplifies usage from C/C++. 155 | 156 | - Remove zero wrapper optimization. The above is incompatible with it. 157 | 158 | - Remove CtxBox, as context has been moved into the container object. 159 | 160 | [Context is now always `Clone + Send + Sync`](https://github.com/h33p/cglue/commit/bf24eaec1d518ff82356a05646ecb3af4f4b177b). 161 | 162 | Ergonomic C/C++ wrappers: 163 | 164 | - In C++ trait objects and groups have member functions and destructors defined. Trait objects and groups themselves are ABI-incompatible when passed by value. 165 | 166 | - In C there are inline functions for invoking behavior. Some of the functions accept void pointers, because they are compatible with multiple variations of the same CGlue object. 167 | 168 | - Ability to specify default container and context types, so that the wrappers become more compact. 169 | 170 | - Manual cleanup is no longer supported as bindgen has become more complex. 171 | 172 | [Somewhat tested with miri](https://github.com/h33p/cglue/blob/af6ab0dd2b2ecfa24e8f67ba9246c0079f654f6e/.github/workflows/build.yml#L121): 173 | 174 | - Stacked borrows are disabled. 175 | 176 | - ABI checks have been patched out, because otherwise rust evaluator does not accept type erasure. 177 | 178 | [Vtable-only functions](https://github.com/h33p/cglue/commit/f9f600fb3accb7d7f1970507c79786eade12e78a): 179 | 180 | - Provide Rust functionality in C/C++ with slightly different types. 181 | 182 | [Wrap strings and slices in return types](https://github.com/h33p/cglue/commit/c8a607e68a851321a4bc288491e879e34d541bd2). 183 | 184 | [Unstable feature to auto-impl groups](https://github.com/h33p/cglue/commit/af6ab0dd2b2ecfa24e8f67ba9246c0079f654f6e): 185 | 186 | - Auto-enables the widest set of optional traits for any given type. 187 | 188 | - Makes `cglue_impl_group!` a no-op. 189 | 190 | [Runtime ABI/API validation with abi\_stable](https://github.com/h33p/cglue/commit/5b75b31a3dfb35967721d94df2e83f3ced8be9c2). 191 | 192 | [Remove no\_empty\_retwrap feature](https://github.com/h33p/cglue/commit/4e2703df12c1f69b1aa4e02f8328d660ef0bf17b). 193 | 194 | [Replace CArc with COptArc, make old CArc private](https://github.com/h33p/cglue/commit/b5caf2229fe236e2697d3b5b15b58a92b59bd6d4): 195 | 196 | - COptArc was always the prefered choice, as it allowed to also represent `None` in the same amount of space. 197 | 198 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [workspace] 3 | members = [ 4 | "cglue", 5 | "cglue-gen", 6 | "cglue-macro", 7 | "cglue-bindgen", 8 | "examples/plugin-api", 9 | "examples/plugin-lib", 10 | "examples/user-bin", 11 | "version-hack" 12 | ] 13 | 14 | default-members = [ 15 | "cglue", 16 | "cglue-gen", 17 | "cglue-macro", 18 | "cglue-bindgen", 19 | "version-hack", 20 | ] 21 | 22 | #[patch.crates-io] 23 | #abi_stable = { version = "0.11", path = "../../other/abi_stable_crates/abi_stable" } 24 | -------------------------------------------------------------------------------- /EXPLANATIONS.md: -------------------------------------------------------------------------------- 1 | # CGlue codegen explanations 2 | 3 | Some of the more important details behind certain decisions of CGlue code generation. 4 | 5 | ## Function bounds 6 | 7 | ### Lifetimes in cfunc definition vs. cfunc vtable declaration 8 | 9 | The key link between the two is that the cfunc must be valid for all possible lifetimes. Thus, in declaration we have the following syntax: 10 | 11 | 12 | ```rust 13 | do_thing: for<'a> extern "C" fn(&'a mut T) 14 | ``` 15 | 16 | While the definition uses the following: 17 | 18 | 19 | ```rust 20 | extern "C" fn cglue_do_thing(&mut T) { 21 | // ... 22 | } 23 | ``` 24 | 25 | It looks different, but `cglue_do_thing` is secretly valid for all lifetimes of T (`for<'a>`). 26 | 27 | This becomes very important when we have additional type bounds required for the function, such as the following: 28 | 29 | ```rust 30 | extern "C" fn cglue_do_thing< 31 | 'a, 32 | T: Trait + Into> 33 | >(&'a mut T) -> CBoxRef<'a, T::SubObj> { 34 | // ... 35 | } 36 | ``` 37 | 38 | The type bound becomes misleading, because it binds `T` to that single lifetime, as opposed to all possible lifetimes (and type bound thus becomes unique on each individual lifetime). The function's type becomes incompatible with default implementation for the vtable creation. To fix this, we must bind the type to any possible lifetime, as opposed to the particular function's lifetime: 39 | 40 | ``` 41 | extern "C" fn cglue_do_thing<'a, T: Trait>(&'a mut T) -> CBoxRef<'a, T::SubObj> 42 | where for<'b> T: Into> 43 | { 44 | // ... 45 | } 46 | ``` 47 | 48 | This will apply the exact same bound for all lifetimes `'a`, which makes the function types uniform and compatible to be with vtable creation. 49 | 50 | This is very important, because all instances of a function must resolve to the same underlying function, otherwise we'd have non-deterministic number of cfunc instantiations with slightly different characteristics. This becomes extremely important in GATs. 51 | 52 | ### Lifetime changes when wrapping 53 | 54 | Within cfuncs lifetimes in associated type definitions (`type Type<'a>: 'a`) take precedence over lifetimes defined by trait functions. This is to make types the source of truth. 55 | 56 | During codegen, we may encounter a function as follows: 57 | 58 | ```rust 59 | fn do_something<'b>(&'b mut self) -> Self::Type<'b> { 60 | // ... 61 | } 62 | ``` 63 | 64 | Since `Self::Type` was defined with lifetime `'a`, the implementation will match `'a` to `'b` and replace `'b` with `'a` within cfuncs. 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Aurimas Blažulionis <0x60@pm.me> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cglue-bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cglue-bindgen" 3 | version = "0.3.0" 4 | authors = ["Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | license = "MIT" 7 | documentation = "https://docs.rs/cglue-bindgen" 8 | repository = "https://github.com/h33p/cglue" 9 | description = "cleanup cbindgen headers for CGlue" 10 | keywords = [ "cglue", "abi", "ffi", "cbindgen" ] 11 | categories = [ "text-processing", "command-line-interface" ] 12 | readme = "./README.md" 13 | 14 | [dependencies] 15 | regex = "1" 16 | itertools = "0.10" 17 | log = "0.4" 18 | serde = { version = "1", features = ["derive"] } 19 | toml = "0.5" 20 | -------------------------------------------------------------------------------- /cglue-bindgen/README.md: -------------------------------------------------------------------------------- 1 | # cglue-bindgen 2 | 3 | Cleanup cbindgen output for CGlue. 4 | 5 | This crate essentially wraps cbindgen and performs additional header cleanup steps on top for 6 | good out-of-the-box usage. Note that the program expects standard naming convention, and will 7 | likely break if there is any renaming happening in cbindgen config. 8 | 9 | ## Install 10 | 11 | ```sh 12 | cargo install cglue-bindgen 13 | ``` 14 | 15 | Also make sure cbindgen is installed: 16 | 17 | ```sh 18 | cargo install cbindgen 19 | ``` 20 | 21 | ## Running 22 | 23 | Run similarly to cbindgen: 24 | 25 | ```sh 26 | cglue-bindgen +nightly -- --config cbindgen.toml --crate your_crate --output output_header.h 27 | ``` 28 | 29 | ## Configuring 30 | 31 | Create a `cglue.toml`, and pass `-c cglue.toml` to `cglue-bindgen` before the `--`. 32 | 33 | Several values can be set: 34 | 35 | `default_container` - set the default container type. This will make C/C++ code less verbose 36 | for objects that match the container and context types. Supports out-of-the-box: 37 | `Box`, `Mut`, `Ref`. 38 | 39 | `default_context` - set the default context type. This will make C/C++ code less verbose for 40 | objects that match the container and context types. Supports out-of-the-box: `Arc`, 41 | `NoContext`. 42 | 43 | ## Using the bindings 44 | 45 | Check the documentation for the respective language: 46 | 47 | * [C](self::codegen::c) 48 | 49 | * [C++](self::codegen::cpp) 50 | 51 | You can also check the [code examples](https://github.com/h33p/cglue/tree/main/examples). 52 | 53 | ## In case of an issue 54 | 55 | Please check if any custom cbindgen options are influencing the way the code is generated in 56 | any way. This crate is very finicky, and for instance, even changing the documentation style 57 | is likely to break the code generation. 58 | 59 | If you still have issues without any custom parameters, please report an issue, because then it 60 | is likely my fault or cbindgen update broke the binding generation. 61 | 62 | Verified to work cbindgen version: `v0.20.0`. 63 | 64 | -------------------------------------------------------------------------------- /cglue-bindgen/src/codegen/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod c; 2 | pub mod cpp; 3 | -------------------------------------------------------------------------------- /cglue-bindgen/src/config.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 4 | pub struct Config { 5 | pub default_container: Option, 6 | pub default_context: Option, 7 | pub function_prefix: Option, 8 | } 9 | -------------------------------------------------------------------------------- /cglue-bindgen/src/main.rs: -------------------------------------------------------------------------------- 1 | //! # cglue-bindgen 2 | //! 3 | //! Cleanup cbindgen output for CGlue. 4 | //! 5 | //! This crate essentially wraps cbindgen and performs additional header cleanup steps on top for 6 | //! good out-of-the-box usage. Note that the program expects standard naming convention, and will 7 | //! likely break if there is any renaming happening in cbindgen config. 8 | //! 9 | //! ## Install 10 | //! 11 | //! ```sh 12 | //! cargo install cglue-bindgen 13 | //! ``` 14 | //! 15 | //! Also make sure cbindgen is installed: 16 | //! 17 | //! ```sh 18 | //! cargo install cbindgen 19 | //! ``` 20 | //! 21 | //! ## Running 22 | //! 23 | //! Run similarly to cbindgen: 24 | //! 25 | //! ```sh 26 | //! cglue-bindgen +nightly -- --config cbindgen.toml --crate your_crate --output output_header.h 27 | //! ``` 28 | //! 29 | //! ## Configuring 30 | //! 31 | //! Create a `cglue.toml`, and pass `-c cglue.toml` to `cglue-bindgen` before the `--`. 32 | //! 33 | //! Several values can be set: 34 | //! 35 | //! `default_container` - set the default container type. This will make C/C++ code less verbose 36 | //! for objects that match the container and context types. Supports out-of-the-box: 37 | //! `Box`, `Mut`, `Ref`. 38 | //! 39 | //! `default_context` - set the default context type. This will make C/C++ code less verbose for 40 | //! objects that match the container and context types. Supports out-of-the-box: `Arc`, 41 | //! `NoContext`. 42 | //! 43 | //! ## Using the bindings 44 | //! 45 | //! Check the documentation for the respective language: 46 | //! 47 | //! * [C](self::codegen::c) 48 | //! 49 | //! * [C++](self::codegen::cpp) 50 | //! 51 | //! You can also check the [code examples](https://github.com/h33p/cglue/tree/main/examples). 52 | //! 53 | //! ## In case of an issue 54 | //! 55 | //! Please check if any custom cbindgen options are influencing the way the code is generated in 56 | //! any way. This crate is very finicky, and for instance, even changing the documentation style 57 | //! is likely to break the code generation. 58 | //! 59 | //! If you still have issues without any custom parameters, please report an issue, because then it 60 | //! is likely my fault or cbindgen update broke the binding generation. 61 | //! 62 | //! Verified to work cbindgen version: `v0.20.0`. 63 | //! 64 | 65 | use std::env; 66 | use std::fs::File; 67 | use std::io::{Read, Write}; 68 | use std::process::*; 69 | 70 | pub mod types; 71 | use types::Result; 72 | 73 | pub mod codegen; 74 | use codegen::{c, cpp}; 75 | 76 | pub mod config; 77 | use config::Config; 78 | 79 | fn main() -> Result<()> { 80 | let args_pre = env::args() 81 | .skip(1) 82 | .take_while(|v| v != "--") 83 | .collect::>(); 84 | let args = env::args().skip_while(|v| v != "--").collect::>(); 85 | 86 | // Hijack the output 87 | 88 | let mut output_file = None; 89 | let mut args_out = vec![]; 90 | 91 | for a in args.windows(2) { 92 | // Skip the first arg, the "--", which also allows us to filter 2 args in one go when we 93 | // need that. 94 | match a[0].as_str() { 95 | "-o" | "--output" => { 96 | if output_file.is_none() { 97 | output_file = Some(a[1].clone()); 98 | } 99 | } 100 | _ => { 101 | if a[1] != "-o" && a[1] != "--output" { 102 | args_out.push(a[1].clone()); 103 | } 104 | } 105 | } 106 | } 107 | 108 | let mut config = Config::default(); 109 | 110 | for a in args_pre.windows(2) { 111 | match a[0].as_str() { 112 | "-c" | "--config" => { 113 | let mut f = File::open(&a[1])?; 114 | let mut val = vec![]; 115 | f.read_to_end(&mut val)?; 116 | config = toml::from_str(std::str::from_utf8(&val)?)?; 117 | } 118 | _ => {} 119 | } 120 | } 121 | 122 | let use_nightly = args_pre.iter().any(|v| v == "+nightly"); 123 | 124 | let mut cmd = if use_nightly { 125 | let mut cmd = Command::new("rustup"); 126 | 127 | cmd.args(&["run", "nightly", "cbindgen"]); 128 | 129 | cmd 130 | } else { 131 | Command::new("cbindgen") 132 | }; 133 | 134 | cmd.args(&args_out[..]); 135 | 136 | let output = cmd.output()?; 137 | 138 | if !output.status.success() { 139 | eprintln!("{}", std::str::from_utf8(&output.stderr)?); 140 | return Err("cbindgen failed".into()); 141 | } 142 | 143 | let out = std::str::from_utf8(&output.stdout)?.to_string(); 144 | 145 | let output = if cpp::is_cpp(&out)? { 146 | cpp::parse_header(&out, &config)? 147 | } else if c::is_c(&out)? { 148 | c::parse_header(&out, &config)? 149 | } else { 150 | return Err("Unsupported header format!".into()); 151 | }; 152 | 153 | if let Some(path) = output_file { 154 | let mut file = File::create(path)?; 155 | file.write_all(output.as_str().as_bytes())?; 156 | } else { 157 | print!("{}", output); 158 | } 159 | 160 | Ok(()) 161 | } 162 | -------------------------------------------------------------------------------- /cglue-gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cglue-gen" 3 | version = "0.3.2" 4 | authors = ["Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | description = "FFI safe code generation for making plugins and C-compatible libraries" 7 | license = "MIT" 8 | documentation = "https://docs.rs/cglue-gen" 9 | repository = "https://github.com/h33p/cglue" 10 | keywords = [ "cglue", "abi", "ffi", "cbindgen", "macro" ] 11 | categories = [ "api-bindings", "accessibility", "parsing" ] 12 | readme = "../README.md" 13 | rust-version = "1.56" 14 | 15 | [dependencies] 16 | syn = { version = "1", features = ["full", "extra-traits"] } 17 | proc-macro2 = "1" 18 | quote = "1" 19 | proc-macro-crate = ">=1.1.3" 20 | itertools = "0.10" 21 | lazy_static = "1" 22 | 23 | [features] 24 | default = [] 25 | rust_void = [] 26 | unstable = [] 27 | layout_checks = [] 28 | task = [] 29 | futures = ["task"] 30 | unwind_abi_default = [] 31 | unwind_abi_ext = [] 32 | abi_stable11 = [] 33 | -------------------------------------------------------------------------------- /cglue-gen/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-check-cfg=cfg(__cglue_force_no_unwind_abi)"); 3 | } 4 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/core/clone.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use std::collections::HashMap; 4 | use syn::{Ident, Path}; 5 | 6 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 7 | let cur_path = super::super::join_paths(parent_path, format_ident!("clone")); 8 | 9 | out.push(( 10 | cur_path, 11 | quote! { 12 | pub trait Clone { 13 | fn clone(&self) -> Self; 14 | } 15 | }, 16 | )); 17 | } 18 | 19 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 20 | let cur_path = super::super::join_paths(parent_path, format_ident!("clone")); 21 | exports.insert(format_ident!("Clone"), cur_path); 22 | } 23 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/core/convert.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use std::collections::HashMap; 4 | use syn::{Ident, Path}; 5 | 6 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 7 | let cur_path = super::super::join_paths(parent_path, format_ident!("convert")); 8 | 9 | out.push(( 10 | cur_path, 11 | quote! { 12 | pub trait AsRef { 13 | fn as_ref(&self) -> &T; 14 | } 15 | pub trait AsMut { 16 | fn as_mut(&mut self) -> &mut T; 17 | } 18 | }, 19 | )); 20 | } 21 | 22 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 23 | let cur_path = super::super::join_paths(parent_path, format_ident!("convert")); 24 | exports.insert(format_ident!("AsRef"), cur_path.clone()); 25 | exports.insert(format_ident!("AsMut"), cur_path); 26 | } 27 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/core/fmt.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use std::collections::HashMap; 4 | use syn::{Ident, Path}; 5 | 6 | const FMT_TRAITS: &[(&str, &str)] = &[ 7 | ("Display", ""), 8 | ("Debug", "?"), 9 | ("Octal", "o"), 10 | ("LowerHex", "x"), 11 | ("UpperHex", "X"), 12 | ("Pointer", "p"), 13 | ("Binary", "b"), 14 | ("LowerExp", "e"), 15 | ("UpperExp", "E"), 16 | ]; 17 | 18 | // fmt is a tricky beast, we need to employ custom wrapping to make it work across FFI-boundary. 19 | fn get_custom_impl(fmt_str: &str, crate_path: &TokenStream) -> TokenStream { 20 | quote! { 21 | #[custom_impl( 22 | // Types within the C interface other than self and additional wrappers. 23 | { 24 | f_out: &mut WriteMut, 25 | }, 26 | // Unwrapped return type 27 | Result<(), ::core::fmt::Error>, 28 | // Conversion in trait impl to C arguments (signature names are expected). 29 | { 30 | let f_out: WriteBaseMut<::core::fmt::Formatter> = From::from(f); 31 | let f_out = &mut #crate_path::trait_group::Opaquable::into_opaque(f_out); 32 | }, 33 | // This is the body of C impl minus the automatic wrapping. 34 | { 35 | write!(f_out, #fmt_str, this) 36 | }, 37 | // This part is processed in the trait impl after the call returns (impl_func_ret, 38 | // nothing extra needs to happen here). 39 | { 40 | }, 41 | )] 42 | } 43 | } 44 | 45 | pub fn fmt_impls() -> TokenStream { 46 | let mut out = TokenStream::new(); 47 | 48 | let crate_path = crate::util::crate_path(); 49 | 50 | for (name, ty) in FMT_TRAITS { 51 | let wrap_impl = get_custom_impl(&format!("{{:{}}}", ty), &crate_path); 52 | let ident = format_ident!("{}", name); 53 | 54 | out.extend(quote! { 55 | pub trait #ident { 56 | #[int_result] 57 | #wrap_impl 58 | fn fmt( 59 | &self, 60 | f: &mut ::core::fmt::Formatter 61 | ) -> Result<(), ::core::fmt::Error>; 62 | } 63 | }); 64 | } 65 | 66 | out 67 | } 68 | 69 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 70 | let cur_path = super::super::join_paths(parent_path, format_ident!("fmt")); 71 | 72 | let fmt_impls = fmt_impls(); 73 | 74 | out.push(( 75 | cur_path, 76 | quote! { 77 | pub trait Write { 78 | #[int_result] 79 | fn write_str(&mut self, s: &str) -> Result<(), ::core::fmt::Error>; 80 | } 81 | 82 | #fmt_impls 83 | }, 84 | )); 85 | } 86 | 87 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 88 | let cur_path = super::super::join_paths(parent_path, format_ident!("fmt")); 89 | exports.insert(format_ident!("Debug"), cur_path.clone()); 90 | exports.insert(format_ident!("Display"), cur_path); 91 | } 92 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/core/future.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use std::collections::HashMap; 4 | use syn::{Ident, Path}; 5 | 6 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 7 | let cur_path = super::super::join_paths(parent_path, format_ident!("future")); 8 | 9 | let crate_path = crate::util::crate_path(); 10 | 11 | out.push(( 12 | cur_path, 13 | quote! { 14 | pub trait Future { 15 | type Output; 16 | 17 | #[custom_impl( 18 | // Types within the C interface other than self and additional wrappers. 19 | { 20 | cx: &#crate_path::task::CRefWaker, 21 | out: &mut ::core::mem::MaybeUninit, 22 | }, 23 | // Unwrapped return type 24 | bool, 25 | // Conversion in trait impl to C arguments (signature names are expected). 26 | { 27 | let mut out_v = ::core::mem::MaybeUninit::uninit(); 28 | let out = &mut out_v; 29 | let cx = #crate_path::task::CRefWaker::from(cx.waker()); 30 | let cx = &cx; 31 | }, 32 | // This is the body of C impl minus the automatic wrapping. 33 | { 34 | cx.with_waker(|waker| { 35 | let mut cx = ::core::task::Context::from_waker(waker); 36 | match this.poll(&mut cx) { 37 | ::core::task::Poll::Ready(v) => { 38 | out.write(v); 39 | true 40 | } 41 | _ => false 42 | } 43 | }) 44 | }, 45 | // This part is processed in the trait impl after the call returns (impl_func_ret). 46 | { 47 | if ret { 48 | ::core::task::Poll::Ready(unsafe { out_v.assume_init() }) 49 | } else { 50 | ::core::task::Poll::Pending 51 | } 52 | }, 53 | )] 54 | fn poll(self: ::core::pin::Pin<&mut Self>, cx: &mut ::core::task::Context) -> ::core::task::Poll; 55 | } 56 | }, 57 | )); 58 | } 59 | 60 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 61 | let cur_path = super::super::join_paths(parent_path, format_ident!("future")); 62 | exports.insert(format_ident!("Future"), cur_path); 63 | } 64 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod clone; 2 | pub mod convert; 3 | pub mod fmt; 4 | pub mod future; 5 | 6 | use proc_macro2::TokenStream; 7 | use quote::format_ident; 8 | use std::collections::HashMap; 9 | use syn::{Ident, Path}; 10 | 11 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 12 | let cur_path = super::join_paths(parent_path, format_ident!("core")); 13 | clone::get_impl(&cur_path, out); 14 | fmt::get_impl(&cur_path, out); 15 | convert::get_impl(&cur_path, out); 16 | #[cfg(feature = "task")] 17 | future::get_impl(&cur_path, out); 18 | } 19 | 20 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 21 | let cur_path = super::join_paths(parent_path, format_ident!("core")); 22 | clone::get_exports(&cur_path, exports); 23 | fmt::get_exports(&cur_path, exports); 24 | convert::get_exports(&cur_path, exports); 25 | #[cfg(feature = "task")] 26 | future::get_exports(&cur_path, exports); 27 | } 28 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/futures/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sink; 2 | pub mod stream; 3 | 4 | use proc_macro2::TokenStream; 5 | use quote::format_ident; 6 | use std::collections::HashMap; 7 | use syn::{Ident, Path}; 8 | 9 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 10 | let cur_path = super::join_paths(parent_path, format_ident!("futures")); 11 | stream::get_impl(&cur_path, out); 12 | sink::get_impl(&cur_path, out); 13 | } 14 | 15 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 16 | let cur_path = super::join_paths(parent_path, format_ident!("futures")); 17 | stream::get_exports(&cur_path, exports); 18 | sink::get_exports(&cur_path, exports); 19 | } 20 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/futures/sink.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use std::collections::HashMap; 4 | use syn::{Ident, Path}; 5 | 6 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 7 | let cur_path = super::super::join_paths(parent_path, format_ident!("sink")); 8 | 9 | let crate_path = crate::util::crate_path(); 10 | 11 | out.push(( 12 | cur_path, 13 | quote! { 14 | pub trait Sink { 15 | type Error; 16 | 17 | #[custom_impl( 18 | // Types within the C interface other than self and additional wrappers. 19 | { 20 | cx: &#crate_path::task::CRefWaker, 21 | out: &mut ::core::mem::MaybeUninit, 22 | }, 23 | // Unwrapped return type 24 | u8, 25 | // Conversion in trait impl to C arguments (signature names are expected). 26 | { 27 | let mut out_v = ::core::mem::MaybeUninit::uninit(); 28 | let out = &mut out_v; 29 | let cx = #crate_path::task::CRefWaker::from(cx.waker()); 30 | let cx = &cx; 31 | }, 32 | // This is the body of C impl minus the automatic wrapping. 33 | { 34 | cx.with_waker(|waker| { 35 | let mut cx = ::core::task::Context::from_waker(waker); 36 | match this.poll_ready(&mut cx) { 37 | ::core::task::Poll::Ready(Ok(())) => 1, 38 | ::core::task::Poll::Ready(Err(e)) => { 39 | out.write(e); 40 | 2 41 | } 42 | _ => 0 43 | } 44 | }) 45 | }, 46 | // This part is processed in the trait impl after the call returns (impl_func_ret). 47 | { 48 | if ret == 1 { 49 | ::core::task::Poll::Ready(Ok(())) 50 | } else if ret == 2 { 51 | ::core::task::Poll::Ready(Err(unsafe { out_v.assume_init() })) 52 | } else { 53 | ::core::task::Poll::Pending 54 | } 55 | }, 56 | )] 57 | fn poll_ready(self: ::core::pin::Pin<&mut Self>, cx: &mut ::core::task::Context) -> ::core::task::Poll>; 58 | 59 | #[custom_impl( 60 | // Types within the C interface other than self and additional wrappers. 61 | { 62 | item: Item, 63 | out: &mut ::core::mem::MaybeUninit, 64 | }, 65 | // Unwrapped return type 66 | u8, 67 | // Conversion in trait impl to C arguments (signature names are expected). 68 | { 69 | let mut out_v = ::core::mem::MaybeUninit::uninit(); 70 | let out = &mut out_v; 71 | }, 72 | // This is the body of C impl minus the automatic wrapping. 73 | { 74 | match this.start_send(item) { 75 | Ok(()) => 0, 76 | Err(e) => { 77 | out.write(e); 78 | 1 79 | } 80 | } 81 | }, 82 | // This part is processed in the trait impl after the call returns (impl_func_ret). 83 | { 84 | if ret == 0 { 85 | Ok(()) 86 | } else { 87 | Err(unsafe { out_v.assume_init() }) 88 | } 89 | }, 90 | )] 91 | fn start_send(self: ::core::pin::Pin<&mut Self>, item: Item) -> Result<(), Self::Error>; 92 | 93 | #[custom_impl( 94 | // Types within the C interface other than self and additional wrappers. 95 | { 96 | cx: &#crate_path::task::CRefWaker, 97 | out: &mut ::core::mem::MaybeUninit, 98 | }, 99 | // Unwrapped return type 100 | u8, 101 | // Conversion in trait impl to C arguments (signature names are expected). 102 | { 103 | let mut out_v = ::core::mem::MaybeUninit::uninit(); 104 | let out = &mut out_v; 105 | let cx = #crate_path::task::CRefWaker::from(cx.waker()); 106 | let cx = &cx; 107 | }, 108 | // This is the body of C impl minus the automatic wrapping. 109 | { 110 | cx.with_waker(|waker| { 111 | let mut cx = ::core::task::Context::from_waker(waker); 112 | match this.poll_flush(&mut cx) { 113 | ::core::task::Poll::Ready(Ok(())) => 1, 114 | ::core::task::Poll::Ready(Err(e)) => { 115 | out.write(e); 116 | 2 117 | } 118 | _ => 0 119 | } 120 | }) 121 | }, 122 | // This part is processed in the trait impl after the call returns (impl_func_ret). 123 | { 124 | if ret == 1 { 125 | ::core::task::Poll::Ready(Ok(())) 126 | } else if ret == 2 { 127 | ::core::task::Poll::Ready(Err(unsafe { out_v.assume_init() })) 128 | } else { 129 | ::core::task::Poll::Pending 130 | } 131 | }, 132 | )] 133 | fn poll_flush(self: ::core::pin::Pin<&mut Self>, cx: &mut ::core::task::Context) -> ::core::task::Poll>; 134 | 135 | #[custom_impl( 136 | // Types within the C interface other than self and additional wrappers. 137 | { 138 | cx: &#crate_path::task::CRefWaker, 139 | out: &mut ::core::mem::MaybeUninit, 140 | }, 141 | // Unwrapped return type 142 | u8, 143 | // Conversion in trait impl to C arguments (signature names are expected). 144 | { 145 | let mut out_v = ::core::mem::MaybeUninit::uninit(); 146 | let out = &mut out_v; 147 | let cx = #crate_path::task::CRefWaker::from(cx.waker()); 148 | let cx = &cx; 149 | }, 150 | // This is the body of C impl minus the automatic wrapping. 151 | { 152 | cx.with_waker(|waker| { 153 | let mut cx = ::core::task::Context::from_waker(waker); 154 | match this.poll_close(&mut cx) { 155 | ::core::task::Poll::Ready(Ok(())) => 1, 156 | ::core::task::Poll::Ready(Err(e)) => { 157 | out.write(e); 158 | 2 159 | } 160 | _ => 0 161 | } 162 | }) 163 | }, 164 | // This part is processed in the trait impl after the call returns (impl_func_ret). 165 | { 166 | if ret == 1 { 167 | ::core::task::Poll::Ready(Ok(())) 168 | } else if ret == 2 { 169 | ::core::task::Poll::Ready(Err(unsafe { out_v.assume_init() })) 170 | } else { 171 | ::core::task::Poll::Pending 172 | } 173 | }, 174 | )] 175 | fn poll_close(self: ::core::pin::Pin<&mut Self>, cx: &mut ::core::task::Context) -> ::core::task::Poll>; 176 | 177 | } 178 | }, 179 | )); 180 | } 181 | 182 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 183 | let cur_path = super::super::join_paths(parent_path, format_ident!("sink")); 184 | exports.insert(format_ident!("Sink"), cur_path); 185 | } 186 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/futures/stream.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use std::collections::HashMap; 4 | use syn::{Ident, Path}; 5 | 6 | pub fn get_impl(parent_path: &Path, out: &mut Vec<(Path, TokenStream)>) { 7 | let cur_path = super::super::join_paths(parent_path, format_ident!("stream")); 8 | 9 | let crate_path = crate::util::crate_path(); 10 | 11 | out.push(( 12 | cur_path, 13 | quote! { 14 | pub trait Stream { 15 | type Item; 16 | 17 | #[custom_impl( 18 | // Types within the C interface other than self and additional wrappers. 19 | { 20 | cx: &#crate_path::task::CRefWaker, 21 | out: &mut ::core::mem::MaybeUninit, 22 | }, 23 | // Unwrapped return type 24 | u8, 25 | // Conversion in trait impl to C arguments (signature names are expected). 26 | { 27 | let mut out_v = ::core::mem::MaybeUninit::uninit(); 28 | let out = &mut out_v; 29 | let cx = #crate_path::task::CRefWaker::from(cx.waker()); 30 | let cx = &cx; 31 | }, 32 | // This is the body of C impl minus the automatic wrapping. 33 | { 34 | cx.with_waker(|waker| { 35 | let mut cx = ::core::task::Context::from_waker(waker); 36 | match this.poll_next(&mut cx) { 37 | ::core::task::Poll::Ready(Some(v)) => { 38 | out.write(v); 39 | 1 40 | } 41 | ::core::task::Poll::Ready(None) => 2, 42 | _ => 0 43 | } 44 | }) 45 | }, 46 | // This part is processed in the trait impl after the call returns (impl_func_ret). 47 | { 48 | if ret == 1 { 49 | ::core::task::Poll::Ready(Some(unsafe { out_v.assume_init() })) 50 | } else if ret == 2 { 51 | ::core::task::Poll::Ready(None) 52 | } else { 53 | ::core::task::Poll::Pending 54 | } 55 | }, 56 | )] 57 | fn poll_next(self: ::core::pin::Pin<&mut Self>, cx: &mut ::core::task::Context) -> ::core::task::Poll>; 58 | } 59 | }, 60 | )); 61 | } 62 | 63 | pub fn get_exports(parent_path: &Path, exports: &mut HashMap) { 64 | let cur_path = super::super::join_paths(parent_path, format_ident!("stream")); 65 | exports.insert(format_ident!("Stream"), cur_path); 66 | } 67 | -------------------------------------------------------------------------------- /cglue-gen/src/ext/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod core; 2 | #[cfg(feature = "futures")] 3 | pub mod futures; 4 | 5 | use proc_macro2::TokenStream; 6 | use quote::{format_ident, quote}; 7 | use std::collections::hash_map::Entry; 8 | use std::collections::HashMap; 9 | use std::collections::HashSet; 10 | use syn::{ 11 | parse::{ParseStream, Parser}, 12 | *, 13 | }; 14 | 15 | pub fn get_exports() -> HashMap { 16 | let mut exports = HashMap::new(); 17 | 18 | let mut ext_path: Path = parse2(quote!(::ext)).unwrap(); 19 | ext_path.segments.push_punct(Default::default()); 20 | 21 | core::get_exports(&ext_path, &mut exports); 22 | #[cfg(feature = "futures")] 23 | futures::get_exports(&ext_path, &mut exports); 24 | 25 | exports 26 | } 27 | 28 | pub fn get_store() -> HashMap<(Path, Ident), ItemTrait> { 29 | let mut token_list = vec![]; 30 | 31 | let mut ext_path: Path = parse2(quote!(::ext)).unwrap(); 32 | ext_path.segments.push_punct(Default::default()); 33 | 34 | core::get_impl(&ext_path, &mut token_list); 35 | #[cfg(feature = "futures")] 36 | futures::get_impl(&ext_path, &mut token_list); 37 | 38 | let mut parsed_traits = HashMap::new(); 39 | 40 | for (path, body) in token_list { 41 | let traits = Parser::parse2(parse_traits, body).expect("Failed to parse traits"); 42 | 43 | for tr in traits { 44 | parsed_traits.insert((path.clone(), tr.ident.clone()), tr); 45 | } 46 | } 47 | 48 | parsed_traits 49 | } 50 | 51 | fn subpath_to_tokens(path: &Path, skip: usize) -> TokenStream { 52 | let mut out = TokenStream::new(); 53 | 54 | for seg in path.segments.pairs().skip(skip) { 55 | match seg { 56 | punctuated::Pair::Punctuated(p, punc) => { 57 | out.extend(quote!(#p #punc)); 58 | } 59 | punctuated::Pair::End(p) => { 60 | out.extend(quote!(#p)); 61 | } 62 | } 63 | } 64 | 65 | out 66 | } 67 | 68 | type Modules = HashMap)>>; 69 | 70 | fn impl_mod( 71 | path: &Path, 72 | name: &Ident, 73 | depth: usize, 74 | mut mod_impl: TokenStream, 75 | children: HashSet, 76 | modules: &mut Modules, 77 | ) -> TokenStream { 78 | let child_depth = depth + 1; 79 | 80 | for ident in children { 81 | let mut path = path.clone(); 82 | 83 | let name = ident.clone(); 84 | 85 | path.segments.push_value(PathSegment { 86 | ident, 87 | arguments: Default::default(), 88 | }); 89 | 90 | path.segments.push_punct(Default::default()); 91 | 92 | let (ts, children) = modules 93 | .get_mut(&child_depth) 94 | .expect("Module depth not found") 95 | .remove(&path) 96 | .expect("Child module not found"); 97 | 98 | mod_impl.extend(impl_mod(&path, &name, child_depth, ts, children, modules)); 99 | } 100 | 101 | quote! { 102 | pub mod #name { 103 | #mod_impl 104 | } 105 | } 106 | } 107 | 108 | /// Remaps all Ident paths that are in the export list to become ::ext::Ident 109 | pub fn prelude_remap(path: Path) -> Path { 110 | if let Some(ident) = path.get_ident().cloned() { 111 | if let Some(path) = get_exports().get(&ident) { 112 | let mut new_path = path.clone(); 113 | 114 | new_path.segments.push(PathSegment { 115 | ident, 116 | arguments: Default::default(), 117 | }); 118 | 119 | new_path 120 | } else { 121 | path 122 | } 123 | } else { 124 | path 125 | } 126 | } 127 | 128 | /// Returns the absolute export path if ident is in exports, and path is empty. 129 | pub fn prelude_remap_with_ident(path: Path, ident: &Ident) -> Path { 130 | if !path.segments.is_empty() { 131 | path 132 | } else if let Some(path) = get_exports().get(ident) { 133 | path.clone() 134 | } else { 135 | path 136 | } 137 | } 138 | 139 | /// Remaps all ::ext:: paths to become ::cglue::ext:: paths. 140 | pub fn ext_abs_remap(path: Path) -> Path { 141 | let mut iter = path.segments.iter(); 142 | if let (Some(_), Some(seg)) = (path.leading_colon, iter.next()) { 143 | if seg.ident == "ext" { 144 | let (leading_colon, ident) = crate::util::crate_path_ident(); 145 | 146 | let mut new_path = Path { 147 | leading_colon, 148 | segments: Default::default(), 149 | }; 150 | 151 | new_path.segments.push_value(PathSegment { 152 | ident, 153 | arguments: Default::default(), 154 | }); 155 | 156 | new_path.segments.push_punct(Default::default()); 157 | 158 | std::mem::drop(iter); 159 | 160 | new_path.segments.extend(path.segments.into_pairs()); 161 | 162 | new_path 163 | } else { 164 | std::mem::drop(iter); 165 | path 166 | } 167 | } else { 168 | std::mem::drop(iter); 169 | path 170 | } 171 | } 172 | 173 | pub fn impl_ext_forward() -> TokenStream { 174 | impl_inner( 175 | |_, _| quote!(), 176 | |p, _| quote!(#[cglue_forward_ext(::#p)]), 177 | |_, _| {}, 178 | ) 179 | } 180 | 181 | /// Implement the external trait store. 182 | pub fn impl_store() -> TokenStream { 183 | let unwind_abi = if cfg!(feature = "unwind_abi_ext") { 184 | quote!(#[unwind_abi]) 185 | } else { 186 | quote!() 187 | }; 188 | 189 | impl_inner( 190 | |subpath, name| quote!(pub use #subpath #name;), 191 | |_, _| quote!(#[cglue_trait_ext] #unwind_abi), 192 | |exports, out| { 193 | // Re-export everything 194 | for (k, v) in exports.into_iter() { 195 | let subpath = subpath_to_tokens(&v, 1); 196 | 197 | for ident in [ 198 | "", 199 | "Ext", 200 | "Vtbl", 201 | "RetTmp", 202 | "Box", 203 | "CtxBox", 204 | "ArcBox", 205 | "Mut", 206 | "CtxMut", 207 | "ArcMut", 208 | "Ref", 209 | "CtxRef", 210 | "ArcRef", 211 | "Base", 212 | "BaseBox", 213 | "BaseCtxBox", 214 | "BaseArcBox", 215 | "BaseMut", 216 | "BaseCtxMut", 217 | "BaseArcMut", 218 | "BaseRef", 219 | "BaseCtxRef", 220 | "BaseArcRef", 221 | ] 222 | .iter() 223 | .map(|p| format_ident!("{}{}", k, p)) 224 | { 225 | out.extend(quote!(pub use self:: #subpath #ident;)); 226 | } 227 | } 228 | }, 229 | ) 230 | } 231 | 232 | fn impl_inner( 233 | use_gen: impl Fn(&TokenStream, &Ident) -> TokenStream, 234 | attribute_gen: impl Fn(&TokenStream, &ItemTrait) -> TokenStream, 235 | exports_gen: impl Fn(HashMap, &mut TokenStream), 236 | ) -> TokenStream { 237 | let mut out = TokenStream::new(); 238 | 239 | let exports = get_exports(); 240 | let store = get_store(); 241 | 242 | let mut modules = HashMap::)>>::new(); 243 | 244 | exports_gen(exports, &mut out); 245 | 246 | for ((p, _), t) in store.into_iter() { 247 | // exclude :: ext :: segment, and the whole layer altogether 248 | let segments = p.segments.len(); 249 | 250 | assert!(segments > 1, "External traits defined in external root!"); 251 | 252 | let depth = segments - 2; 253 | 254 | push_to_parent(depth, &p, &mut modules); 255 | 256 | let (module, _) = modules 257 | .entry(depth) 258 | .or_default() 259 | .entry(p.clone()) 260 | .or_default(); 261 | 262 | let name = &t.ident; 263 | let subpath = subpath_to_tokens(&p, 1); 264 | 265 | let use_gened = use_gen(&subpath, name); 266 | 267 | let attribute = attribute_gen(&subpath, &t); 268 | 269 | module.extend(quote! { 270 | #use_gened 271 | 272 | use ::cglue_macro::*; 273 | 274 | #attribute 275 | #t 276 | }); 277 | } 278 | 279 | if let Some(root) = modules.remove(&0) { 280 | for (p, (ts, children)) in root { 281 | let name = &p.segments.iter().next_back().unwrap().ident; 282 | out.extend(impl_mod(&p, name, 0, ts, children, &mut modules)) 283 | } 284 | } else if !modules.is_empty() { 285 | panic!("Module implementations defined, but everything is disjoint from the root!"); 286 | } 287 | 288 | out 289 | } 290 | 291 | fn push_to_parent(depth: usize, path: &Path, modules: &mut Modules) { 292 | if depth == 0 { 293 | return; 294 | } 295 | 296 | let child_depth = depth - 1; 297 | 298 | let mut parent_path = path.clone(); 299 | let my_ident = parent_path 300 | .segments 301 | .pop() 302 | .map(punctuated::Pair::into_value) 303 | .unwrap() 304 | .ident; 305 | 306 | let entry = modules 307 | .entry(child_depth) 308 | .or_default() 309 | .entry(parent_path.clone()); 310 | 311 | match entry { 312 | Entry::Occupied(mut e) => e.get_mut().1.insert(my_ident), 313 | Entry::Vacant(_) => { 314 | push_to_parent(child_depth, &parent_path, modules); 315 | let (_, children) = modules 316 | .entry(child_depth) 317 | .or_default() 318 | .entry(parent_path) 319 | .or_default(); 320 | children.insert(my_ident) 321 | } 322 | }; 323 | } 324 | 325 | fn parse_traits(input: ParseStream) -> Result> { 326 | let mut out = vec![]; 327 | 328 | while !input.is_empty() { 329 | let val = input.parse()?; 330 | 331 | out.push(val); 332 | } 333 | 334 | Ok(out) 335 | } 336 | 337 | fn join_paths(path: &Path, ident: Ident) -> Path { 338 | let mut ret = path.clone(); 339 | 340 | if !ret.segments.empty_or_trailing() { 341 | ret.segments.push_punct(Default::default()); 342 | } 343 | 344 | ret.segments.push_value(PathSegment { 345 | ident, 346 | arguments: Default::default(), 347 | }); 348 | 349 | ret.segments.push_punct(Default::default()); 350 | 351 | ret 352 | } 353 | -------------------------------------------------------------------------------- /cglue-gen/src/forward.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | 3 | use std::collections::BTreeMap; 4 | 5 | use super::func::WrappedType; 6 | use super::generics::ParsedGenerics; 7 | 8 | use quote::*; 9 | use syn::*; 10 | 11 | pub fn gen_forward(tr: ItemTrait, ext_path: Option) -> TokenStream { 12 | let crate_path = crate::util::crate_path(); 13 | 14 | let mut types = BTreeMap::new(); 15 | 16 | types.insert( 17 | format_ident!("Self"), 18 | WrappedType { 19 | ty: parse2(quote!(Self)).unwrap(), 20 | ty_ret_tmp: None, 21 | ty_static: None, 22 | return_conv: None, 23 | lifetime_bound: None, 24 | lifetime_type_bound: None, 25 | other_bounds: None, 26 | other_bounds_simple: None, 27 | impl_return_conv: None, 28 | inject_ret_tmp: false, 29 | unbounded_hrtb: false, 30 | }, 31 | ); 32 | 33 | let mut wrapped_types = TokenStream::new(); 34 | 35 | let (funcs, generics, _, _) = super::traits::parse_trait( 36 | &tr, 37 | &crate_path, 38 | false, 39 | |(ty_ident, _, ty_where_clause, _), _, _, _, _, _, _| { 40 | if let Some(ident) = ty_ident { 41 | wrapped_types.extend(quote!(type #ident = CGlueT::#ident #ty_where_clause;)); 42 | } 43 | }, 44 | ); 45 | 46 | let ParsedGenerics { 47 | life_declare, 48 | life_use, 49 | gen_declare, 50 | gen_use, 51 | gen_where_bounds, 52 | .. 53 | } = &generics; 54 | 55 | let trait_name = &tr.ident; 56 | 57 | let mut impls = TokenStream::new(); 58 | 59 | let mut need_mut = false; 60 | 61 | for func in funcs { 62 | let nm = func.forward_wrapped_trait_impl(&mut impls); 63 | need_mut = nm || need_mut; 64 | } 65 | 66 | let mut required_mutability = TokenStream::new(); 67 | 68 | required_mutability.extend(quote!(::core::ops::Deref)); 69 | 70 | if need_mut { 71 | required_mutability.extend(quote!( + ::core::ops::DerefMut)) 72 | } 73 | 74 | let tr_impl = if ext_path.is_some() { 75 | quote!() 76 | } else { 77 | quote!(#tr) 78 | }; 79 | 80 | let needs_send = tr.supertraits.iter().any(|s| { 81 | if let TypeParamBound::Trait(tr) = s { 82 | tr.path.get_ident().map(|i| i == "Send") == Some(true) 83 | } else { 84 | false 85 | } 86 | }); 87 | 88 | let send_bound = if needs_send { quote!(+ Send) } else { quote!() }; 89 | quote! { 90 | #tr_impl 91 | 92 | impl<#life_declare CGlueO: #required_mutability #send_bound, CGlueT, #gen_declare> #ext_path #trait_name<#life_use #gen_use> for #crate_path::forward::Fwd where CGlueT: #ext_path #trait_name<#life_use #gen_use>, #gen_where_bounds { 93 | #wrapped_types 94 | #impls 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /cglue-gen/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! CGlue Code Generator 2 | //! 3 | //! This crate provides implementations of all procedural macros inside `cglue-macro`. 4 | 5 | pub mod ext; 6 | pub mod forward; 7 | pub mod func; 8 | pub mod generics; 9 | pub mod trait_groups; 10 | pub mod traits; 11 | pub mod util; 12 | -------------------------------------------------------------------------------- /cglue-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cglue-macro" 3 | version = "0.3.1" 4 | authors = ["Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | description = "FFI safe code generation macros for making plugins and C-compatible libraries" 7 | license = "MIT" 8 | documentation = "https://docs.rs/cglue-macro" 9 | repository = "https://github.com/h33p/cglue" 10 | keywords = [ "cglue", "abi", "ffi", "cbindgen", "macro" ] 11 | categories = [ "api-bindings", "accessibility", "parsing" ] 12 | readme = "../README.md" 13 | rust-version = "1.56" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | syn = { version = "1", features = ["full", "extra-traits"] } 20 | proc-macro2 = "1" 21 | quote = "1" 22 | cglue-gen = { version = "0.3", path = "../cglue-gen" } 23 | 24 | [features] 25 | default = [] 26 | rust_void = ["cglue-gen/rust_void"] 27 | unstable = ["cglue-gen/unstable"] 28 | layout_checks = ["cglue-gen/layout_checks"] 29 | task = ["cglue-gen/task"] 30 | futures = ["cglue-gen/futures", "task"] 31 | unwind_abi_default = ["cglue-gen/unwind_abi_default"] 32 | unwind_abi_ext = ["cglue-gen/unwind_abi_ext"] 33 | abi_stable11 = ["cglue-gen/abi_stable11"] 34 | -------------------------------------------------------------------------------- /cglue/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cglue" 3 | version = "0.3.5" 4 | authors = ["Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | description = "FFI safe abstractions for making plugins and C-compatible libraries" 7 | license = "MIT" 8 | documentation = "https://docs.rs/cglue" 9 | repository = "https://github.com/h33p/cglue" 10 | keywords = [ "cglue", "abi", "ffi", "cbindgen", "macro" ] 11 | categories = [ "api-bindings", "accessibility", "parsing" ] 12 | readme = "../README.md" 13 | rust-version = "1.56" 14 | 15 | [dependencies] 16 | cglue-macro = { version = "0.3", path = "../cglue-macro" } 17 | no-std-compat = { version = "0.4", features = ["alloc"] } 18 | serde = { version = "1", optional = true, default-features = false, features = ["derive", "alloc"] } 19 | try_default = { version = "= 1.0.0", optional = true } 20 | log = { version = "0.4", optional = true } 21 | tarc = { version = "0.1", default-features = false } 22 | _futures = { package = "futures", version = "0.3", optional = true, default-features = false } 23 | abi_stable10 = { package = "abi_stable", version = "0.10", optional = true, default-features = false } 24 | _abi_stable11 = { package = "abi_stable", version = "0.11", optional = true, default-features = false, features = ["rust_latest_stable"] } 25 | 26 | [build-dependencies] 27 | rustc_version = "0.4" 28 | 29 | [dev-dependencies] 30 | pollster = "0.2" 31 | 32 | [features] 33 | default = ["std"] 34 | std = ["no-std-compat/std", "tarc/std"] 35 | rust_void = ["cglue-macro/rust_void"] 36 | unstable = ["cglue-macro/unstable", "try_default"] 37 | task = ["cglue-macro/task"] 38 | layout_checks = ["cglue-macro/layout_checks", "abi_stable"] 39 | futures = ["_futures", "task", "cglue-macro/futures"] 40 | unwind_abi_default = ["cglue-macro/unwind_abi_default"] 41 | unwind_abi_ext = ["cglue-macro/unwind_abi_ext"] 42 | abi_stable11 = ["_abi_stable11", "cglue-macro/abi_stable11"] 43 | abi_stable = ["abi_stable10"] 44 | 45 | [package.metadata.docs.rs] 46 | features = ["std", "task", "futures"] 47 | -------------------------------------------------------------------------------- /cglue/build.rs: -------------------------------------------------------------------------------- 1 | use rustc_version::{version, Version}; 2 | 3 | fn main() { 4 | let cfgs = [ 5 | ("1.57.0", "const_panic_on_stable"), 6 | ("1.65.0", "gats_on_stable"), 7 | ("1.81.0", "c_unwind_on_stable"), 8 | ]; 9 | 10 | let version = version().unwrap(); 11 | 12 | for (v, c) in &cfgs { 13 | println!("cargo:rustc-check-cfg=cfg({})", c); 14 | if version >= Version::parse(v).unwrap() { 15 | println!("cargo:rustc-cfg={}", c); 16 | } 17 | } 18 | 19 | let test_cfgs = ["__cglue_force_no_unwind_abi"]; 20 | 21 | for c in &test_cfgs { 22 | println!("cargo:rustc-check-cfg=cfg({})", c); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cglue/src/boxed.rs: -------------------------------------------------------------------------------- 1 | //! # FFI-safe wrapped box. 2 | use crate::slice::CSliceMut; 3 | use crate::trait_group::c_void; 4 | use crate::trait_group::*; 5 | use core::ops::{Deref, DerefMut}; 6 | use core::ptr::NonNull; 7 | use std::boxed::Box; 8 | 9 | /// FFI-safe box 10 | /// 11 | /// This box has a static self reference, alongside a custom drop function. 12 | /// 13 | /// The drop function can be called from anywhere, it will free on correct allocator internally. 14 | #[repr(C)] 15 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 16 | pub struct CBox<'a, T: 'a> { 17 | // TODO: remove these remaps in 0.4 18 | #[cfg_attr( 19 | all(feature = "abi_stable10", not(feature = "abi_stable11")), 20 | sabi(unsafe_change_type = "&'a mut T") 21 | )] 22 | #[cfg_attr(feature = "abi_stable11", sabi(unsafe_change_type = &'a mut T))] 23 | instance: NonNull, 24 | #[cfg_attr( 25 | all(feature = "abi_stable10", not(feature = "abi_stable11")), 26 | sabi(unsafe_change_type = "Option") 27 | )] 28 | #[cfg_attr( 29 | feature = "abi_stable11", 30 | sabi(unsafe_change_type = Option) 31 | )] 32 | drop_fn: Option)>, 33 | #[cfg_attr( 34 | all(feature = "abi_stable10", not(feature = "abi_stable11")), 35 | sabi(unsafe_change_type = "::abi_stable::marker_type::UnsafeIgnoredType<()>") 36 | )] 37 | #[cfg_attr( 38 | feature = "abi_stable11", 39 | sabi(unsafe_change_type = ::abi_stable::marker_type::UnsafeIgnoredType<()>) 40 | )] 41 | _phantom: core::marker::PhantomData<&'a mut T>, 42 | } 43 | 44 | unsafe impl<'a, T: 'a> Send for CBox<'a, T> where &'a mut T: Send {} 45 | unsafe impl<'a, T: 'a> Sync for CBox<'a, T> where &'a mut T: Sync {} 46 | 47 | impl super::trait_group::IntoInner for CBox<'_, T> { 48 | type InnerTarget = T; 49 | 50 | unsafe fn into_inner(self) -> Self::InnerTarget { 51 | let b = Box::from_raw(self.instance.as_ptr()); 52 | std::mem::forget(self); 53 | *b 54 | } 55 | } 56 | 57 | impl Deref for CBox<'_, T> { 58 | type Target = T; 59 | 60 | fn deref(&self) -> &Self::Target { 61 | unsafe { &*self.instance.as_ptr() } 62 | } 63 | } 64 | 65 | impl DerefMut for CBox<'_, T> { 66 | fn deref_mut(&mut self) -> &mut Self::Target { 67 | unsafe { &mut *self.instance.as_ptr() } 68 | } 69 | } 70 | 71 | impl From> for CBox<'_, T> { 72 | fn from(this: Box) -> Self { 73 | let instance = unsafe { NonNull::new_unchecked(Box::into_raw(this)) }; 74 | Self { 75 | instance, 76 | drop_fn: Some(cglue_drop_box::), 77 | _phantom: core::marker::PhantomData, 78 | } 79 | } 80 | } 81 | 82 | impl From for CBox<'_, T> { 83 | fn from(this: T) -> Self { 84 | let b = Box::new(this); 85 | CBox::from(b) 86 | } 87 | } 88 | 89 | // TODO: Remove? Is this even needed? 90 | impl From<(T, NoContext)> for CBox<'_, T> { 91 | fn from((this, _): (T, NoContext)) -> Self { 92 | let b = Box::new(this); 93 | CBox::from(b) 94 | } 95 | } 96 | 97 | impl Drop for CBox<'_, T> { 98 | fn drop(&mut self) { 99 | if let Some(drop_fn) = self.drop_fn.take() { 100 | unsafe { drop_fn(self.instance) }; 101 | } 102 | } 103 | } 104 | 105 | // FIXME: express both Send and !Send box safely (https://github.com/h33p/cglue/issues/18) 106 | unsafe impl<'a, T: Send> Opaquable for CBox<'a, T> { 107 | type OpaqueTarget = CBox<'a, c_void>; 108 | } 109 | 110 | unsafe extern "C" fn cglue_drop_box(this: NonNull) { 111 | let _ = Box::from_raw(this.as_ptr()); 112 | } 113 | 114 | /// FFI-safe (unsized) boxed slice 115 | /// 116 | /// This box has a static self reference, alongside a custom drop function. 117 | /// 118 | /// The drop function can be called from anywhere, it will free on correct allocator internally. 119 | #[repr(C)] 120 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 121 | pub struct CSliceBox<'a, T: 'a> { 122 | instance: CSliceMut<'a, T>, 123 | drop_fn: Option)>, 124 | } 125 | 126 | impl Deref for CSliceBox<'_, T> { 127 | type Target = [T]; 128 | 129 | fn deref(&self) -> &Self::Target { 130 | &self.instance 131 | } 132 | } 133 | 134 | impl DerefMut for CSliceBox<'_, T> { 135 | fn deref_mut(&mut self) -> &mut Self::Target { 136 | &mut self.instance 137 | } 138 | } 139 | 140 | impl From> for CSliceBox<'_, T> { 141 | fn from(this: Box<[T]>) -> Self { 142 | let instance = Box::leak(this).into(); 143 | Self { 144 | instance, 145 | drop_fn: Some(cglue_drop_slice_box::), 146 | } 147 | } 148 | } 149 | 150 | impl Drop for CSliceBox<'_, T> { 151 | fn drop(&mut self) { 152 | if let Some(drop_fn) = self.drop_fn.take() { 153 | unsafe { drop_fn(&mut self.instance) }; 154 | } 155 | } 156 | } 157 | 158 | unsafe impl<'a, T> Opaquable for CSliceBox<'a, T> { 159 | type OpaqueTarget = CSliceBox<'a, c_void>; 160 | } 161 | 162 | unsafe extern "C" fn cglue_drop_slice_box(this: &mut CSliceMut<'_, T>) { 163 | // SAFETY: we extend the lifetime of the reference but free the underlying data immediately and 164 | // not use the reference again. 165 | let extended_instance = (this as *mut CSliceMut<_>).as_mut().unwrap(); 166 | let _ = Box::from_raw(extended_instance.as_slice_mut()); 167 | } 168 | -------------------------------------------------------------------------------- /cglue/src/callback.rs: -------------------------------------------------------------------------------- 1 | //! # FFI compatible callbacks 2 | //! 3 | //! The essence of them is to be simple, reliable, and flexible. Thus, every callback accepts a C 4 | //! function that takes 2 arguments: `context`, and `argument`. 5 | //! 6 | //! `context` is any type of context. We take a sized pointer to it. It can hold anything like a 7 | //! closure that we then wrap for callback functionality. 8 | //! 9 | //! `argument` is the actual argument that gets produced and passed over. This will be variable, 10 | //! and it is passed as value. 11 | //! 12 | //! `OpaqueCallback`, as the name suggests, marks the `context` as opaque, casts it to `c_void` 13 | //! pointer. It allows the code not to care about what's behind the context, it just knows that it 14 | //! needs to pass it over to the callback. 15 | 16 | use crate::trait_group::c_void; 17 | use std::prelude::v1::*; 18 | 19 | // C style callbacks that are needed so that C code can easily use callback like functions 20 | #[repr(transparent)] 21 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 22 | pub struct OpaqueCallback<'a, T: 'a>(Callback<'a, c_void, T>); 23 | 24 | impl<'a, T> OpaqueCallback<'a, T> { 25 | #[must_use = "this value is the stopping condition"] 26 | pub fn call(&mut self, arg: T) -> bool { 27 | (self.0.func)(self.0.context, arg) 28 | } 29 | } 30 | 31 | #[repr(C)] 32 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 33 | pub struct Callback<'a, T: 'a, F> { 34 | context: &'a mut T, 35 | func: extern "C" fn(&mut T, F) -> bool, 36 | } 37 | 38 | impl<'a, T, F> From> for OpaqueCallback<'a, F> { 39 | fn from(callback: Callback<'a, T, F>) -> Self { 40 | Self(callback.into_opaque()) 41 | } 42 | } 43 | 44 | impl<'a, T, F> Callback<'a, T, F> { 45 | pub fn into_opaque(self) -> Callback<'a, c_void, F> { 46 | unsafe { 47 | Callback { 48 | context: &mut *(self.context as *mut T as *mut c_void), 49 | func: std::mem::transmute(self.func), 50 | } 51 | } 52 | } 53 | 54 | pub fn new(context: &'a mut T, func: extern "C" fn(&mut T, F) -> bool) -> Self { 55 | Self { context, func } 56 | } 57 | } 58 | 59 | impl<'a, T: FnMut(F) -> bool, F> From<&'a mut T> for OpaqueCallback<'a, F> { 60 | fn from(func: &'a mut T) -> Self { 61 | extern "C" fn callback bool, F>(func: &mut T, context: F) -> bool { 62 | func(context) 63 | } 64 | 65 | Callback { 66 | context: func, 67 | func: callback::, 68 | } 69 | .into() 70 | } 71 | } 72 | 73 | impl<'a, T> From<&'a mut Vec> for OpaqueCallback<'a, T> { 74 | fn from(vec: &'a mut Vec) -> Self { 75 | extern "C" fn callback(v: &mut Vec, context: T) -> bool { 76 | v.push(context); 77 | true 78 | } 79 | 80 | Callback { 81 | context: vec, 82 | func: callback::, 83 | } 84 | .into() 85 | } 86 | } 87 | 88 | impl<'a, T> std::iter::Extend for OpaqueCallback<'a, T> { 89 | fn extend>(&mut self, iter: F) { 90 | for item in iter { 91 | if !self.call(item) { 92 | break; 93 | } 94 | } 95 | } 96 | } 97 | 98 | pub trait FeedCallback { 99 | fn feed_into_mut(self, callback: &mut OpaqueCallback) -> usize; 100 | 101 | fn feed_into(self, mut callback: OpaqueCallback) -> usize 102 | where 103 | Self: Sized, 104 | { 105 | self.feed_into_mut(&mut callback) 106 | } 107 | } 108 | 109 | impl, T> FeedCallback for I { 110 | fn feed_into_mut(self, callback: &mut OpaqueCallback) -> usize { 111 | let mut cnt = 0; 112 | for v in self { 113 | cnt += 1; 114 | if !callback.call(v) { 115 | break; 116 | } 117 | } 118 | cnt 119 | } 120 | } 121 | 122 | pub trait FromExtend: Extend + Sized { 123 | #[allow(clippy::wrong_self_convention)] 124 | fn from_extend(&mut self) -> OpaqueCallback { 125 | extern "C" fn callback, T>(v: &mut C, context: T) -> bool { 126 | v.extend(Some(context)); 127 | true 128 | } 129 | 130 | Callback { 131 | context: self, 132 | func: callback::, 133 | } 134 | .into() 135 | } 136 | } 137 | 138 | impl, T> FromExtend for C {} 139 | 140 | pub trait Callbackable { 141 | fn call(&mut self, data: T) -> bool; 142 | } 143 | 144 | impl Callbackable for &mut OpaqueCallback<'_, T> { 145 | fn call(&mut self, data: T) -> bool { 146 | (*self).call(data) 147 | } 148 | } 149 | 150 | impl Callbackable for OpaqueCallback<'_, T> { 151 | fn call(&mut self, data: T) -> bool { 152 | (self.0.func)(self.0.context, data) 153 | } 154 | } 155 | 156 | impl bool> Callbackable for F { 157 | fn call(&mut self, data: T) -> bool { 158 | (*self)(data) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /cglue/src/forward.rs: -------------------------------------------------------------------------------- 1 | //! # Forwards a trait on references 2 | //! 3 | //! Combined with the `#[cglue_forward]` macro forward implementation will be generated on `Fwd` 4 | //! type. Whether `Fwd` implements the trait depends purely on whether the trait has 5 | //! functions with mutable references or not. 6 | 7 | use crate::trait_group::Opaquable; 8 | use ::core::ops::{Deref, DerefMut}; 9 | 10 | #[repr(transparent)] 11 | #[derive(Clone, Copy, Debug)] 12 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 13 | pub struct Fwd(pub T); 14 | 15 | impl, F> Deref for Fwd { 16 | type Target = F; 17 | 18 | fn deref(&self) -> &Self::Target { 19 | self.0.deref() 20 | } 21 | } 22 | 23 | impl, F> DerefMut for Fwd { 24 | fn deref_mut(&mut self) -> &mut Self::Target { 25 | self.0.deref_mut() 26 | } 27 | } 28 | 29 | pub trait Forward: Sized { 30 | fn forward(self) -> Fwd { 31 | Fwd(self) 32 | } 33 | } 34 | 35 | pub trait ForwardMut: Sized { 36 | fn forward_mut(self) -> Fwd { 37 | Fwd(self) 38 | } 39 | } 40 | 41 | impl Forward for T {} 42 | impl ForwardMut for T {} 43 | 44 | unsafe impl Opaquable for Fwd { 45 | type OpaqueTarget = Fwd; 46 | } 47 | -------------------------------------------------------------------------------- /cglue/src/from2.rs: -------------------------------------------------------------------------------- 1 | //! # From on Into 2 | 3 | /// Wrapper trait that is implemented on `Into` types. 4 | /// 5 | /// This trait is purely needed for type parameter inferring purposes, where `Into` can not be used, 6 | /// but `From` would make it not as versatile. This trait acts like `From`, but is implemented when 7 | /// only `Into` is implemented. 8 | pub trait From2 { 9 | fn from2(other: T) -> Self; 10 | } 11 | 12 | impl, F> From2 for F { 13 | fn from2(other: T) -> Self { 14 | other.into() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cglue/src/iter.rs: -------------------------------------------------------------------------------- 1 | //! # FFI compatible iterators 2 | //! 3 | //! It is a simple interface that allows to pass streams into functions. 4 | 5 | use crate::trait_group::c_void; 6 | use core::mem::MaybeUninit; 7 | use std::prelude::v1::*; 8 | 9 | /// FFI compatible iterator. 10 | /// 11 | /// Any mutable reference to an iterator can be converted to a `CIterator`. 12 | /// 13 | /// `CIterator` implements `Iterator`. 14 | /// 15 | /// # Examples 16 | /// 17 | /// Using [`AsCIterator`](AsCIterator) helper: 18 | /// 19 | /// ``` 20 | /// use cglue::iter::{CIterator, AsCIterator}; 21 | /// 22 | /// extern "C" fn sum_all(iter: CIterator) -> usize { 23 | /// iter.sum() 24 | /// } 25 | /// 26 | /// let mut iter = (0..10).map(|v| v * v); 27 | /// 28 | /// assert_eq!(sum_all(iter.as_citer()), 285); 29 | /// ``` 30 | /// 31 | /// Converting with `Into` trait: 32 | /// 33 | /// ``` 34 | /// use cglue::iter::{CIterator, AsCIterator}; 35 | /// 36 | /// extern "C" fn sum_all(iter: CIterator) -> usize { 37 | /// iter.sum() 38 | /// } 39 | /// 40 | /// let mut iter = (0..=10).map(|v| v * v); 41 | /// 42 | /// assert_eq!(sum_all((&mut iter).into()), 385); 43 | /// ``` 44 | #[repr(C)] 45 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 46 | pub struct CIterator<'a, T> { 47 | iter: &'a mut c_void, 48 | func: extern "C" fn(&mut c_void, out: &mut MaybeUninit) -> i32, 49 | } 50 | 51 | impl<'a, I: Iterator, T> From<&'a mut I> for CIterator<'a, T> { 52 | fn from(iter: &'a mut I) -> Self { 53 | CIterator::new(iter) 54 | } 55 | } 56 | 57 | impl<'a, T> CIterator<'a, T> { 58 | pub fn new>(iter: &'a mut I) -> Self { 59 | extern "C" fn func, T>( 60 | iter: &mut I, 61 | out: &mut MaybeUninit, 62 | ) -> i32 { 63 | match iter.next() { 64 | Some(e) => { 65 | unsafe { out.as_mut_ptr().write(e) }; 66 | 0 67 | } 68 | None => 1, 69 | } 70 | } 71 | 72 | // SAFETY: type erasure is safe here, because the values are encapsulated and always in 73 | // a pair. 74 | let iter = unsafe { (iter as *mut _ as *mut c_void).as_mut().unwrap() }; 75 | let func = func:: as extern "C" fn(_, _) -> _; 76 | let func = unsafe { std::mem::transmute::<_, _>(func) }; 77 | 78 | Self { iter, func } 79 | } 80 | } 81 | 82 | impl<'a, T> Iterator for CIterator<'a, T> { 83 | type Item = T; 84 | 85 | fn next(&mut self) -> Option { 86 | let mut out = MaybeUninit::uninit(); 87 | if (self.func)(self.iter, &mut out) == 0 { 88 | Some(unsafe { out.assume_init() }) 89 | } else { 90 | None 91 | } 92 | } 93 | } 94 | 95 | pub trait AsCIterator: Iterator + Sized { 96 | fn as_citer(&mut self) -> CIterator { 97 | self.into() 98 | } 99 | } 100 | 101 | impl AsCIterator for T {} 102 | -------------------------------------------------------------------------------- /cglue/src/option.rs: -------------------------------------------------------------------------------- 1 | //! # FFI safe option. 2 | 3 | /// FFI-safe Option. 4 | /// 5 | /// This type is not really meant for general use, but rather as a last-resort conversion for type 6 | /// wrapping. 7 | /// 8 | /// Typical workflow would include temporarily converting into/from COption. 9 | #[repr(C)] 10 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 11 | #[derive(Clone, Copy)] 12 | pub enum COption { 13 | None, 14 | Some(T), 15 | } 16 | 17 | impl Default for COption { 18 | fn default() -> Self { 19 | Self::None 20 | } 21 | } 22 | 23 | impl From> for COption { 24 | fn from(opt: Option) -> Self { 25 | match opt { 26 | None => Self::None, 27 | Some(t) => Self::Some(t), 28 | } 29 | } 30 | } 31 | 32 | impl From> for Option { 33 | fn from(opt: COption) -> Self { 34 | match opt { 35 | COption::None => None, 36 | COption::Some(t) => Some(t), 37 | } 38 | } 39 | } 40 | 41 | impl COption { 42 | pub fn is_some(&self) -> bool { 43 | matches!(*self, COption::Some(_)) 44 | } 45 | 46 | pub fn unwrap(self) -> T { 47 | match self { 48 | COption::Some(val) => val, 49 | COption::None => panic!("called `COption::unwrap()` on a `None` value"), 50 | } 51 | } 52 | 53 | pub fn as_ref(&self) -> Option<&T> { 54 | match *self { 55 | COption::Some(ref x) => Some(x), 56 | COption::None => None, 57 | } 58 | } 59 | 60 | pub fn as_mut(&mut self) -> Option<&mut T> { 61 | match *self { 62 | COption::Some(ref mut x) => Some(x), 63 | COption::None => None, 64 | } 65 | } 66 | 67 | pub fn take(&mut self) -> Option { 68 | core::mem::take(self).into() 69 | } 70 | } 71 | 72 | #[cfg(feature = "serde")] 73 | use core::fmt; 74 | #[cfg(feature = "serde")] 75 | use core::marker::PhantomData; 76 | #[cfg(feature = "serde")] 77 | use serde::{de, ser, Deserialize, Serialize}; 78 | 79 | #[cfg(feature = "serde")] 80 | struct COptionVisitor { 81 | marker: PhantomData, 82 | } 83 | 84 | #[cfg(feature = "serde")] 85 | impl<'de, T> de::Visitor<'de> for COptionVisitor 86 | where 87 | T: Deserialize<'de>, 88 | { 89 | type Value = COption; 90 | 91 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 92 | formatter.write_str("option") 93 | } 94 | 95 | #[inline] 96 | fn visit_unit(self) -> Result 97 | where 98 | E: de::Error, 99 | { 100 | Ok(COption::None) 101 | } 102 | 103 | #[inline] 104 | fn visit_none(self) -> Result 105 | where 106 | E: de::Error, 107 | { 108 | Ok(COption::None) 109 | } 110 | 111 | #[inline] 112 | fn visit_some(self, deserializer: D) -> Result 113 | where 114 | D: de::Deserializer<'de>, 115 | { 116 | T::deserialize(deserializer).map(COption::Some) 117 | } 118 | 119 | /*#[doc(hidden)] 120 | fn __private_visit_untagged_option(self, deserializer: D) -> Result 121 | where 122 | D: Deserializer<'de>, 123 | { 124 | Ok(T::deserialize(deserializer).ok()) 125 | }*/ 126 | } 127 | 128 | #[cfg(feature = "serde")] 129 | impl<'de, T> Deserialize<'de> for COption 130 | where 131 | T: Deserialize<'de>, 132 | { 133 | fn deserialize(deserializer: D) -> Result 134 | where 135 | D: de::Deserializer<'de>, 136 | { 137 | deserializer.deserialize_option(COptionVisitor { 138 | marker: PhantomData, 139 | }) 140 | } 141 | } 142 | 143 | #[cfg(feature = "serde")] 144 | impl Serialize for COption 145 | where 146 | T: Serialize, 147 | { 148 | #[inline] 149 | fn serialize(&self, serializer: S) -> Result 150 | where 151 | S: ser::Serializer, 152 | { 153 | match *self { 154 | COption::Some(ref value) => serializer.serialize_some(value), 155 | COption::None => serializer.serialize_none(), 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /cglue/src/repr_cstring.rs: -------------------------------------------------------------------------------- 1 | //! # Null-terminated transparent C-strings. 2 | 3 | use std::prelude::v1::*; 4 | use std::ptr::NonNull; 5 | use std::slice::*; 6 | use std::str::from_utf8_unchecked; 7 | 8 | #[cfg(feature = "std")] 9 | use std::os::raw::c_char; 10 | #[cfg(not(feature = "std"))] 11 | #[allow(non_camel_case_types)] 12 | pub type c_char = i8; 13 | 14 | /// Wrapper around null-terminated C-style strings. 15 | /// 16 | /// Analog to Rust's `String`, [`ReprCString`] owns the underlying data. 17 | #[repr(transparent)] 18 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 19 | pub struct ReprCString(NonNull); 20 | 21 | // The underlying pointer isn't being mutated after construction, 22 | // hence it is safe to assume access to the raw pointer is both Send + Sync 23 | unsafe impl Send for ReprCString {} 24 | unsafe impl Sync for ReprCString {} 25 | 26 | unsafe fn string_size(mut ptr: *const c_char) -> usize { 27 | (1..) 28 | .take_while(|_| { 29 | let ret = *ptr; 30 | ptr = ptr.offset(1); 31 | ret != 0 32 | }) 33 | .last() 34 | .unwrap_or(0) 35 | + 1 36 | } 37 | 38 | impl From<&[u8]> for ReprCString { 39 | fn from(from: &[u8]) -> Self { 40 | let b = Box::new(from.to_vec().into_boxed_slice()); 41 | Self(NonNull::new(Box::leak(b).as_mut_ptr() as *mut _).unwrap()) 42 | } 43 | } 44 | 45 | impl From<&str> for ReprCString { 46 | fn from(from: &str) -> Self { 47 | let b = from 48 | .bytes() 49 | .take_while(|&b| b != 0) 50 | .chain(Some(0)) 51 | .collect::>() 52 | .into_boxed_slice(); 53 | Self(NonNull::new(Box::leak(b).as_mut_ptr() as *mut _).unwrap()) 54 | } 55 | } 56 | 57 | impl From for ReprCString { 58 | fn from(from: String) -> Self { 59 | from.as_str().into() 60 | } 61 | } 62 | 63 | impl<'a> std::borrow::Borrow> for ReprCString { 64 | fn borrow(&self) -> &ReprCStr<'a> { 65 | unsafe { &*(self as *const _ as *const _) } 66 | } 67 | } 68 | 69 | impl AsRef for ReprCString { 70 | fn as_ref(&self) -> &str { 71 | unsafe { 72 | from_utf8_unchecked(from_raw_parts( 73 | self.0.as_ptr() as *const _, 74 | string_size(self.0.as_ptr()) - 1, 75 | )) 76 | } 77 | } 78 | } 79 | 80 | impl std::ops::Deref for ReprCString { 81 | type Target = str; 82 | 83 | fn deref(&self) -> &Self::Target { 84 | self.as_ref() 85 | } 86 | } 87 | 88 | impl Drop for ReprCString { 89 | fn drop(&mut self) { 90 | let _ = unsafe { 91 | Box::from_raw(from_raw_parts_mut( 92 | self.0.as_ptr() as *mut _, 93 | string_size(self.0.as_ptr()), 94 | )) 95 | }; 96 | } 97 | } 98 | 99 | impl Clone for ReprCString { 100 | fn clone(&self) -> Self { 101 | self.as_ref().into() 102 | } 103 | } 104 | 105 | impl std::fmt::Display for ReprCString { 106 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 107 | f.pad(self.as_ref()) 108 | } 109 | } 110 | 111 | impl std::fmt::Debug for ReprCString { 112 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 113 | f.debug_struct("ReprCString") 114 | .field("0", &self.as_ref()) 115 | .finish() 116 | } 117 | } 118 | 119 | impl std::hash::Hash for ReprCString { 120 | fn hash(&self, state: &mut H) { 121 | self.as_ref().hash(state); 122 | } 123 | } 124 | 125 | impl std::cmp::PartialEq for ReprCString { 126 | fn eq(&self, other: &Self) -> bool { 127 | self.as_ref().eq(other.as_ref()) 128 | } 129 | } 130 | 131 | impl std::cmp::Eq for ReprCString {} 132 | 133 | #[cfg(feature = "serde")] 134 | impl serde::Serialize for ReprCString { 135 | fn serialize(&self, serializer: S) -> std::result::Result 136 | where 137 | S: serde::Serializer, 138 | { 139 | serializer.serialize_str(self.as_ref()) 140 | } 141 | } 142 | 143 | #[cfg(feature = "serde")] 144 | impl<'de> serde::Deserialize<'de> for ReprCString { 145 | fn deserialize(deserializer: D) -> std::result::Result 146 | where 147 | D: serde::Deserializer<'de>, 148 | { 149 | struct ReprCStringVisitor; 150 | 151 | impl<'de> ::serde::de::Visitor<'de> for ReprCStringVisitor { 152 | type Value = ReprCString; 153 | 154 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 155 | formatter.write_str("a string") 156 | } 157 | 158 | fn visit_str(self, v: &str) -> Result 159 | where 160 | E: ::serde::de::Error, 161 | { 162 | Ok(v.into()) 163 | } 164 | } 165 | 166 | deserializer.deserialize_str(ReprCStringVisitor) 167 | } 168 | } 169 | 170 | #[cfg(test)] 171 | mod tests { 172 | use super::ReprCString; 173 | 174 | #[test] 175 | fn string_size_matches() { 176 | assert_eq!(0, ReprCString::from("").as_ref().len()); 177 | assert_eq!(1, ReprCString::from("1").as_ref().len()); 178 | assert_eq!(5, ReprCString::from("12345").as_ref().len()); 179 | } 180 | } 181 | 182 | /// Wrapper around null-terminated C-style strings. 183 | /// 184 | /// Analog to Rust's `str`, [`ReprCStr`] borrows the underlying data. 185 | #[repr(transparent)] 186 | #[derive(Copy, Clone)] 187 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 188 | pub struct ReprCStr<'a>(&'a c_char); 189 | 190 | #[cfg(feature = "std")] 191 | use std::ffi::CStr; 192 | 193 | #[cfg(feature = "std")] 194 | impl<'a> From<&'a CStr> for ReprCStr<'a> { 195 | fn from(from: &'a CStr) -> Self { 196 | Self(unsafe { (from.as_ptr() as *const c_char).as_ref() }.unwrap()) 197 | } 198 | } 199 | 200 | impl<'a> AsRef for ReprCStr<'a> { 201 | fn as_ref(&self) -> &str { 202 | unsafe { 203 | from_utf8_unchecked(from_raw_parts( 204 | self.0 as *const _ as *const _, 205 | string_size(self.0) - 1, 206 | )) 207 | } 208 | } 209 | } 210 | 211 | impl<'a> std::fmt::Display for ReprCStr<'a> { 212 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 213 | f.pad(self.as_ref()) 214 | } 215 | } 216 | 217 | impl<'a> std::fmt::Debug for ReprCStr<'a> { 218 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 219 | f.debug_struct("ReprCStr<'a>") 220 | .field("0", &self.as_ref()) 221 | .finish() 222 | } 223 | } 224 | 225 | impl<'a> std::hash::Hash for ReprCStr<'a> { 226 | fn hash(&self, state: &mut H) { 227 | self.as_ref().hash(state); 228 | } 229 | } 230 | 231 | impl<'a> std::cmp::PartialEq for ReprCStr<'a> { 232 | fn eq(&self, other: &Self) -> bool { 233 | self.as_ref().eq(other.as_ref()) 234 | } 235 | } 236 | 237 | impl<'a> std::cmp::Eq for ReprCStr<'a> {} 238 | 239 | #[cfg(feature = "serde")] 240 | impl<'a> serde::Serialize for ReprCStr<'a> { 241 | fn serialize(&self, serializer: S) -> std::result::Result 242 | where 243 | S: serde::Serializer, 244 | { 245 | serializer.serialize_str(self.as_ref()) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /cglue/src/result.rs: -------------------------------------------------------------------------------- 1 | //! # FFI safe result. 2 | //! 3 | //! This module contains several key parts: 4 | //! 5 | //! ## [CResult](crate::result::CResult) 6 | //! 7 | //! It is a simple `#[repr(C)]` enum that is equivalent and interchangeable with `Result`. 8 | //! 9 | //! ## [IntError](crate::result::IntError) 10 | //! 11 | //! [IntError](crate::result::IntError) is a type that allows for efficient FFI-boundary crossing 12 | //! and simple interop with C code. It takes a `Result`, and splits it up to 2 distinct parts 13 | //! - `ok_out` pointer, and an integer return value. Value of zero always means success, and that 14 | //! `ok_out` was filled, whereas any other value can represent a specific meaning `E` must specify 15 | //! by itself. 16 | //! 17 | //! ## [IntResult](crate::result::IntResult) 18 | //! 19 | //! It is a helper trait that is implemented on all `Result` types where `E` implements 20 | //! [IntError](crate::result::IntError). 21 | //! 22 | use core::mem::MaybeUninit; 23 | use core::num::NonZeroI32; 24 | 25 | /// FFI safe result. 26 | /// 27 | /// This type is not meant for general use, but rather as a last-resort conversion for type wrapping. 28 | /// 29 | /// Typical workflow would include temporarily converting into/from CResult. 30 | /// 31 | /// But preferred way to pass results efficiently would be to implement `IntError` trait on the `E` 32 | /// type. 33 | #[repr(C)] 34 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 35 | pub enum CResult { 36 | Ok(T), 37 | Err(E), 38 | } 39 | 40 | impl From> for CResult { 41 | fn from(opt: Result) -> Self { 42 | match opt { 43 | Ok(t) => Self::Ok(t), 44 | Err(e) => Self::Err(e), 45 | } 46 | } 47 | } 48 | 49 | impl From> for Result { 50 | fn from(opt: CResult) -> Self { 51 | match opt { 52 | CResult::Ok(t) => Ok(t), 53 | CResult::Err(e) => Err(e), 54 | } 55 | } 56 | } 57 | 58 | impl CResult { 59 | pub fn is_ok(&self) -> bool { 60 | matches!(*self, CResult::Ok(_)) 61 | } 62 | 63 | pub fn is_err(&self) -> bool { 64 | matches!(*self, CResult::Err(_)) 65 | } 66 | 67 | pub fn unwrap(self) -> T 68 | where 69 | E: core::fmt::Debug, 70 | { 71 | Result::from(self).unwrap() 72 | } 73 | 74 | pub fn ok(self) -> Option { 75 | match self { 76 | CResult::Ok(x) => Some(x), 77 | _ => None, 78 | } 79 | } 80 | 81 | pub fn as_ref(&self) -> Result<&T, &E> { 82 | match *self { 83 | CResult::Ok(ref x) => Ok(x), 84 | CResult::Err(ref e) => Err(e), 85 | } 86 | } 87 | 88 | pub fn as_mut(&mut self) -> Result<&mut T, &mut E> { 89 | match *self { 90 | CResult::Ok(ref mut x) => Ok(x), 91 | CResult::Err(ref mut e) => Err(e), 92 | } 93 | } 94 | } 95 | 96 | /// Helper trait for integer errors. 97 | /// 98 | /// This trait essentially forwards [`into_int_result`](crate::result::into_int_result), and 99 | /// [`into_int_out_result`](crate::result::into_int_out_result) functions for easier access. 100 | pub trait IntResult { 101 | fn into_int_result(self) -> i32; 102 | fn into_int_out_result(self, ok_out: &mut MaybeUninit) -> i32; 103 | } 104 | 105 | impl IntResult for Result { 106 | fn into_int_result(self) -> i32 { 107 | into_int_result(self) 108 | } 109 | 110 | fn into_int_out_result(self, ok_out: &mut MaybeUninit) -> i32 { 111 | into_int_out_result(self, ok_out) 112 | } 113 | } 114 | 115 | /// Represents an integer-convertable error value. 116 | /// 117 | /// This trait can be implemented for error types to allow for more 118 | /// efficient conversion and more user-friendly usage from C API side. 119 | pub trait IntError { 120 | fn into_int_err(self) -> NonZeroI32; 121 | fn from_int_err(err: NonZeroI32) -> Self; 122 | } 123 | 124 | #[cfg(feature = "std")] 125 | impl IntError for std::io::Error { 126 | fn into_int_err(self) -> NonZeroI32 { 127 | let err = self.raw_os_error().unwrap_or(0); 128 | 129 | let err = if err == 0 { 130 | // TODO: match numbers here for io::ErrorKind 131 | 0xffff 132 | } else { 133 | err 134 | }; 135 | 136 | NonZeroI32::new(err).unwrap() 137 | } 138 | 139 | fn from_int_err(err: NonZeroI32) -> Self { 140 | Self::from_raw_os_error(err.get()) 141 | } 142 | } 143 | 144 | impl IntError for () { 145 | fn into_int_err(self) -> NonZeroI32 { 146 | NonZeroI32::new(1).unwrap() 147 | } 148 | 149 | fn from_int_err(_err: NonZeroI32) -> Self {} 150 | } 151 | 152 | impl IntError for core::fmt::Error { 153 | fn into_int_err(self) -> NonZeroI32 { 154 | NonZeroI32::new(1).unwrap() 155 | } 156 | 157 | fn from_int_err(_err: NonZeroI32) -> Self { 158 | Self 159 | } 160 | } 161 | 162 | /// Convert result into an integer error value. 163 | /// 164 | /// Returned value of `0` means that the result is of Ok value, otherwise it is an error. 165 | /// 166 | /// # Arguments 167 | /// 168 | /// * `res` - result to convert. 169 | pub fn into_int_result(res: Result) -> i32 { 170 | match res { 171 | Ok(_) => 0, 172 | Err(e) => e.into_int_err().get(), 173 | } 174 | } 175 | 176 | /// Convert result into an integer error value, potentially writing the Ok value. 177 | /// 178 | /// If return value is `0`, `ok_out` will have initialised data, otherwise not. 179 | /// 180 | /// # Arguments 181 | /// 182 | /// * `res` - result to convert. 183 | /// * `ok_out` - target output for Ok value. 184 | pub fn into_int_out_result(res: Result, ok_out: &mut MaybeUninit) -> i32 { 185 | match res { 186 | Ok(v) => { 187 | unsafe { ok_out.as_mut_ptr().write(v) }; 188 | 0 189 | } 190 | Err(e) => e.into_int_err().get(), 191 | } 192 | } 193 | 194 | /// Convert from error code to concrete result. 195 | /// 196 | /// # Arguments 197 | /// 198 | /// * `res` - result int value. Value of `0` means `Ok`. 199 | /// * `ok_val` - Ok value to use, if result is `Ok`. 200 | /// 201 | /// # Safety 202 | /// 203 | /// `ok_val` must be initialised if `res = 0`. This can be used safely in conjunction with 204 | /// `into_int_out_result`, assuming arguments are not modified in-between the calls. 205 | pub unsafe fn from_int_result(res: i32, ok_val: MaybeUninit) -> Result { 206 | match NonZeroI32::new(res) { 207 | None => Ok(ok_val.assume_init()), 208 | Some(e) => Err(E::from_int_err(e)), 209 | } 210 | } 211 | 212 | /// Convert from error code to Ok or Err. 213 | /// 214 | /// # Arguments 215 | /// 216 | /// * `res` - result int value. Value of `0` will return `Ok`. 217 | pub fn from_int_result_empty(res: i32) -> Result<(), E> { 218 | match NonZeroI32::new(res) { 219 | None => Ok(()), 220 | Some(e) => Err(E::from_int_err(e)), 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /cglue/src/slice.rs: -------------------------------------------------------------------------------- 1 | //! # Slices as C-structs. 2 | //! 3 | //! These slices are then transferable across the FFI boundary safely. 4 | 5 | use core::convert::TryFrom; 6 | use core::marker::PhantomData; 7 | use std::prelude::v1::*; 8 | 9 | /// Wrapper around const slices. 10 | /// 11 | /// This is meant as a safe type to pass across the FFI boundary with similar semantics as regular 12 | /// slice. However, not all functionality is present, use the slice conversion functions. 13 | /// 14 | /// # Examples 15 | /// 16 | /// Simple conversion: 17 | /// 18 | /// ``` 19 | /// use cglue::slice::CSliceRef; 20 | /// 21 | /// let arr = [0, 5, 3, 2]; 22 | /// 23 | /// let cslice = CSliceRef::from(&arr[..]); 24 | /// 25 | /// let slice = cslice.as_slice(); 26 | /// 27 | /// assert_eq!(&arr, slice); 28 | /// ``` 29 | #[repr(C)] 30 | #[derive(Clone, Copy)] 31 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 32 | pub struct CSliceRef<'a, T: 'a> { 33 | data: *const T, 34 | len: usize, 35 | _lifetime: PhantomData<&'a T>, 36 | } 37 | 38 | unsafe impl<'a, T> Send for CSliceRef<'a, T> where T: Send {} 39 | unsafe impl<'a, T> Sync for CSliceRef<'a, T> where T: Sync {} 40 | 41 | impl core::fmt::Debug for CSliceRef<'_, T> 42 | where 43 | for<'a> &'a [T]: core::fmt::Debug, 44 | { 45 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 46 | f.debug_struct("CSliceRef") 47 | .field("data", &self.data) 48 | .field("len", &self.len) 49 | .field("slice", &self.as_slice()); 50 | Ok(()) 51 | } 52 | } 53 | 54 | impl core::fmt::Display for CSliceRef<'_, u8> { 55 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 56 | write!(f, "{}", String::from_utf8_lossy(self.as_slice())) 57 | } 58 | } 59 | 60 | impl<'a, T> CSliceRef<'a, T> { 61 | /// Get the length of the slice. 62 | pub const fn len(&self) -> usize { 63 | self.len 64 | } 65 | 66 | /// Check if the slice is empty. 67 | pub const fn is_empty(&self) -> bool { 68 | self.len() == 0 69 | } 70 | 71 | /// Get the raw pointer to the slice. 72 | pub const fn as_ptr(&self) -> *const T { 73 | self.data 74 | } 75 | 76 | /// Reinterpret as a Rust slice. 77 | pub fn as_slice(&'a self) -> &'a [T] { 78 | unsafe { core::slice::from_raw_parts(self.data, self.len) } 79 | } 80 | 81 | /// Construct from a Rust slice. 82 | pub const fn from_slice(s: &'a [T]) -> Self { 83 | Self { 84 | data: s.as_ptr(), 85 | len: s.len(), 86 | _lifetime: PhantomData {}, 87 | } 88 | } 89 | } 90 | 91 | impl<'a> CSliceRef<'a, u8> { 92 | /// Construct from a utf-8 string. 93 | pub const fn from_str(s: &'a str) -> Self { 94 | Self::from_slice(s.as_bytes()) 95 | } 96 | } 97 | 98 | impl<'a> CSliceRef<'a, u8> { 99 | /// Convert into a utf-8 string. 100 | /// 101 | /// # Safety 102 | /// 103 | /// The slice must be a valid utf-8 string. 104 | pub unsafe fn into_str(self) -> &'a str { 105 | core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.data, self.len)) 106 | } 107 | } 108 | 109 | impl<'a> From<&'a str> for CSliceRef<'a, u8> { 110 | fn from(from: &'a str) -> Self { 111 | Self::from_str(from) 112 | } 113 | } 114 | 115 | impl<'a, T> From<&'a [T]> for CSliceRef<'a, T> { 116 | fn from(from: &'a [T]) -> Self { 117 | Self::from_slice(from) 118 | } 119 | } 120 | 121 | impl<'a, T> From> for &'a [T] { 122 | fn from(from: CSliceRef<'a, T>) -> Self { 123 | unsafe { core::slice::from_raw_parts(from.data, from.len) } 124 | } 125 | } 126 | 127 | impl<'a> TryFrom> for &'a str { 128 | type Error = core::str::Utf8Error; 129 | 130 | fn try_from(from: CSliceRef<'a, u8>) -> Result { 131 | core::str::from_utf8(unsafe { core::slice::from_raw_parts(from.data, from.len) }) 132 | } 133 | } 134 | 135 | impl core::ops::Deref for CSliceRef<'_, T> { 136 | type Target = [T]; 137 | 138 | fn deref(&self) -> &Self::Target { 139 | self.as_slice() 140 | } 141 | } 142 | 143 | /// Wrapper around mutable slices. 144 | /// 145 | /// This is meant as a safe type to pass across the FFI boundary with similar semantics as regular 146 | /// slice. However, not all functionality is present, use the slice conversion functions. 147 | #[repr(C)] 148 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 149 | pub struct CSliceMut<'a, T: 'a> { 150 | data: *mut T, 151 | len: usize, 152 | _lifetime: PhantomData<&'a T>, 153 | } 154 | 155 | unsafe impl<'a, T> Send for CSliceMut<'a, T> where T: Send {} 156 | unsafe impl<'a, T> Sync for CSliceMut<'a, T> where T: Sync {} 157 | 158 | impl core::fmt::Debug for CSliceMut<'_, T> 159 | where 160 | for<'a> &'a [T]: core::fmt::Debug, 161 | { 162 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { 163 | f.debug_struct("CSliceMut") 164 | .field("data", &self.data) 165 | .field("len", &self.len) 166 | .field("slice", &self.as_slice()); 167 | Ok(()) 168 | } 169 | } 170 | 171 | impl core::fmt::Display for CSliceMut<'_, u8> { 172 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 173 | write!(f, "{}", String::from_utf8_lossy(self.as_slice())) 174 | } 175 | } 176 | 177 | impl<'a, T> CSliceMut<'a, T> { 178 | /// Get the length of the slice. 179 | pub const fn len(&self) -> usize { 180 | self.len 181 | } 182 | 183 | /// Check if the slice is empty. 184 | pub const fn is_empty(&self) -> bool { 185 | self.len() == 0 186 | } 187 | 188 | /// Get constant pointer to the underlying data. 189 | pub const fn as_ptr(&self) -> *const T { 190 | self.data as *const T 191 | } 192 | 193 | /// Get mutable pointer to the underlying data. 194 | pub const fn as_mut_ptr(&self) -> *mut T { 195 | self.data 196 | } 197 | 198 | /// Reinterpret as a const Rust slice. 199 | pub fn as_slice(&'a self) -> &'a [T] { 200 | unsafe { core::slice::from_raw_parts(self.data, self.len) } 201 | } 202 | 203 | /// Reinterpret as a mutable Rust slice. 204 | pub fn as_slice_mut(&'a mut self) -> &'a mut [T] { 205 | unsafe { core::slice::from_raw_parts_mut(self.data, self.len) } 206 | } 207 | } 208 | 209 | impl<'a> CSliceMut<'a, u8> { 210 | /// Convert into utf-8 string 211 | /// 212 | /// # Safety 213 | /// 214 | /// The underlying slice must be a valid utf-8 string. 215 | pub unsafe fn into_str(self) -> &'a str { 216 | core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.data, self.len)) 217 | } 218 | 219 | /// Convert into mutable utf-8 string 220 | /// 221 | /// # Safety 222 | /// 223 | /// The underlying slice must be a valid utf-8 string. 224 | pub unsafe fn into_mut_str(self) -> &'a mut str { 225 | core::str::from_utf8_unchecked_mut(core::slice::from_raw_parts_mut(self.data, self.len)) 226 | } 227 | } 228 | 229 | impl<'a, T> From<&'a mut [T]> for CSliceMut<'a, T> { 230 | fn from(from: &'a mut [T]) -> Self { 231 | Self { 232 | data: from.as_mut_ptr(), 233 | len: from.len(), 234 | _lifetime: PhantomData::default(), 235 | } 236 | } 237 | } 238 | 239 | impl<'a> From<&'a mut str> for CSliceMut<'a, u8> { 240 | fn from(from: &'a mut str) -> Self { 241 | Self { 242 | data: from.as_mut_ptr(), 243 | len: from.len(), 244 | _lifetime: PhantomData::default(), 245 | } 246 | } 247 | } 248 | 249 | impl<'a, T> From> for &'a [T] { 250 | fn from(from: CSliceMut<'a, T>) -> Self { 251 | unsafe { core::slice::from_raw_parts(from.data, from.len) } 252 | } 253 | } 254 | 255 | impl<'a: 'b, 'b, T> From<&'b CSliceMut<'a, T>> for CSliceRef<'a, T> { 256 | fn from(from: &'b CSliceMut<'a, T>) -> Self { 257 | Self { 258 | data: from.data, 259 | len: from.len, 260 | _lifetime: from._lifetime, 261 | } 262 | } 263 | } 264 | 265 | impl<'a: 'b, 'b, T> From<&'b mut CSliceMut<'a, T>> for CSliceMut<'a, T> { 266 | fn from(from: &'b mut CSliceMut<'a, T>) -> Self { 267 | Self { 268 | data: from.data, 269 | len: from.len, 270 | _lifetime: from._lifetime, 271 | } 272 | } 273 | } 274 | 275 | impl<'a> TryFrom> for &'a str { 276 | type Error = core::str::Utf8Error; 277 | 278 | fn try_from(from: CSliceMut<'a, u8>) -> Result { 279 | core::str::from_utf8(unsafe { core::slice::from_raw_parts(from.data, from.len) }) 280 | } 281 | } 282 | 283 | impl<'a, T> From> for &'a mut [T] { 284 | fn from(from: CSliceMut<'a, T>) -> Self { 285 | unsafe { core::slice::from_raw_parts_mut(from.data, from.len) } 286 | } 287 | } 288 | 289 | impl<'a> TryFrom> for &'a mut str { 290 | type Error = core::str::Utf8Error; 291 | 292 | fn try_from(from: CSliceMut<'a, u8>) -> Result { 293 | core::str::from_utf8_mut(unsafe { core::slice::from_raw_parts_mut(from.data, from.len) }) 294 | } 295 | } 296 | 297 | impl<'a, T> core::ops::Deref for CSliceMut<'a, T> { 298 | type Target = [T]; 299 | 300 | fn deref(&self) -> &Self::Target { 301 | self.as_slice() 302 | } 303 | } 304 | 305 | impl<'a, T> core::ops::DerefMut for CSliceMut<'a, T> { 306 | fn deref_mut(&mut self) -> &mut Self::Target { 307 | unsafe { core::slice::from_raw_parts_mut(self.data, self.len) } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /cglue/src/task/mod.rs: -------------------------------------------------------------------------------- 1 | //! C-compatible task structures. 2 | 3 | use core::task::*; 4 | use tarc::BaseArc; 5 | 6 | #[repr(C)] 7 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 8 | struct OpaqueRawWakerVtbl { 9 | clone: unsafe extern "C" fn(OpaqueRawWaker) -> CRawWaker, 10 | wake: unsafe extern "C" fn(OpaqueRawWaker), 11 | wake_by_ref: unsafe extern "C" fn(OpaqueRawWaker), 12 | drop: unsafe extern "C" fn(OpaqueRawWaker), 13 | } 14 | 15 | impl Default for &'static OpaqueRawWakerVtbl { 16 | fn default() -> Self { 17 | unsafe extern "C" fn clone(w: OpaqueRawWaker) -> CRawWaker { 18 | let waker: RawWaker = core::mem::transmute(w); 19 | waker_clone(&waker as *const _ as *const ()) 20 | } 21 | 22 | unsafe extern "C" fn wake(w: OpaqueRawWaker) { 23 | let waker: Waker = core::mem::transmute(w); 24 | waker.wake() 25 | } 26 | 27 | unsafe extern "C" fn wake_by_ref(w: OpaqueRawWaker) { 28 | let waker: RawWaker = core::mem::transmute(w); 29 | let waker: &Waker = core::mem::transmute(&waker); 30 | waker.wake_by_ref() 31 | } 32 | 33 | unsafe extern "C" fn drop(w: OpaqueRawWaker) { 34 | let _: Waker = core::mem::transmute(w); 35 | } 36 | 37 | &OpaqueRawWakerVtbl { 38 | clone, 39 | wake, 40 | wake_by_ref, 41 | drop, 42 | } 43 | } 44 | } 45 | 46 | #[repr(C)] 47 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 48 | struct CRawWaker { 49 | waker: OpaqueRawWaker, 50 | vtable: &'static OpaqueRawWakerVtbl, 51 | } 52 | 53 | impl CRawWaker { 54 | fn to_raw(this: BaseArc) -> RawWaker { 55 | unsafe fn clone(data: *const ()) -> RawWaker { 56 | let data = data as *const CRawWaker; 57 | BaseArc::increment_strong_count(data); 58 | let waker = BaseArc::from_raw(data); 59 | CRawWaker::to_raw(waker) 60 | } 61 | unsafe fn wake(data: *const ()) { 62 | let this = BaseArc::from_raw(data as *const CRawWaker); 63 | (this.vtable.wake)(this.waker) 64 | } 65 | unsafe fn wake_by_ref(data: *const ()) { 66 | let data = data as *const CRawWaker; 67 | let this = &*data; 68 | (this.vtable.wake_by_ref)(this.waker) 69 | } 70 | unsafe fn drop(data: *const ()) { 71 | let this = BaseArc::from_raw(data as *const CRawWaker); 72 | (this.vtable.drop)(this.waker) 73 | } 74 | 75 | let vtbl = &RawWakerVTable::new(clone, wake, wake_by_ref, drop); 76 | 77 | RawWaker::new(this.into_raw() as *const (), vtbl) 78 | } 79 | } 80 | 81 | unsafe extern "C" fn waker_clone(waker: *const ()) -> CRawWaker { 82 | let waker: &Waker = &*(waker as *const Waker); 83 | let waker = core::mem::transmute(waker.clone()); 84 | 85 | CRawWaker { 86 | waker, 87 | vtable: Default::default(), 88 | } 89 | } 90 | 91 | unsafe extern "C" fn waker_wake_by_ref(waker: *const ()) { 92 | let waker: &Waker = &*(waker as *const Waker); 93 | waker.wake_by_ref() 94 | } 95 | 96 | #[repr(C)] 97 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 98 | pub struct CWaker(CRawWaker); 99 | 100 | impl From for CWaker { 101 | fn from(waker: Waker) -> Self { 102 | let waker: OpaqueRawWaker = unsafe { core::mem::transmute(waker) }; 103 | Self(CRawWaker { 104 | waker, 105 | vtable: Default::default(), 106 | }) 107 | } 108 | } 109 | 110 | impl Clone for CWaker { 111 | fn clone(&self) -> Self { 112 | Self(unsafe { (self.0.vtable.clone)(self.0.waker) }) 113 | } 114 | } 115 | 116 | impl Drop for CWaker { 117 | fn drop(&mut self) { 118 | unsafe { 119 | (self.0.vtable.drop)(self.0.waker); 120 | } 121 | } 122 | } 123 | 124 | impl CWaker { 125 | pub fn wake_by_ref(&self) { 126 | unsafe { (self.0.vtable.wake_by_ref)(self.0.waker) } 127 | } 128 | 129 | pub fn wake(self) { 130 | let vtable = self.0.vtable; 131 | let waker = self.0.waker; 132 | core::mem::forget(self); 133 | unsafe { (vtable.wake)(waker) }; 134 | } 135 | } 136 | 137 | #[repr(transparent)] 138 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 139 | #[derive(Clone, Copy)] 140 | struct OpaqueRawWaker { 141 | waker: [*const (); 2], 142 | } 143 | 144 | #[repr(C)] 145 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 146 | #[derive(Clone, Copy)] 147 | pub struct CRefWaker<'a> { 148 | raw: &'a OpaqueRawWaker, 149 | clone: unsafe extern "C" fn(*const ()) -> CRawWaker, 150 | wake_by_ref: unsafe extern "C" fn(*const ()), 151 | } 152 | 153 | impl<'a> CRefWaker<'a> { 154 | pub unsafe fn from_raw(raw: &'a RawWaker) -> Self { 155 | let raw: &'a OpaqueRawWaker = core::mem::transmute(raw); 156 | 157 | Self { 158 | raw, 159 | clone: waker_clone, 160 | wake_by_ref: waker_wake_by_ref, 161 | } 162 | } 163 | 164 | pub fn with_waker(&self, cb: impl FnOnce(&Waker) -> T) -> T { 165 | unsafe fn unreach(_: *const ()) { 166 | unreachable!() 167 | } 168 | unsafe fn noop(_: *const ()) {} 169 | unsafe fn clone(data: *const ()) -> RawWaker { 170 | let this = &*(data as *const CRefWaker); 171 | let waker = unsafe { (this.clone)(this.raw as *const _ as *const ()) }; 172 | let waker = BaseArc::new(waker); 173 | CRawWaker::to_raw(waker) 174 | } 175 | unsafe fn wake_by_ref(data: *const ()) { 176 | let this = &*(data as *const CRefWaker); 177 | unsafe { (this.wake_by_ref)(this.raw as *const _ as *const ()) }; 178 | } 179 | 180 | let vtbl = &RawWakerVTable::new(clone, unreach, wake_by_ref, noop); 181 | let waker = RawWaker::new(self as *const Self as *const (), vtbl); 182 | let waker = unsafe { Waker::from_raw(waker) }; 183 | 184 | cb(&waker) 185 | } 186 | } 187 | 188 | impl<'a> From<&'a Waker> for CRefWaker<'a> { 189 | fn from(waker: &'a Waker) -> Self { 190 | const _: [(); core::mem::size_of::()] = [(); core::mem::size_of::()]; 191 | unsafe { Self::from_raw(core::mem::transmute(waker)) } 192 | } 193 | } 194 | 195 | #[cfg(test)] 196 | mod tests { 197 | use super::*; 198 | use pollster::block_on; 199 | 200 | // Since unavailable before 1.64 201 | use core::fmt; 202 | use core::future::Future; 203 | use core::pin::*; 204 | 205 | pub fn poll_fn(f: F) -> PollFn 206 | where 207 | F: FnMut(&mut Context<'_>) -> Poll, 208 | { 209 | PollFn { f } 210 | } 211 | 212 | /// A Future that wraps a function returning [`Poll`]. 213 | /// 214 | /// This `struct` is created by [`poll_fn()`]. See its 215 | /// documentation for more. 216 | #[must_use = "futures do nothing unless you `.await` or poll them"] 217 | pub struct PollFn { 218 | f: F, 219 | } 220 | 221 | impl Unpin for PollFn {} 222 | 223 | impl fmt::Debug for PollFn { 224 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 225 | f.debug_struct("PollFn").finish() 226 | } 227 | } 228 | 229 | impl Future for PollFn 230 | where 231 | F: FnMut(&mut Context<'_>) -> Poll, 232 | { 233 | type Output = T; 234 | 235 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 236 | // SAFETY: We are not moving out of the pinned field. 237 | (unsafe { &mut self.get_unchecked_mut().f })(cx) 238 | } 239 | } 240 | 241 | #[test] 242 | fn cwaker_simple() { 243 | let mut polled = false; 244 | let fut = poll_fn(|cx| { 245 | if !polled { 246 | polled = true; 247 | cx.waker().wake_by_ref(); 248 | Poll::Pending 249 | } else { 250 | Poll::Ready(()) 251 | } 252 | }); 253 | let fut = crate::trait_obj!(fut as Future); 254 | block_on(fut) 255 | } 256 | 257 | #[test] 258 | fn cwaker_simple_cloned() { 259 | let mut polled = false; 260 | let fut = poll_fn(|cx| { 261 | if !polled { 262 | polled = true; 263 | cx.waker().clone().wake(); 264 | Poll::Pending 265 | } else { 266 | Poll::Ready(()) 267 | } 268 | }); 269 | let fut = crate::trait_obj!(fut as Future); 270 | block_on(fut) 271 | } 272 | 273 | #[test] 274 | fn cwaker_threaded() { 275 | let (tx, rx) = std::sync::mpsc::channel::(); 276 | 277 | let thread = std::thread::spawn(move || { 278 | for waker in rx.into_iter() { 279 | waker.wake(); 280 | } 281 | }); 282 | 283 | let mut polled = false; 284 | let fut = poll_fn(move |cx| { 285 | if !polled { 286 | polled = true; 287 | tx.send(cx.waker().clone()).unwrap(); 288 | Poll::Pending 289 | } else { 290 | Poll::Ready(()) 291 | } 292 | }); 293 | let fut = crate::trait_obj!(fut as Future); 294 | block_on(fut); 295 | 296 | thread.join().unwrap(); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /cglue/src/tests/arc/mod.rs: -------------------------------------------------------------------------------- 1 | use super::simple::structs::*; 2 | use crate::arc::*; 3 | //use crate::boxed::*; 4 | use crate::*; 5 | use std::sync::Arc; 6 | 7 | #[cglue_trait] 8 | pub trait DoThings { 9 | fn dt_1(&self) -> usize; 10 | } 11 | 12 | impl DoThings for SA { 13 | fn dt_1(&self) -> usize { 14 | 55 15 | } 16 | } 17 | 18 | #[cglue_trait] 19 | pub trait DoThingsSend: Send { 20 | fn dts_1(&self) -> usize; 21 | } 22 | 23 | impl DoThingsSend for SA { 24 | fn dts_1(&self) -> usize { 25 | 55 26 | } 27 | } 28 | 29 | #[cglue_trait] 30 | pub trait DoerGetter { 31 | #[wrap_with_obj(DoThings)] 32 | type ReturnType: DoThings; 33 | 34 | fn dget_1(&self) -> Self::ReturnType; 35 | } 36 | 37 | impl DoerGetter for SA { 38 | type ReturnType = SA; 39 | 40 | fn dget_1(&self) -> Self::ReturnType { 41 | SA {} 42 | } 43 | } 44 | 45 | /*#[test] 46 | fn use_dothings() { 47 | let sa = SA {}; 48 | let wrapped = CBox::from((sa, CArc::from(()).into_opt())); 49 | assert_eq!(wrapped.dt_1(), 55); 50 | }*/ 51 | 52 | #[test] 53 | fn use_getter_obj() { 54 | let sa = SA {}; 55 | 56 | let arc = std::sync::Arc::from(()); 57 | 58 | assert_eq!(Arc::strong_count(&arc), 1); 59 | 60 | let opt_arc = CArc::<()>::from(arc.clone()); 61 | 62 | assert_eq!(Arc::strong_count(&arc), 2); 63 | 64 | let getter = trait_obj!((sa, opt_arc) as DoerGetter); 65 | 66 | assert_eq!(Arc::strong_count(&arc), 2); 67 | 68 | let doer = getter.dget_1(); 69 | 70 | assert_eq!(Arc::strong_count(&arc), 3); 71 | 72 | let _ = getter.dget_1(); 73 | 74 | assert_eq!(Arc::strong_count(&arc), 3); 75 | 76 | std::mem::drop(getter); 77 | 78 | assert_eq!(Arc::strong_count(&arc), 2); 79 | 80 | std::mem::drop(doer); 81 | 82 | assert_eq!(Arc::strong_count(&arc), 1); 83 | } 84 | 85 | #[test] 86 | fn use_clone_obj() { 87 | let sa = SA {}; 88 | 89 | let arc = std::sync::Arc::from(()); 90 | 91 | assert_eq!(Arc::strong_count(&arc), 1); 92 | 93 | let opt_arc = CArc::<()>::from(arc.clone()); 94 | 95 | assert_eq!(Arc::strong_count(&arc), 2); 96 | 97 | let obj: crate::ext::CloneBaseArcBox<_, _> = trait_obj!((sa, opt_arc) as Clone); 98 | 99 | assert_eq!(Arc::strong_count(&arc), 2); 100 | 101 | let cloned = obj.clone(); 102 | 103 | assert_eq!(Arc::strong_count(&arc), 3); 104 | 105 | std::mem::drop(cloned); 106 | 107 | assert_eq!(Arc::strong_count(&arc), 2); 108 | 109 | std::mem::drop(obj); 110 | 111 | assert_eq!(Arc::strong_count(&arc), 1); 112 | } 113 | -------------------------------------------------------------------------------- /cglue/src/tests/ext/as_ref.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use cglue_macro::*; 3 | 4 | cglue_trait_group!(MaybeAsRef, {}, ::ext::core::convert::AsRef, {}, false); 5 | cglue_impl_group!(SA, MaybeAsRef, ::ext::core::convert::AsRef); 6 | 7 | #[test] 8 | fn use_as_ref() { 9 | let sa = SA {}; 10 | let obj = trait_obj!(sa as ::ext::core::convert::AsRef); 11 | impl_as_ref(&obj) 12 | } 13 | 14 | #[test] 15 | fn use_as_ref_group() { 16 | let sa = SA {}; 17 | let obj = group_obj!(sa as MaybeAsRef); 18 | let obj = as_ref!(obj impl AsRef).unwrap(); 19 | impl_as_ref(obj) 20 | } 21 | 22 | #[cfg(test)] 23 | fn impl_as_ref(t: &impl ::core::convert::AsRef) { 24 | let _ = t.as_ref(); 25 | } 26 | -------------------------------------------------------------------------------- /cglue/src/tests/ext/clone.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use super::super::simple::trait_defs::*; 3 | use cglue_macro::*; 4 | 5 | cglue_trait_group!(MaybeClone, { TA }, { Clone }); 6 | cglue_impl_group!(SA, MaybeClone, Clone); 7 | 8 | #[test] 9 | fn use_clone() { 10 | let sa = SA {}; 11 | let obj = trait_obj!(sa as Clone); 12 | impl_clone(&obj) 13 | } 14 | 15 | #[test] 16 | fn use_clone_group() { 17 | let sa = SA {}; 18 | let obj = group_obj!(sa as MaybeClone); 19 | let obj = as_ref!(obj impl Clone).unwrap(); 20 | impl_clone(obj) 21 | } 22 | 23 | #[cfg(test)] 24 | fn impl_clone(t: &impl ::core::clone::Clone) { 25 | let _ = t.clone(); 26 | } 27 | -------------------------------------------------------------------------------- /cglue/src/tests/ext/fmt.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use super::super::simple::trait_defs::*; 3 | use cglue_macro::*; 4 | 5 | cglue_trait_group!(MaybeDebug, { TA }, { ::ext::core::fmt::Debug }); 6 | cglue_impl_group!(SA, MaybeDebug, Debug); 7 | 8 | cglue_trait_group!(NumberFormat, { Debug, Display, 9 | ::ext::core::fmt::Octal, 10 | ::ext::core::fmt::LowerHex, 11 | ::ext::core::fmt::UpperHex, 12 | ::ext::core::fmt::Binary, 13 | }, {}); 14 | cglue_impl_group!(usize, NumberFormat); 15 | 16 | #[test] 17 | fn use_debug() { 18 | let sa = SA {}; 19 | let obj = trait_obj!(sa as Debug); 20 | impl_debug(&obj); 21 | 22 | println!("{:?}", obj); 23 | 24 | assert_eq!("SA", &format!("{:?}", obj)); 25 | } 26 | 27 | #[test] 28 | fn use_debug_group() { 29 | let sa = SA {}; 30 | let obj = group_obj!(sa as MaybeDebug); 31 | let obj = as_ref!(obj impl Debug).unwrap(); 32 | impl_debug(obj) 33 | } 34 | 35 | #[cfg(test)] 36 | fn impl_debug(t: &impl ::core::fmt::Debug) { 37 | let _ = format!("{:?}", t); 38 | } 39 | 40 | #[test] 41 | fn use_display() { 42 | let v = 42; 43 | 44 | let obj = trait_obj!(v as Display); 45 | 46 | assert_eq!("42", &format!("{}", obj)); 47 | } 48 | 49 | #[test] 50 | fn use_num_fmt() { 51 | let v = 42; 52 | 53 | let obj = group_obj!(v as NumberFormat); 54 | 55 | assert_eq!(&format!("{}", v), &format!("{}", obj)); 56 | assert_eq!(&format!("{:?}", v), &format!("{:?}", obj)); 57 | assert_eq!(&format!("{:x}", v), &format!("{:x}", obj)); 58 | assert_eq!(&format!("{:X}", v), &format!("{:X}", obj)); 59 | assert_eq!(&format!("{:b}", v), &format!("{:b}", obj)); 60 | } 61 | -------------------------------------------------------------------------------- /cglue/src/tests/ext/future.rs: -------------------------------------------------------------------------------- 1 | use cglue_macro::*; 2 | 3 | #[test] 4 | fn use_future() { 5 | async fn hii() -> u64 { 6 | 42 7 | } 8 | 9 | let obj = trait_obj!(hii() as Future); 10 | 11 | impl_future(&obj); 12 | 13 | assert_eq!(pollster::block_on(obj), 42); 14 | } 15 | 16 | #[cfg(test)] 17 | fn impl_future(_: &impl ::core::future::Future) {} 18 | -------------------------------------------------------------------------------- /cglue/src/tests/ext/futures.rs: -------------------------------------------------------------------------------- 1 | use cglue_macro::*; 2 | 3 | #[test] 4 | fn use_stream() { 5 | use futures::stream::StreamExt; 6 | 7 | let items = [42, 43, 42]; 8 | 9 | let obj = trait_obj!(futures::stream::iter(items) as Stream); 10 | 11 | impl_stream(&obj); 12 | 13 | assert_eq!(pollster::block_on(obj.collect::>()), items); 14 | } 15 | 16 | #[cfg(test)] 17 | fn impl_stream(_: &impl ::futures::Stream) {} 18 | 19 | #[test] 20 | fn use_sink() { 21 | use futures::sink::SinkExt; 22 | 23 | let items = [42, 43, 42]; 24 | 25 | let sink = futures::sink::unfold(0, |idx, elem: i32| async move { 26 | assert_eq!(elem, items[idx]); 27 | Ok::<_, usize>(idx + 1) 28 | }); 29 | 30 | let mut obj = trait_obj!(sink as Sink); 31 | 32 | impl_sink(&obj); 33 | 34 | pollster::block_on(async { 35 | for i in items { 36 | obj.send(i).await.unwrap(); 37 | } 38 | }); 39 | 40 | // The logic of the sink should force a panic afterwards 41 | // However, we cannot test this post 1.81 without explicitly opting in C-unwind ABI. 42 | #[cfg(any( 43 | feature = "unwind_abi_ext", 44 | feature = "unwind_abi_default", 45 | not(c_unwind_on_stable) 46 | ))] 47 | assert!( 48 | std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || { 49 | pollster::block_on(obj.send(0)) 50 | })) 51 | .is_err() 52 | ); 53 | } 54 | 55 | #[cfg(test)] 56 | fn impl_sink(_: &impl ::futures::Sink) {} 57 | -------------------------------------------------------------------------------- /cglue/src/tests/ext/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod as_ref; 2 | pub mod clone; 3 | pub mod fmt; 4 | #[cfg(feature = "task")] 5 | pub mod future; 6 | #[cfg(feature = "futures")] 7 | pub mod futures; 8 | -------------------------------------------------------------------------------- /cglue/src/tests/extra/custom_impl.rs: -------------------------------------------------------------------------------- 1 | //! These tests are intended to check more complex casting behaviour. 2 | use super::super::simple::structs::*; 3 | use cglue_macro::*; 4 | 5 | #[cglue_trait] 6 | pub trait CustomImpl { 7 | #[custom_impl( 8 | // Types within the C interface other than self and additional wrappers. 9 | { 10 | _npo_opt: Option<&usize>, 11 | }, 12 | // Unwrapped return type 13 | bool, 14 | // Conversion in trait impl to C arguments (signature names are expected). 15 | { 16 | }, 17 | // This is the body of C impl minus the automatic wrapping. 18 | { 19 | true 20 | }, 21 | // This part is processed in the trait impl after the call returns (impl_func_ret, 22 | // nothing extra needs to happen here). 23 | { 24 | }, 25 | )] 26 | fn cimpl_1(&self, _npo_opt: Option<&usize>) -> bool { 27 | false 28 | } 29 | 30 | #[vtbl_only] 31 | #[custom_impl( 32 | // Types within the C interface other than self and additional wrappers. 33 | { 34 | _npo_opt: Option<&usize>, 35 | }, 36 | // Unwrapped return type 37 | bool, 38 | // Conversion in trait impl to C arguments (signature names are expected). 39 | { 40 | This should not even be used in compilation! 41 | }, 42 | // This is the body of C impl minus the automatic wrapping. 43 | { 44 | true 45 | }, 46 | // This part is processed in the trait impl after the call returns (impl_func_ret, 47 | // nothing extra needs to happen here). 48 | { 49 | Neither should this! 50 | }, 51 | )] 52 | fn cimpl_2(&self, _npo_opt: Option<&usize>) -> bool { 53 | false 54 | } 55 | 56 | #[custom_impl( 57 | // Types within the C interface other than self and additional wrappers. 58 | { 59 | _npo_opt: Option<&usize>, 60 | }, 61 | // Unwrapped return type 62 | bool, 63 | // Conversion in trait impl to C arguments (signature names are expected). 64 | { 65 | let _npo_opt: usize = _npo_opt.into(); 66 | let _npo_opt = Some(&_npo_opt); 67 | }, 68 | // This is the body of C impl minus the automatic wrapping. 69 | { 70 | true 71 | }, 72 | // This part is processed in the trait impl after the call returns (impl_func_ret, 73 | // nothing extra needs to happen here). 74 | { 75 | }, 76 | )] 77 | fn cimpl_3>(&self, _npo_opt: T) -> bool { 78 | false 79 | } 80 | } 81 | 82 | impl CustomImpl for SA {} 83 | 84 | #[test] 85 | fn test_custom1() { 86 | let sa = SA {}; 87 | 88 | assert!(!sa.cimpl_1(None)); 89 | 90 | let sa = trait_obj!(sa as CustomImpl); 91 | 92 | assert!(sa.cimpl_1(None)); 93 | } 94 | 95 | #[test] 96 | fn test_custom2() { 97 | let sa = SA {}; 98 | 99 | assert!(!sa.cimpl_2(None)); 100 | 101 | let sa = trait_obj!(sa as CustomImpl); 102 | 103 | assert!(!sa.cimpl_2(None)); 104 | } 105 | 106 | #[test] 107 | fn test_custom3() { 108 | let sa = SA {}; 109 | 110 | assert!(!sa.cimpl_3(2usize)); 111 | 112 | let sa = trait_obj!(sa as CustomImpl); 113 | 114 | assert!(sa.cimpl_3(4usize)); 115 | } 116 | -------------------------------------------------------------------------------- /cglue/src/tests/extra/forward.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | #[allow(dead_code)] 4 | #[cglue_forward] 5 | trait ForwardMe { 6 | #[skip_func] 7 | fn fm_1(&self) -> &Self { 8 | self 9 | } 10 | 11 | #[vtbl_only] 12 | fn fm_2(&self) -> &Self { 13 | self 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /cglue/src/tests/extra/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod custom_impl; 2 | pub mod forward; 3 | pub mod wrap_default; 4 | -------------------------------------------------------------------------------- /cglue/src/tests/extra/wrap_default.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use cglue_macro::*; 3 | 4 | pub struct ExtraFeatureWrap { 5 | _v: T, 6 | } 7 | 8 | #[cglue_trait] 9 | pub trait ExtraFeature { 10 | fn ef_1(&self) -> usize; 11 | } 12 | 13 | #[cglue_trait] 14 | pub trait Basic: Sized + Send + Sync { 15 | #[vtbl_only('_, wrap_with_obj(ExtraFeature))] 16 | fn b_1(&self) -> ExtraFeatureWrap<&Self> { 17 | ExtraFeatureWrap { _v: self } 18 | } 19 | 20 | #[vtbl_only('static, wrap_with_obj(ExtraFeature))] 21 | fn b_2(self) -> ExtraFeatureWrap { 22 | ExtraFeatureWrap { _v: self } 23 | } 24 | 25 | #[vtbl_only('_, wrap_with_obj(ExtraFeature))] 26 | fn b_3(&self, _arg: bool) -> ExtraFeatureWrap<&Self> { 27 | ExtraFeatureWrap { _v: self } 28 | } 29 | 30 | #[vtbl_only('static, wrap_with_obj(ExtraFeature))] 31 | fn b_4(self, _arg: bool, _a2: u8) -> ExtraFeatureWrap { 32 | ExtraFeatureWrap { _v: self } 33 | } 34 | } 35 | 36 | impl ExtraFeature for ExtraFeatureWrap { 37 | fn ef_1(&self) -> usize { 38 | 42 39 | } 40 | } 41 | 42 | impl ExtraFeature for SA { 43 | fn ef_1(&self) -> usize { 44 | 43 45 | } 46 | } 47 | 48 | impl Basic for SA {} 49 | 50 | #[test] 51 | fn test_wrap() { 52 | let basic = trait_obj!(SA {} as Basic); 53 | assert_eq!(basic.b_1().ef_1(), 42); 54 | } 55 | -------------------------------------------------------------------------------- /cglue/src/tests/generics/associated.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use super::super::simple::trait_defs::*; 3 | use super::groups::*; 4 | use super::param::*; 5 | use crate::trait_group::c_void; 6 | use cglue_macro::*; 7 | 8 | #[cglue_trait] 9 | pub trait AssociatedReturn { 10 | #[wrap_with(*const c_void)] 11 | #[return_wrap(|ret| Box::leak(Box::new(ret)) as *mut _ as *const c_void)] 12 | type ReturnType; 13 | 14 | fn ar_1(&self) -> Self::ReturnType; 15 | } 16 | 17 | impl AssociatedReturn for SA { 18 | type ReturnType = usize; 19 | 20 | fn ar_1(&self) -> usize { 21 | 42 22 | } 23 | } 24 | 25 | #[cglue_trait] 26 | pub trait ObjReturn { 27 | #[wrap_with_obj(TA)] 28 | type ReturnType: TA + 'static; 29 | 30 | fn or_1(&self) -> Self::ReturnType; 31 | } 32 | 33 | impl ObjReturn for SA { 34 | type ReturnType = SA; 35 | 36 | fn or_1(&self) -> SA { 37 | SA {} 38 | } 39 | } 40 | 41 | #[cglue_trait] 42 | #[int_result] 43 | pub trait ObjResultReturn { 44 | #[wrap_with_obj(TA)] 45 | type ReturnType: TA + 'static; 46 | 47 | #[allow(clippy::result_unit_err)] 48 | fn orr_1(&self) -> Result; 49 | 50 | #[no_int_result] 51 | #[allow(clippy::result_unit_err)] 52 | fn orr_2(&self) -> Result { 53 | self.orr_1() 54 | } 55 | } 56 | 57 | impl ObjResultReturn for SA { 58 | type ReturnType = SA; 59 | 60 | fn orr_1(&self) -> Result { 61 | Ok(SA {}) 62 | } 63 | } 64 | 65 | #[cglue_trait] 66 | pub trait ObjUnboundedReturn { 67 | #[wrap_with_obj(TA)] 68 | type ReturnType: TA; 69 | 70 | fn our_1(&self) -> Self::ReturnType; 71 | } 72 | 73 | impl ObjUnboundedReturn for SA { 74 | type ReturnType = SB; 75 | 76 | fn our_1(&self) -> SB { 77 | SB {} 78 | } 79 | } 80 | 81 | #[cglue_trait] 82 | pub trait GenericReturn { 83 | #[wrap_with_obj(GenericTrait)] 84 | type ReturnType: GenericTrait; 85 | 86 | fn gr_1(&self) -> Self::ReturnType; 87 | } 88 | 89 | impl GenericReturn for SA { 90 | type ReturnType = SA; 91 | 92 | fn gr_1(&self) -> SA { 93 | SA {} 94 | } 95 | } 96 | 97 | // TODO: generic return where T gets automatically bounded by cglue_trait 98 | 99 | #[cglue_trait] 100 | pub trait GenericGroupReturn { 101 | #[wrap_with_group(GenericGroup)] 102 | type ReturnType: GenericTrait; 103 | 104 | fn ggr_1(&self) -> Self::ReturnType; 105 | } 106 | 107 | impl GenericGroupReturn for SA { 108 | type ReturnType = SA; 109 | 110 | fn ggr_1(&self) -> SA { 111 | SA {} 112 | } 113 | } 114 | 115 | #[cglue_trait] 116 | pub trait GenericConsumedGroupReturn { 117 | #[wrap_with_group(GenericGroup)] 118 | type ReturnType: GenericTrait; 119 | 120 | fn gcgr_1(self) -> Self::ReturnType; 121 | } 122 | 123 | impl GenericConsumedGroupReturn for SA { 124 | type ReturnType = SA; 125 | 126 | fn gcgr_1(self) -> SA { 127 | self 128 | } 129 | } 130 | 131 | #[cglue_trait] 132 | pub trait UnwrappedAssociatedVar { 133 | type AssocVar; 134 | } 135 | 136 | #[cglue_trait] 137 | pub trait UnwrappedAssociatedReturn { 138 | type ReturnType; 139 | 140 | fn uar_1(self) -> Self::ReturnType; 141 | } 142 | 143 | impl UnwrappedAssociatedReturn for SA { 144 | type ReturnType = SA; 145 | 146 | fn uar_1(self) -> SA { 147 | self 148 | } 149 | } 150 | 151 | cglue_trait_group!( 152 | UnwrappedGroup, 153 | UnwrappedAssociatedReturn, 154 | {} 155 | ); 156 | cglue_impl_group!(SA, UnwrappedGroup); 157 | 158 | #[test] 159 | fn use_assoc_return() { 160 | let sa = SA {}; 161 | 162 | let obj = trait_obj!(sa as AssociatedReturn); 163 | 164 | let ret = obj.ar_1(); 165 | 166 | println!("{:?}", ret); 167 | 168 | // SAFETY: the underlying type is a usize box, we are just testing. 169 | let b = unsafe { Box::from_raw(ret as *mut usize) }; 170 | assert_eq!(*b, 42); 171 | } 172 | 173 | #[test] 174 | fn use_obj_return() { 175 | let sa = SA {}; 176 | 177 | let obj = trait_obj!(sa as ObjReturn); 178 | 179 | let ta = obj.or_1(); 180 | 181 | assert_eq!(ta.ta_1(), 5); 182 | } 183 | 184 | #[test] 185 | fn use_gen_return() { 186 | let sa = SA {}; 187 | 188 | let obj = trait_obj!(sa as GenericReturn); 189 | 190 | let ta = obj.gr_1(); 191 | 192 | assert_eq!(ta.gt_1(), 27); 193 | } 194 | 195 | #[test] 196 | fn use_group_return() { 197 | let sa = SA {}; 198 | 199 | let obj = trait_obj!(sa as GenericGroupReturn); 200 | 201 | let group = obj.ggr_1(); 202 | 203 | let cast = cast!(group impl GenWithInlineClause).unwrap(); 204 | 205 | assert!(cast.gwi_1(&cast.gt_1())); 206 | assert!(!cast.gwi_1(&(cast.gt_1() + 1))); 207 | } 208 | 209 | #[test] 210 | fn use_consumed_group_return() { 211 | let sa = SA {}; 212 | 213 | let obj = trait_obj!(sa as GenericConsumedGroupReturn); 214 | 215 | let group = obj.gcgr_1(); 216 | 217 | let cast = cast!(group impl GenWithInlineClause).unwrap(); 218 | 219 | assert!(cast.gwi_1(&cast.gt_1())); 220 | assert!(!cast.gwi_1(&(cast.gt_1() + 1))); 221 | } 222 | 223 | #[test] 224 | fn use_unwrapped_associated_return() { 225 | let sa = SA {}; 226 | 227 | let obj = trait_obj!(sa as UnwrappedAssociatedReturn); 228 | 229 | let _sa: SA = obj.uar_1(); 230 | } 231 | 232 | #[test] 233 | fn use_unwrapped_group() { 234 | let sa = SA {}; 235 | 236 | let obj = group_obj!(sa as UnwrappedGroup); 237 | 238 | let _sa: SA = obj.uar_1(); 239 | } 240 | -------------------------------------------------------------------------------- /cglue/src/tests/generics/associated_ref.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use super::super::simple::trait_defs::*; 3 | use super::super::simple::trait_groups::*; 4 | use cglue_macro::*; 5 | 6 | #[cglue_trait] 7 | pub trait ObjRefReturn { 8 | #[wrap_with_obj_ref(TA)] 9 | type ReturnType: TA + 'static; 10 | 11 | fn orr_1(&self) -> &Self::ReturnType; 12 | } 13 | 14 | impl ObjRefReturn for SA { 15 | type ReturnType = SA; 16 | 17 | fn orr_1(&self) -> &SA { 18 | self 19 | } 20 | } 21 | 22 | #[cglue_trait] 23 | pub trait ObjMutReturn { 24 | #[wrap_with_obj_mut(TA)] 25 | type ReturnType: TA + 'static; 26 | 27 | fn omr_1(&mut self) -> &mut Self::ReturnType; 28 | } 29 | 30 | impl ObjMutReturn for SA { 31 | type ReturnType = SA; 32 | 33 | fn omr_1(&mut self) -> &mut SA { 34 | self 35 | } 36 | } 37 | 38 | #[cglue_trait] 39 | pub trait GroupRefReturn { 40 | #[wrap_with_group_ref(TestGroup)] 41 | type ReturnType: TA + 'static; 42 | 43 | fn grr_1(&self) -> &Self::ReturnType; 44 | } 45 | 46 | impl GroupRefReturn for SA { 47 | type ReturnType = SA; 48 | 49 | fn grr_1(&self) -> &SA { 50 | self 51 | } 52 | } 53 | 54 | impl GroupRefReturn for SB { 55 | type ReturnType = SB; 56 | 57 | fn grr_1(&self) -> &SB { 58 | self 59 | } 60 | } 61 | 62 | #[cglue_trait] 63 | pub trait GroupMutReturn { 64 | #[wrap_with_group_mut(TestGroup)] 65 | type ReturnType: TA + 'static; 66 | 67 | fn gmr_1(&mut self) -> &mut Self::ReturnType; 68 | } 69 | 70 | impl GroupMutReturn for SA { 71 | type ReturnType = SA; 72 | 73 | fn gmr_1(&mut self) -> &mut SA { 74 | self 75 | } 76 | } 77 | 78 | #[cglue_trait] 79 | pub trait GroupMutReturnUnbounded { 80 | #[wrap_with_group_mut(TestGroup)] 81 | type ReturnType: TA; 82 | 83 | fn gmru_1(&mut self) -> &mut Self::ReturnType; 84 | } 85 | 86 | impl GroupMutReturnUnbounded for SA { 87 | type ReturnType = SA; 88 | 89 | fn gmru_1(&mut self) -> &mut SA { 90 | self 91 | } 92 | } 93 | 94 | #[cglue_trait] 95 | pub trait GroupLtMutReturn<'a> { 96 | #[wrap_with_group_mut(TestGroup)] 97 | type ReturnType: TA + 'a; 98 | 99 | fn glmr_1(&'a mut self) -> &'a mut Self::ReturnType; 100 | } 101 | 102 | impl<'a> GroupLtMutReturn<'a> for SA { 103 | type ReturnType = SA; 104 | 105 | fn glmr_1(&mut self) -> &mut SA { 106 | self 107 | } 108 | } 109 | 110 | #[test] 111 | fn use_assoc_ref() { 112 | let sa = SA {}; 113 | 114 | let obj = trait_obj!(sa as ObjRefReturn); 115 | 116 | let obj2 = obj.orr_1(); 117 | 118 | assert_eq!(obj2.ta_1(), 5); 119 | } 120 | 121 | #[test] 122 | fn use_assoc_mut() { 123 | let sa = SA {}; 124 | 125 | let mut obj = trait_obj!(sa as ObjMutReturn); 126 | 127 | let obj2 = obj.omr_1(); 128 | 129 | assert_eq!(obj2.ta_1(), 5); 130 | } 131 | 132 | #[test] 133 | fn use_group_ref() { 134 | let sa = SB {}; 135 | 136 | let obj = trait_obj!(sa as GroupRefReturn); 137 | 138 | let obj2 = obj.grr_1(); 139 | 140 | assert_eq!(obj2.ta_1(), 6); 141 | } 142 | 143 | #[test] 144 | fn use_group_mut() { 145 | let sa = SA {}; 146 | 147 | let mut obj = trait_obj!(sa as GroupMutReturn); 148 | 149 | let obj2 = obj.gmr_1(); 150 | 151 | assert_eq!(obj2.ta_1(), 5); 152 | } 153 | 154 | #[test] 155 | fn use_group_mut_unbounded() { 156 | let mut sa = SA {}; 157 | 158 | let mut obj = trait_obj!(&mut sa as GroupMutReturnUnbounded); 159 | 160 | let obj2 = obj.gmru_1(); 161 | 162 | assert_eq!(obj2.ta_1(), 5); 163 | } 164 | 165 | #[test] 166 | fn use_group_lt_mut() { 167 | let sa = SA {}; 168 | 169 | let mut obj = trait_obj!(sa as GroupLtMutReturn); 170 | 171 | let obj2 = obj.glmr_1(); 172 | 173 | assert_eq!(obj2.ta_1(), 5); 174 | } 175 | -------------------------------------------------------------------------------- /cglue/src/tests/generics/generic_associated.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use super::super::simple::trait_defs::*; 3 | use super::super::simple::trait_groups::*; 4 | use cglue_macro::*; 5 | 6 | #[cglue_trait] 7 | pub trait GroupGatReturn { 8 | #[wrap_with_group(TestGroup)] 9 | type ReturnType<'abc>: TA + 'abc 10 | where 11 | Self: 'abc; 12 | 13 | fn ggr_1<'a>(&'a mut self, val: &'a u32) -> Self::ReturnType<'a>; 14 | } 15 | 16 | impl GroupGatReturn for SA { 17 | type ReturnType<'a> = &'a SA; 18 | 19 | fn ggr_1(&mut self, _val: &u32) -> &SA { 20 | self 21 | } 22 | } 23 | 24 | #[test] 25 | fn use_gat_return() { 26 | use crate::prelude::v1::*; 27 | let sa = SA {}; 28 | let mut obj = trait_obj!(sa as GroupGatReturn); 29 | let ta = obj.ggr_1(&0); 30 | assert_eq!(ta.ta_1(), 5); 31 | } 32 | -------------------------------------------------------------------------------- /cglue/src/tests/generics/generic_structs.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::trait_defs::*; 2 | use crate::*; 3 | 4 | #[derive(Clone, Default)] 5 | pub struct GA { 6 | val: T, 7 | } 8 | 9 | #[cglue_trait] 10 | pub trait Getter { 11 | fn get_val(&self) -> &T; 12 | } 13 | 14 | impl Getter for GA { 15 | fn get_val(&self) -> &T { 16 | &self.val 17 | } 18 | } 19 | 20 | impl TA for GA { 21 | extern "C" fn ta_1(&self) -> usize { 22 | self.val 23 | } 24 | } 25 | 26 | #[derive(Clone)] 27 | pub struct Lifetimed<'a, T> { 28 | val: &'a T, 29 | } 30 | 31 | impl<'a, T> Getter for Lifetimed<'a, T> { 32 | fn get_val(&self) -> &T { 33 | self.val 34 | } 35 | } 36 | 37 | impl<'a> TA for Lifetimed<'a, usize> { 38 | extern "C" fn ta_1(&self) -> usize { 39 | *self.val 40 | } 41 | } 42 | 43 | cglue_trait_group!(GenGroup, Getter, { TA }); 44 | cglue_impl_group!(GA, GenGroup, { TA }); 45 | cglue_impl_group!(GA, GenGroup, {}); 46 | // Internally, the macro prefers to emit 'cglue_a and both of these cases should "just work" 47 | cglue_impl_group!(Lifetimed<'a, T: Eq>, GenGroup, { TA }); 48 | cglue_impl_group!(Lifetimed<'cglue_a, T = u64>, GenGroup, {}); 49 | 50 | #[test] 51 | fn use_getter() { 52 | let ga = GA { val: 50usize }; 53 | 54 | let obj = trait_obj!(ga as Getter); 55 | 56 | assert_eq!(*obj.get_val(), 50); 57 | } 58 | 59 | #[test] 60 | fn gen_clone() { 61 | let ga = GA { val: 50usize }; 62 | 63 | let obj = trait_obj!(ga as Clone); 64 | 65 | let _ = obj.clone(); 66 | } 67 | 68 | #[test] 69 | fn use_ta() { 70 | let ga = GA::default(); 71 | 72 | let obj = trait_obj!(ga as TA); 73 | 74 | assert_eq!(obj.ta_1(), 0); 75 | } 76 | 77 | #[test] 78 | fn use_group() { 79 | let ga = GA::::default(); 80 | let group = group_obj!(ga as GenGroup); 81 | assert!(cast!(group impl TA).is_some()); 82 | 83 | let ga = GA::::default(); 84 | let group = group_obj!(ga as GenGroup); 85 | assert!(cast!(group impl TA).is_none()); 86 | } 87 | -------------------------------------------------------------------------------- /cglue/src/tests/generics/groups.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use super::param::*; 3 | use cglue_macro::*; 4 | 5 | cglue_trait_group!(GenericGroup where T: Eq {}, GenericTrait, { GenWithWhereClause, GenWithInlineClause }); 6 | 7 | cglue_trait_group!(MixedGenericGroup where F: Eq, T: Eq {}, GenericTrait, { GenWithWhereClause, GenWithInlineClause }); 8 | 9 | cglue_impl_group!(SA, GenericGroup where T: Eq {}, { GenWithInlineClause }); 10 | 11 | cglue_impl_group!(SA, MixedGenericGroup where F: Eq, T: Eq {}, { GenWithWhereClause }); 12 | 13 | #[test] 14 | fn use_group_infer() { 15 | let sa = SA {}; 16 | 17 | let obj = group_obj!(sa as GenericGroup); 18 | 19 | println!("Val: {}", obj.gt_1()); 20 | } 21 | 22 | #[test] 23 | fn use_group_explicit() { 24 | let sa = SA {}; 25 | 26 | let obj = group_obj!(sa as GenericGroup); 27 | 28 | println!("Val: {}", obj.gt_1()); 29 | } 30 | 31 | #[test] 32 | fn use_mixed_group_partial_infer() { 33 | let sa = SA {}; 34 | 35 | let obj = group_obj!(sa as MixedGenericGroup); 36 | 37 | println!("Val: {}", obj.gt_1()); 38 | } 39 | 40 | #[test] 41 | fn use_mixed_group_explicit() { 42 | let sa = SA {}; 43 | 44 | let obj = group_obj!(sa as MixedGenericGroup); 45 | 46 | println!("Val: {}", obj.gt_1()); 47 | } 48 | -------------------------------------------------------------------------------- /cglue/src/tests/generics/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod associated; 2 | pub mod associated_ref; 3 | #[cfg(gats_on_stable)] 4 | pub mod generic_associated; 5 | pub mod generic_structs; 6 | pub mod groups; 7 | pub mod param; 8 | -------------------------------------------------------------------------------- /cglue/src/tests/generics/param.rs: -------------------------------------------------------------------------------- 1 | use super::super::simple::structs::*; 2 | use cglue_macro::*; 3 | 4 | #[cglue_trait] 5 | pub trait GenericTrait { 6 | fn gt_1(&self) -> T; 7 | } 8 | 9 | impl GenericTrait for SA { 10 | fn gt_1(&self) -> usize { 11 | 27 12 | } 13 | } 14 | 15 | #[cglue_trait] 16 | pub trait GenWithWhereClause 17 | where 18 | T: Eq, 19 | { 20 | fn gww_1(&self, input: &T) -> bool; 21 | } 22 | 23 | impl GenWithWhereClause for SA { 24 | fn gww_1(&self, input: &usize) -> bool { 25 | self.gt_1().eq(input) 26 | } 27 | } 28 | 29 | #[cglue_trait] 30 | pub trait GenWithInlineClause { 31 | fn gwi_1(&self, input: &T) -> bool; 32 | } 33 | 34 | impl GenWithInlineClause for SA { 35 | fn gwi_1(&self, input: &usize) -> bool { 36 | self.gt_1().eq(input) 37 | } 38 | } 39 | 40 | #[cglue_trait] 41 | pub trait GenWithLifetime<'a, T: Eq + 'a> { 42 | fn gwl_1(&self) -> &T; 43 | fn gwl_2(&self) {} 44 | } 45 | 46 | impl<'a> GenWithLifetime<'a, usize> for SA { 47 | fn gwl_1(&self) -> &usize { 48 | &60 49 | } 50 | } 51 | 52 | #[test] 53 | fn use_gen_infer() { 54 | let sa = SA {}; 55 | 56 | let obj = trait_obj!(sa as GenericTrait); 57 | 58 | println!("{}", obj.gt_1()); 59 | } 60 | 61 | #[test] 62 | fn use_gen_explicit() { 63 | let sa = SA {}; 64 | 65 | let obj = trait_obj!(sa as GenericTrait); 66 | 67 | println!("{}", obj.gt_1()); 68 | } 69 | 70 | #[test] 71 | fn use_lifetime() { 72 | let sa = SA {}; 73 | 74 | let obj = trait_obj!(sa as GenWithLifetime); 75 | 76 | println!("{}", obj.gwl_1()); 77 | } 78 | 79 | #[test] 80 | fn use_lifetime_explicit_t() { 81 | let sa = SA {}; 82 | 83 | let obj = trait_obj!(sa as GenWithLifetime); 84 | 85 | println!("{}", obj.gwl_1()); 86 | } 87 | 88 | #[test] 89 | fn use_lifetime_explicit() { 90 | let sa = SA {}; 91 | 92 | let obj = trait_obj!(sa as GenWithLifetime<'static, usize>); 93 | 94 | println!("{}", obj.gwl_1()); 95 | } 96 | -------------------------------------------------------------------------------- /cglue/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arc; 2 | pub mod ext; 3 | pub mod extra; 4 | pub mod generics; 5 | pub mod simple; 6 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/bounded.rs: -------------------------------------------------------------------------------- 1 | use super::structs::*; 2 | use crate::*; 3 | 4 | #[cglue_trait] 5 | pub trait GenWithSelfConstraint: Send { 6 | fn gwsc_1(&self, input: &usize) -> bool; 7 | } 8 | 9 | impl GenWithSelfConstraint for SA { 10 | fn gwsc_1(&self, input: &usize) -> bool { 11 | *input == 55 12 | } 13 | } 14 | 15 | #[test] 16 | fn use_self_constraint() { 17 | let sa = SA {}; 18 | 19 | let obj = trait_obj!(sa as GenWithSelfConstraint); 20 | 21 | let ret = std::thread::spawn(move || obj.gwsc_1(&55)).join().unwrap(); 22 | 23 | assert!(ret); 24 | } 25 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/consuming.rs: -------------------------------------------------------------------------------- 1 | use super::structs::*; 2 | use cglue_macro::*; 3 | 4 | #[cglue_trait] 5 | pub trait TOnlyConsuming { 6 | fn toc_1(self) -> usize; 7 | } 8 | 9 | impl TOnlyConsuming for SA { 10 | fn toc_1(self) -> usize { 11 | 57 12 | } 13 | } 14 | 15 | #[cglue_trait] 16 | pub trait TMixedConsuming { 17 | fn tmc_1(self) -> usize; 18 | fn tmc_2(&self) -> usize; 19 | } 20 | 21 | impl TMixedConsuming for SA { 22 | fn tmc_1(self) -> usize { 23 | 43 24 | } 25 | 26 | fn tmc_2(&self) -> usize { 27 | 42 28 | } 29 | } 30 | 31 | cglue_trait_group!(ConsumerGroup, TOnlyConsuming, TMixedConsuming); 32 | 33 | cglue_impl_group!(SA, ConsumerGroup, TMixedConsuming); 34 | 35 | #[test] 36 | fn use_consuming() { 37 | let sa = SA {}; 38 | 39 | let obj = trait_obj!(sa as TOnlyConsuming); 40 | 41 | assert_eq!(obj.toc_1(), 57); 42 | } 43 | 44 | #[test] 45 | fn use_mixed_consuming() { 46 | let sa = SA {}; 47 | 48 | let obj = trait_obj!(sa as TMixedConsuming); 49 | 50 | assert_eq!(obj.tmc_2(), 42); 51 | assert_eq!(obj.tmc_1(), 43); 52 | } 53 | 54 | #[test] 55 | fn use_group_consuming() { 56 | let sa = SA {}; 57 | 58 | let obj = group_obj!(sa as ConsumerGroup); 59 | 60 | let obj = cast!(obj impl TMixedConsuming).unwrap(); 61 | 62 | assert_eq!(obj.tmc_2(), 42); 63 | assert_eq!(obj.tmc_1(), 43); 64 | } 65 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/hrtb.rs: -------------------------------------------------------------------------------- 1 | use super::structs::*; 2 | use super::trait_defs::*; 3 | use crate::*; 4 | 5 | pub trait Plugin: for<'a> PluginInner<'a> {} 6 | 7 | #[cglue_trait] 8 | pub trait PluginInner<'a> { 9 | #[wrap_with_obj(SubPlugin)] 10 | type Ret: SubPlugin<'a> + 'a; 11 | 12 | fn get_plug(&'a mut self) -> Self::Ret; 13 | } 14 | 15 | #[cglue_trait] 16 | pub trait SubPlugin<'a> { 17 | #[wrap_with_obj_mut(TA)] 18 | type BorrowedTA: TA + 'a; 19 | 20 | fn do_thing(&self); 21 | fn get_ta(&'a mut self) -> &'a mut Self::BorrowedTA; 22 | } 23 | 24 | impl<'a> PluginInner<'a> for SA { 25 | type Ret = Printer<'a>; 26 | 27 | fn get_plug(&'a mut self) -> Self::Ret { 28 | Printer { sa: self } 29 | } 30 | } 31 | 32 | pub struct Printer<'a> { 33 | sa: &'a mut SA, 34 | } 35 | 36 | impl<'a, 'b> SubPlugin<'a> for Printer<'b> { 37 | type BorrowedTA = SA; 38 | 39 | fn do_thing(&self) { 40 | println!("{}", self.sa.ta_1()); 41 | } 42 | 43 | fn get_ta(&mut self) -> &mut Self::BorrowedTA { 44 | self.sa 45 | } 46 | } 47 | 48 | #[cglue_trait] 49 | pub trait AsSubThing { 50 | #[wrap_with_obj_mut(TA)] 51 | type SubTarget: TA; 52 | 53 | fn get_ta(&mut self) -> &mut Self::SubTarget; 54 | } 55 | 56 | #[repr(transparent)] 57 | pub struct FwdMut<'a, T>(&'a mut T); 58 | 59 | impl<'a, T: TA> TA for FwdMut<'a, T> { 60 | extern "C" fn ta_1(&self) -> usize { 61 | self.0.ta_1() 62 | } 63 | } 64 | 65 | pub struct Plug { 66 | val: T, 67 | } 68 | 69 | impl AsSubThing for Plug { 70 | type SubTarget = T; 71 | 72 | fn get_ta(&mut self) -> &mut Self::SubTarget { 73 | &mut self.val 74 | } 75 | } 76 | 77 | cglue_trait_group!(PluginInstance<'a>, { PluginInner<'a> }, { Clone }); 78 | 79 | cglue_impl_group!(SA, PluginInstance<'a>, {}); 80 | 81 | #[test] 82 | fn use_subthing() { 83 | let mut sa = SA {}; 84 | let val = FwdMut(&mut sa); 85 | 86 | let mut plug = Plug { val }; 87 | 88 | let mut obj = trait_obj!(&mut plug as AsSubThing); 89 | obj.get_ta(); 90 | } 91 | 92 | #[test] 93 | fn build_subplugin() { 94 | let mut sa = SA {}; 95 | let mut subplug = Printer { sa: &mut sa }; 96 | 97 | let mut obj = trait_obj!(&mut subplug as SubPlugin); 98 | 99 | let ta = obj.get_ta(); 100 | 101 | assert_eq!(ta.ta_1(), 5); 102 | } 103 | 104 | #[test] 105 | fn use_plugin() { 106 | let sa = SA {}; 107 | 108 | let mut obj = trait_obj!(sa as PluginInner); 109 | 110 | let printer = obj.get_plug(); 111 | 112 | printer.do_thing(); 113 | } 114 | 115 | #[test] 116 | fn use_plugin_mut() { 117 | let mut sa = SA {}; 118 | 119 | let mut obj = trait_obj!(&mut sa as PluginInner); 120 | 121 | let printer = obj.get_plug(); 122 | 123 | printer.do_thing(); 124 | } 125 | 126 | #[test] 127 | fn use_plugin_group() { 128 | let sa = SA {}; 129 | 130 | let mut obj = group_obj!(sa as PluginInstance); 131 | 132 | let printer = obj.get_plug(); 133 | 134 | printer.do_thing(); 135 | } 136 | 137 | #[test] 138 | fn use_plugin_group_mut() { 139 | let mut sa = SA {}; 140 | 141 | let base = PluginInstance::<_, _>::from(&mut sa); 142 | 143 | let mut obj = crate::trait_group::Opaquable::into_opaque(base); 144 | 145 | let printer = obj.get_plug(); 146 | 147 | printer.do_thing(); 148 | } 149 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bounded; 2 | pub mod consuming; 3 | pub mod hrtb; 4 | pub mod structs; 5 | pub mod trait_defs; 6 | pub mod trait_groups; 7 | pub mod traits; 8 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/structs.rs: -------------------------------------------------------------------------------- 1 | use super::trait_defs::*; 2 | #[cfg(test)] 3 | use cglue_macro::*; 4 | 5 | #[derive(Clone, Copy, Debug)] 6 | #[repr(C)] 7 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 8 | pub struct SA {} 9 | #[derive(Clone, Copy, Debug)] 10 | #[repr(C)] 11 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 12 | pub struct SB {} 13 | 14 | impl TA for SA { 15 | extern "C" fn ta_1(&self) -> usize { 16 | 5 17 | } 18 | } 19 | 20 | impl<'a> TA for &'a SA { 21 | extern "C" fn ta_1(&self) -> usize { 22 | (**self).ta_1() 23 | } 24 | } 25 | 26 | impl AsRef for SA { 27 | fn as_ref(&self) -> &SA { 28 | self 29 | } 30 | } 31 | 32 | impl TA for SB { 33 | extern "C" fn ta_1(&self) -> usize { 34 | 6 35 | } 36 | } 37 | 38 | impl TB for SB { 39 | extern "C" fn tb_1(&self, val: usize) -> usize { 40 | val * 2 41 | } 42 | 43 | fn tb_2(&self, val: usize) -> usize { 44 | val * val 45 | } 46 | } 47 | 48 | impl TC for SA { 49 | fn tc_1(&self) {} 50 | extern "C" fn tc_2(&mut self) {} 51 | } 52 | 53 | impl TT for SA { 54 | fn tt_1(&self, v: T) -> T { 55 | v 56 | } 57 | } 58 | 59 | #[test] 60 | fn call_a() { 61 | let mut a = SA {}; 62 | let b = SB {}; 63 | let c = SB {}; 64 | 65 | let obja = trait_obj!(&mut a as TA); 66 | let objb = trait_obj!(&b as TA); 67 | let objc = trait_obj!(c as TA); 68 | 69 | assert_eq!(obja.ta_1() + objb.ta_1() + objc.ta_1(), 17); 70 | } 71 | 72 | #[test] 73 | fn get_b() { 74 | let b = SB {}; 75 | 76 | let objb = trait_obj!(crate::boxed::CBox::from(b) as TB); 77 | 78 | assert_eq!(objb.tb_2(objb.tb_1(10)), 400); 79 | } 80 | 81 | #[test] 82 | fn get_b_arc() { 83 | let b = SB {}; 84 | 85 | let objb = trait_obj!(crate::arc::CArcSome::from(b) as TB); 86 | 87 | assert_eq!(objb.tb_2(objb.tb_1(10)), 400); 88 | } 89 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/trait_defs.rs: -------------------------------------------------------------------------------- 1 | use cglue_macro::*; 2 | 3 | #[cglue_trait] 4 | pub trait TA { 5 | extern "C" fn ta_1(&self) -> usize; 6 | } 7 | 8 | #[cglue_trait] 9 | pub trait TB { 10 | extern "C" fn tb_1(&self, val: usize) -> usize; 11 | fn tb_2(&self, val: usize) -> usize; 12 | } 13 | 14 | #[cglue_trait] 15 | pub trait TC { 16 | fn tc_1(&self); 17 | extern "C" fn tc_2(&mut self); 18 | fn tc_3(&mut self, mut _ignored: usize) { 19 | self.tc_2() 20 | } 21 | } 22 | 23 | #[cglue_trait] 24 | pub trait TE {} 25 | 26 | #[cglue_trait] 27 | pub trait TT { 28 | fn tt_1(&self, v: T) -> T; 29 | } 30 | 31 | #[cglue_trait] 32 | pub trait TF { 33 | unsafe fn tf_1(&self); 34 | fn tf_2(self: core::pin::Pin<&Self>); 35 | } 36 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/trait_groups.rs: -------------------------------------------------------------------------------- 1 | //! These tests check definition and usage of different trait groups 2 | use super::structs::*; 3 | use super::trait_defs::*; 4 | use cglue_macro::*; 5 | 6 | cglue_trait_group!(TestGroup, TA, { TB, TC }); 7 | cglue_impl_group!(SA, TestGroup, { TC }); 8 | 9 | cglue_impl_group!(&'a SA, TestGroup, {}); 10 | cglue_impl_group!(SB, super::trait_groups::TestGroup, { TB }); 11 | 12 | cglue_trait_group!(TestGroupGen, TT, { TT = TTUsize, TT = TTUSixtyFour }); 13 | cglue_impl_group!(SA, TestGroupGen, { TT = TTUsize }); 14 | 15 | #[test] 16 | fn test_group() { 17 | let mut a = SA {}; 18 | 19 | // Slight regression in 0.2, can not use TestGroupRef, because 20 | // (optional) TC requires mutable refs for the impl to work. 21 | // Can be fixed through unstable features or forcing boxing 22 | // and a ref group impl. 23 | // 24 | // Having both requres explicitly specifying inner type like done here. 25 | fn into_test<'a, T: Into> + Send + 'a>(t: T) -> TestGroupBox<'a> { 26 | group_obj!(t as TestGroup) 27 | } 28 | let _ = into_test(&a); 29 | 30 | let _ = group_obj!(&mut a as TestGroup); 31 | 32 | let group = group_obj!(a as TestGroup); 33 | 34 | { 35 | let group = as_ref!(group impl TC).unwrap(); 36 | group.tc_1(); 37 | } 38 | 39 | assert!(!check!(group impl TB)); 40 | 41 | let cast = cast!(group impl TC).unwrap(); 42 | 43 | let mut group = cast.upcast(); 44 | 45 | assert!(as_mut!(group impl TB).is_none()); 46 | } 47 | 48 | #[test] 49 | fn test_group_2() { 50 | let mut b = SB {}; 51 | 52 | let group = group_obj!(&mut b as TestGroup); 53 | assert!(check!(group impl TB)); 54 | 55 | let group = group_obj!(&b as TestGroup); 56 | assert!(check!(group impl TB)); 57 | 58 | let group = group_obj!(b as TestGroup); 59 | assert!(check!(group impl TB)); 60 | 61 | let tup = (&group, ()); 62 | assert!(check!(tup.0 impl TB)); 63 | 64 | let tb = as_ref!(group impl TB).unwrap(); 65 | 66 | tb.tb_1(1); 67 | } 68 | 69 | #[test] 70 | fn test_group_3() { 71 | let mut a = SA {}; 72 | 73 | let group = group_obj!(&mut a as TestGroupGen); 74 | assert!(check!(group impl TTUsize)); 75 | #[cfg(not(feature = "unstable"))] 76 | assert!(!check!(group impl TTUSixtyFour)); 77 | #[cfg(feature = "unstable")] 78 | assert!(check!(group impl TTUSixtyFour)); 79 | 80 | let tusize = as_ref!(group impl TTUsize).unwrap(); 81 | 82 | tusize.tt_1(1usize); 83 | } 84 | -------------------------------------------------------------------------------- /cglue/src/tests/simple/traits.rs: -------------------------------------------------------------------------------- 1 | //! These tests are intended to check more complex casting behaviour. 2 | use crate::slice::*; 3 | use cglue_macro::*; 4 | 5 | #[cglue_trait] 6 | pub trait WithSlice { 7 | fn wslice_1(&mut self, _slc: &[usize]) {} 8 | fn wslice_2(&mut self, _str: &str) {} 9 | fn wslice_3(&mut self) -> &str { 10 | "slice" 11 | } 12 | fn wslice_4<'a>(&'a mut self, _val: &str) -> &'a str { 13 | "slice" 14 | } 15 | } 16 | 17 | #[cglue_trait] 18 | pub trait WithOptions { 19 | fn wopt_1(&self, _npo_opt: Option<&usize>) {} 20 | fn wopt_2(&self, _opt: Option) {} 21 | fn wopt_3(&mut self, _npo_option: Option<&u64>, _wrap_option: Option) {} 22 | } 23 | 24 | #[cglue_trait] 25 | #[int_result] 26 | pub trait WithIntResult { 27 | fn wint_1(&self, val: usize) -> Result { 28 | Ok(val) 29 | } 30 | #[no_int_result] 31 | fn wint_2(&self, val: usize) -> Result { 32 | Ok(val) 33 | } 34 | } 35 | 36 | type AliasResult = Result; 37 | 38 | #[cglue_trait] 39 | #[int_result(AliasResult)] 40 | pub trait WithAliasIntResult { 41 | #[no_unwind_abi] 42 | fn waint_1(&self, val: usize) -> AliasResult { 43 | Ok(val) 44 | } 45 | #[no_int_result] 46 | fn waint_2(&self, val: usize) -> AliasResult { 47 | Ok(val) 48 | } 49 | } 50 | 51 | #[cglue_trait] 52 | pub trait WithInto { 53 | fn winto_1(&self, _into: impl Into) {} 54 | } 55 | 56 | struct Implementor {} 57 | 58 | impl WithSlice for Implementor {} 59 | impl WithOptions for Implementor {} 60 | impl WithIntResult for Implementor {} 61 | impl WithAliasIntResult for Implementor {} 62 | impl WithInto for Implementor {} 63 | 64 | type ICont = crate::trait_group::CGlueObjContainer; 65 | type IRefCont = ICont<&'static Implementor, C>; 66 | type IMutCont = ICont<&'static mut Implementor, C>; 67 | 68 | type WSCont = IMutCont>; 69 | type WOCont = IMutCont>; 70 | type WIRCont = IRefCont>; 71 | type WAIRCont = IRefCont>; 72 | type WINTOCont = IRefCont>; 73 | 74 | #[cfg(all(feature = "unwind_abi_default", not(__cglue_force_no_unwind_abi)))] 75 | macro_rules! extern_fn { 76 | ($(for<$lt:lifetime>)? ($($tt:ty),*) $(-> $ty:ty)?) => { $(for<$lt>)? unsafe extern "C-unwind" fn($($tt),*) $(-> $ty)? } 77 | } 78 | 79 | #[cfg(any(not(feature = "unwind_abi_default"), __cglue_force_no_unwind_abi))] 80 | macro_rules! extern_fn { 81 | ($(for<$lt:lifetime>)? ($($tt:ty),*) $(-> $ty:ty)?) => { $(for<$lt>)? unsafe extern "C" fn($($tt),*) $(-> $ty)? } 82 | } 83 | 84 | #[test] 85 | fn slices_wrapped() { 86 | let vtbl = <&WithSliceVtbl>::default(); 87 | let _: extern_fn!((&mut WSCont, CSliceRef)) = vtbl.wslice_1(); 88 | let _: extern_fn!((&mut WSCont, CSliceRef)) = vtbl.wslice_2(); 89 | let _: extern_fn!((&mut WSCont) -> CSliceRef) = vtbl.wslice_3(); 90 | let _: extern_fn!(for<'a> (&'a mut WSCont, CSliceRef) -> CSliceRef<'a, u8>) = 91 | vtbl.wslice_4(); 92 | } 93 | 94 | #[test] 95 | fn npo_option_forwarded() { 96 | let vtbl = <&WithOptionsVtbl>::default(); 97 | let _: extern_fn!((&WOCont, Option<&usize>)) = vtbl.wopt_1(); 98 | } 99 | 100 | #[test] 101 | fn non_npo_option_wrapped() { 102 | let vtbl = <&WithOptionsVtbl>::default(); 103 | let _: extern_fn!((&WOCont, crate::option::COption)) = vtbl.wopt_2(); 104 | } 105 | 106 | #[test] 107 | fn mixed_options() { 108 | let vtbl = <&WithOptionsVtbl>::default(); 109 | let _: extern_fn!((&mut WOCont, Option<&u64>, crate::option::COption)) = vtbl.wopt_3(); 110 | } 111 | 112 | #[test] 113 | fn int_result() { 114 | let vtbl = <&WithIntResultVtbl>::default(); 115 | let _: extern_fn!((&WIRCont, usize, &mut core::mem::MaybeUninit) -> i32) = vtbl.wint_1(); 116 | } 117 | 118 | #[test] 119 | fn no_int_result() { 120 | let vtbl = <&WithIntResultVtbl>::default(); 121 | let _: extern_fn!((&WIRCont, usize) -> crate::result::CResult) = vtbl.wint_2(); 122 | } 123 | 124 | #[test] 125 | fn alias_int_result() { 126 | let vtbl = <&WithAliasIntResultVtbl>::default(); 127 | let _: unsafe extern "C" fn(&WAIRCont, usize, &mut core::mem::MaybeUninit) -> i32 = 128 | vtbl.waint_1(); 129 | } 130 | 131 | #[test] 132 | fn alias_no_int_result() { 133 | let vtbl = <&WithAliasIntResultVtbl>::default(); 134 | let _: extern_fn!((&WAIRCont, usize) -> crate::result::CResult) = vtbl.waint_2(); 135 | } 136 | 137 | #[test] 138 | fn into_t_wrapped() { 139 | let vtbl = <&WithIntoVtbl>::default(); 140 | let _: extern_fn!((&WINTOCont, usize)) = vtbl.winto_1(); 141 | } 142 | -------------------------------------------------------------------------------- /cglue/src/trait_group/specify.rs: -------------------------------------------------------------------------------- 1 | /// Allows specifying type tuple type parameters. 2 | /// 3 | /// Only allows specifying tuple elements that have void at the given position. 4 | /// 5 | /// This trait is used in trait groups in order to aid specification of associated types. 6 | /// 7 | /// # Example 8 | /// 9 | /// ``` 10 | /// use cglue::trait_group::specify::*; 11 | /// 12 | /// // Create a mixed type 13 | /// type Mixed = ((), u8, String, (), &'static str); 14 | /// type Specified = <>::Type as Specify>::Type; 15 | /// 16 | /// fn take_specified(v: Option<(u64, u8, String, f32, &'static str)>) {} 17 | /// 18 | /// let v: Option = None; 19 | /// take_specified(v); 20 | /// ``` 21 | pub trait Specify { 22 | type Type; 23 | } 24 | 25 | /// Actually implements `Specify` trait. 26 | macro_rules! impl_specify0 { 27 | ($(($pre:ident, $pre_num:ident),)* | ($cur:ident, $cur_num:ident) | $(($next0:ident, $next_num0:ident),)* | ) => { 28 | impl<$($pre,)* $cur, $($next0),*> Specify<$cur_num, $cur> for ($($pre,)* (), $($next0),*) { 29 | type Type = ($($pre,)* $cur, $($next0),*); 30 | } 31 | }; 32 | ($(($pre:ident, $pre_num:ident),)* | ($cur:ident, $cur_num:ident) | $(($next0:ident, $next_num0:ident),)* | ($next1:ident, $next_num1:ident), $(($next2:ident, $next_num2:ident),)*) => { 33 | impl<$($pre,)* $cur, $($next0),*> Specify<$cur_num, $cur> for ($($pre,)* (), $($next0),*) { 34 | type Type = ($($pre,)* $cur, $($next0),*); 35 | } 36 | 37 | impl_specify0!($(($pre, $pre_num),)* | ($cur, $cur_num) | $(($next0, $next_num0),)* ($next1, $next_num1), | $(($next2, $next_num2),)*); 38 | } 39 | } 40 | 41 | /// Dispatches `Specify` trait implementation. 42 | macro_rules! impl_specify { 43 | ($(($pre:ident, $pre_num:ident),)* | ($cur:ident, $cur_num:ident) | $(($next:ident, $next_num:ident),)*) => { 44 | pub struct $cur_num(()); 45 | 46 | impl_specify0!($(($pre, $pre_num),)* | ($cur, $cur_num) | | $(($next, $next_num),)*); 47 | impl_specify!($(($pre, $pre_num),)* ($cur, $cur_num), | | $(($next, $next_num),)*); 48 | }; 49 | ($(($pre:ident, $pre_num:ident),)* | | ) => { }; 50 | ($(($pre:ident, $pre_num:ident),)* | | ($cur:ident, $cur_num:ident), $(($next:ident, $next_num:ident),)*) => { 51 | impl_specify!($(($pre, $pre_num),)* | ($cur, $cur_num) | $(($next, $next_num),)*); 52 | }; 53 | } 54 | 55 | impl_specify! { 56 | | | 57 | (T0, U0), 58 | (T1, U1), 59 | (T2, U2), 60 | (T3, U3), 61 | (T4, U4), 62 | (T5, U5), 63 | (T6, U6), 64 | (T7, U7), 65 | (T8, U8), 66 | (T9, U9), 67 | (T10, U10), 68 | (T11, U11), 69 | (T12, U12), 70 | (T13, U13), 71 | (T14, U14), 72 | (T15, U15), 73 | (T16, U16), 74 | } 75 | -------------------------------------------------------------------------------- /cglue/src/tuple.rs: -------------------------------------------------------------------------------- 1 | //! `Repr(C)` tuples 2 | 3 | macro_rules! make_tuple { 4 | ( 5 | attrs[$(#[$attr: meta])*] 6 | $name: ident[$( $param: ident ),* $(,)? ] 7 | ) => ( 8 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] 9 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 10 | #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] 11 | #[repr(C)] 12 | $(#[$attr])* 13 | pub struct $name< $($param,)* > ( 14 | $(pub $param,)* 15 | ); 16 | 17 | impl<$($param,)*> From<($($param,)*)> for $name<$($param,)*> { 18 | #[allow(non_snake_case)] 19 | fn from(($($param,)*): ($($param,)*)) -> Self { 20 | Self($($param,)*) 21 | } 22 | } 23 | 24 | impl<$($param,)*> From<$name<$($param,)*>> for ($($param,)*) { 25 | #[allow(non_snake_case)] 26 | fn from($name($($param,)*): $name<$($param,)*>) -> Self { 27 | ($($param,)*) 28 | } 29 | } 30 | 31 | impl< $($param,)* > $name<$($param,)*>{ 32 | /// Convert this C tuple into Rust tuple. 33 | #[inline] 34 | pub fn into_tuple(self)-> ($($param,)*) { 35 | self.into() 36 | } 37 | } 38 | )} 39 | 40 | make_tuple!( 41 | attrs[ 42 | /// FFI-safe 1 element tuple. 43 | ] 44 | CTup1[A] 45 | ); 46 | 47 | make_tuple!( 48 | attrs[ 49 | /// FFI-safe 2 element tuple. 50 | ] 51 | CTup2[A, B] 52 | ); 53 | 54 | make_tuple!( 55 | attrs[ 56 | /// FFI-safe 3 element tuple. 57 | ] 58 | CTup3[A, B, C] 59 | ); 60 | 61 | make_tuple!( 62 | attrs[ 63 | /// FFI-safe 4 element tuple. 64 | ] 65 | CTup4[A, B, C, D] 66 | ); 67 | -------------------------------------------------------------------------------- /cglue/src/vec.rs: -------------------------------------------------------------------------------- 1 | use core::mem::ManuallyDrop; 2 | use std::prelude::v1::*; 3 | 4 | #[repr(C)] 5 | #[cfg_attr(feature = "abi_stable", derive(::abi_stable::StableAbi))] 6 | pub struct CVec { 7 | data: *mut T, 8 | len: usize, 9 | capacity: usize, 10 | drop_fn: Option, 11 | reserve_fn: extern "C" fn(&mut CVec, size: usize) -> usize, 12 | } 13 | 14 | unsafe impl Send for CVec {} 15 | unsafe impl Sync for CVec {} 16 | 17 | impl From> for CVec { 18 | fn from(mut vec: Vec) -> Self { 19 | let data = vec.as_mut_ptr(); 20 | let len = vec.len(); 21 | let capacity = vec.capacity(); 22 | core::mem::forget(vec); 23 | Self { 24 | data, 25 | len, 26 | capacity, 27 | drop_fn: Some(cglue_drop_vec::), 28 | reserve_fn: cglue_reserve_vec::, 29 | } 30 | } 31 | } 32 | 33 | impl Clone for CVec { 34 | fn clone(&self) -> Self { 35 | Self::from(Vec::from(&**self)) 36 | } 37 | } 38 | 39 | impl Default for CVec { 40 | fn default() -> Self { 41 | Self::from(Vec::new()) 42 | } 43 | } 44 | 45 | impl Drop for CVec { 46 | fn drop(&mut self) { 47 | if let Some(drop_fn) = self.drop_fn { 48 | unsafe { drop_fn(self.data, self.len, self.capacity) } 49 | } 50 | } 51 | } 52 | 53 | impl core::fmt::Debug for CVec { 54 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 55 | core::fmt::Debug::fmt(&**self, f) 56 | } 57 | } 58 | 59 | impl core::ops::Deref for CVec { 60 | type Target = [T]; 61 | 62 | fn deref(&self) -> &Self::Target { 63 | unsafe { core::slice::from_raw_parts(self.data, self.len) } 64 | } 65 | } 66 | 67 | impl core::ops::DerefMut for CVec { 68 | fn deref_mut(&mut self) -> &mut Self::Target { 69 | unsafe { core::slice::from_raw_parts_mut(self.data, self.len) } 70 | } 71 | } 72 | 73 | impl CVec { 74 | pub fn len(&self) -> usize { 75 | self.len 76 | } 77 | 78 | pub fn is_empty(&self) -> bool { 79 | self.len == 0 80 | } 81 | 82 | pub fn capacity(&self) -> usize { 83 | self.capacity 84 | } 85 | 86 | pub fn as_ptr(&self) -> *const T { 87 | self.data 88 | } 89 | 90 | pub fn as_mut_ptr(&mut self) -> *mut T { 91 | self.data 92 | } 93 | 94 | pub fn push(&mut self, value: T) { 95 | self.reserve(1); 96 | unsafe { core::ptr::write(self.data.add(self.len), value) }; 97 | self.len += 1; 98 | } 99 | 100 | /// Insert into the vector 101 | /// 102 | /// # Examples 103 | /// 104 | /// ``` 105 | /// use cglue::vec::CVec; 106 | /// 107 | /// let a: Vec = vec![1, 2, 3]; 108 | /// let mut cvec = CVec::from(a); 109 | /// cvec.insert(1, 2); 110 | /// 111 | /// assert_eq!(&cvec[..], &[1, 2, 2, 3]); 112 | /// ``` 113 | pub fn insert(&mut self, index: usize, element: T) { 114 | assert!(index <= self.len); 115 | 116 | self.reserve(1); 117 | unsafe { 118 | let p = self.data.add(index); 119 | core::ptr::copy(p, p.offset(1), self.len - index); 120 | core::ptr::write(p, element); 121 | } 122 | self.len += 1; 123 | } 124 | 125 | pub fn reserve(&mut self, additional: usize) { 126 | if self.capacity - self.len < additional { 127 | (self.reserve_fn)(self, additional); 128 | } 129 | } 130 | 131 | pub fn pop(&mut self) -> Option { 132 | if self.len == 0 { 133 | None 134 | } else { 135 | self.len -= 1; 136 | unsafe { Some(core::ptr::read(self.data.add(self.len))) } 137 | } 138 | } 139 | 140 | /// Insert into the vector 141 | /// 142 | /// # Examples 143 | /// 144 | /// ``` 145 | /// use cglue::vec::CVec; 146 | /// 147 | /// let a: Vec = vec![1, 2, 3]; 148 | /// let mut cvec = CVec::from(a); 149 | /// cvec.remove(1); 150 | /// 151 | /// assert_eq!(&cvec[..], &[1, 3]); 152 | /// ``` 153 | pub fn remove(&mut self, index: usize) -> T { 154 | assert!(index < self.len); 155 | 156 | unsafe { 157 | let ptr = self.data.add(index); 158 | let ret = core::ptr::read(ptr); 159 | core::ptr::copy(ptr.offset(1), ptr, self.len - index - 1); 160 | self.len -= 1; 161 | ret 162 | } 163 | } 164 | } 165 | 166 | struct TempVec<'a, T>(ManuallyDrop>, &'a mut CVec); 167 | 168 | impl<'a, T> From<&'a mut CVec> for TempVec<'a, T> { 169 | fn from(vec: &'a mut CVec) -> Self { 170 | Self( 171 | ManuallyDrop::new(unsafe { Vec::from_raw_parts(vec.data, vec.len, vec.capacity) }), 172 | vec, 173 | ) 174 | } 175 | } 176 | 177 | impl<'a, T> core::ops::Deref for TempVec<'a, T> { 178 | type Target = Vec; 179 | 180 | fn deref(&self) -> &Self::Target { 181 | &self.0 182 | } 183 | } 184 | 185 | impl<'a, T> core::ops::DerefMut for TempVec<'a, T> { 186 | fn deref_mut(&mut self) -> &mut Self::Target { 187 | &mut self.0 188 | } 189 | } 190 | 191 | impl<'a, T> Drop for TempVec<'a, T> { 192 | fn drop(&mut self) { 193 | self.1.data = self.0.as_mut_ptr(); 194 | self.1.len = self.0.len(); 195 | self.1.capacity = self.0.capacity(); 196 | } 197 | } 198 | 199 | #[cfg(feature = "serde")] 200 | impl serde::Serialize for CVec 201 | where 202 | T: serde::Serialize, 203 | { 204 | #[inline] 205 | fn serialize(&self, serializer: S) -> Result 206 | where 207 | S: serde::ser::Serializer, 208 | { 209 | serializer.collect_seq(&**self) 210 | } 211 | } 212 | 213 | #[cfg(feature = "serde")] 214 | impl<'de, T> serde::Deserialize<'de> for CVec 215 | where 216 | T: serde::Deserialize<'de>, 217 | { 218 | fn deserialize(deserializer: D) -> Result 219 | where 220 | D: serde::Deserializer<'de>, 221 | { 222 | Vec::deserialize(deserializer).map(<_>::into) 223 | } 224 | } 225 | 226 | unsafe extern "C" fn cglue_drop_vec(data: *mut T, len: usize, capacity: usize) { 227 | let _ = Vec::from_raw_parts(data, len, capacity); 228 | } 229 | 230 | extern "C" fn cglue_reserve_vec(vec: &mut CVec, size: usize) -> usize { 231 | let mut vec = TempVec::from(vec); 232 | vec.reserve(size); 233 | vec.capacity() 234 | } 235 | -------------------------------------------------------------------------------- /examples/c-user-bin/.gitignore: -------------------------------------------------------------------------------- 1 | bindings.h 2 | *.so 3 | *.out 4 | *.o 5 | -------------------------------------------------------------------------------- /examples/c-user-bin/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -Wl,--no-as-needed -g -O0 -std=c99 -L../../target/release 3 | LIBS=-lm -ldl -lpthread -l:libplugin_api.a 4 | 5 | ODIR=./ 6 | 7 | bindings: 8 | sh ./bindgen.sh 9 | 10 | %.o: %.c $(DEPS) bindings 11 | $(CC) -c -o $@ $< $(CFLAGS) 12 | 13 | main.out: main.o 14 | cargo build --release 15 | cp ../../target/release/libplugin_lib.so $(ODIR)/ 16 | (cd ../cpp-plugin-lib/ && make) 17 | cp ../cpp-plugin-lib/libplugin_cpp.so $(ODIR)/ 18 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 19 | 20 | .PHONY: all 21 | all: main.out 22 | 23 | .DEFAULT_GOAL := all 24 | 25 | clean: 26 | rm -f libplugin_lib.so libplugin_cpp.so bindings.h $(ODIR)/*.o $(ODIR)/*.out 27 | -------------------------------------------------------------------------------- /examples/c-user-bin/bindgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OUT=${PWD}/bindings.h 4 | CFG=${PWD}/cbindgen.toml 5 | CGLUE_CFG=${PWD}/cglue.toml 6 | 7 | cd ../plugin-api 8 | 9 | ../../target/release/cglue-bindgen +nightly -c $CGLUE_CFG -- --config $CFG --crate plugin-api --output $OUT -l C 10 | -------------------------------------------------------------------------------- /examples/c-user-bin/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | 3 | tab_width = 4 4 | documentation_style = "doxy" 5 | style = "both" 6 | cpp_compat = true 7 | 8 | [parse] 9 | parse_deps = true 10 | 11 | include = ["cglue", "plugin-api"] 12 | 13 | [export] 14 | include = ["FeaturesGroupArcBox", "PluginInnerRef", "PluginInnerMut"] 15 | 16 | [macro_expansion] 17 | bitflags = true 18 | 19 | [fn] 20 | sort_by = "None" 21 | 22 | [parse.expand] 23 | crates = ["cglue", "plugin-api"] 24 | -------------------------------------------------------------------------------- /examples/c-user-bin/cglue.toml: -------------------------------------------------------------------------------- 1 | default_container = "Box" 2 | default_context = "Arc" 3 | -------------------------------------------------------------------------------- /examples/c-user-bin/main.c: -------------------------------------------------------------------------------- 1 | // This example shows client-side usage from C. 2 | 3 | #include 4 | #include 5 | #include "bindings.h" 6 | 7 | int trim(char *str); 8 | 9 | void use_kvstore(FeaturesGroup *obj); 10 | void kvdump(FeaturesGroup *obj); 11 | 12 | int main() { 13 | char name[256]; 14 | 15 | printf("Enter name of the plugin library [plugin_lib]:\n"); 16 | 17 | fgets(name, sizeof(name), stdin); 18 | int len = trim(name); 19 | 20 | PluginInner obj; 21 | int ret; 22 | 23 | // Load the plugin, returns 0 on success. 24 | // Otherwise, obj has undefined data. 25 | if (ret = load_plugin(len > 0 ? name : "plugin_lib", &obj)) { 26 | printf("Failed to load plugin (%d)!\n", ret); 27 | plugininner_drop(obj); 28 | return 1; 29 | } 30 | 31 | // In this block we temporarily borrow the features. 32 | { 33 | // Until `borrowed` is dropped, `obj` should not be touched. 34 | FeaturesGroup borrowed = borrow_features(&obj); 35 | 36 | // print_self is part of MainFeature, and always exists. 37 | featuresgroup_print_self(&borrowed); 38 | 39 | // These are optional features we check to see if they exist 40 | if (borrowed.vtbl_keyvaluestore != NULL) { 41 | printf("Using borrowed kvstore:\n"); 42 | use_kvstore(&borrowed); 43 | } 44 | 45 | if (borrowed.vtbl_keyvaluedumper != NULL) { 46 | printf("Dumping borrowed kvstore:\n"); 47 | kvdump(&borrowed); 48 | } 49 | 50 | printf("Borrowed done.\n"); 51 | 52 | // Release the resources, `obj` is safe to use again. 53 | featuresgroup_drop(borrowed); 54 | } 55 | 56 | // In this block we consume obj and it becomes unusable. 57 | { 58 | FeaturesGroup owned = into_features(obj); 59 | 60 | featuresgroup_print_self(&owned); 61 | 62 | if (owned.vtbl_keyvaluestore != NULL) { 63 | printf("Using owned kvstore:\n"); 64 | use_kvstore(&owned); 65 | } 66 | 67 | if (owned.vtbl_keyvaluedumper != NULL) { 68 | printf("Dumping owned kvstore:\n"); 69 | kvdump(&owned); 70 | } 71 | 72 | // We drop the last CGlue object, meaning the library should be freed. 73 | featuresgroup_drop(owned); 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | int trim(char *str) { 80 | int len = strlen(str); 81 | 82 | for (int i = 0; i < 2; i++) { 83 | char c = str[--len]; 84 | 85 | if (c == '\n' || c == '\r') { 86 | str[len] = '\0'; 87 | } else { 88 | len++; 89 | break; 90 | } 91 | } 92 | 93 | return len; 94 | } 95 | 96 | void use_kvstore(FeaturesGroup *obj) { 97 | char key[256]; 98 | 99 | printf("Enter key:\n"); 100 | 101 | fgets(key, sizeof(key), stdin); 102 | trim(key); 103 | 104 | // STR will automatically construct a CSliceRef, 105 | // but it can also be done manually for reuse. 106 | printf("Cur val: %zu\n", featuresgroup_get_key_value(obj, STR(key))); 107 | 108 | size_t new_val = 0; 109 | 110 | printf("Enter value:\n"); 111 | scanf("%zu", &new_val); 112 | 113 | char nl[2]; 114 | fgets(nl, sizeof(nl), stdin); 115 | 116 | featuresgroup_write_key_value(obj, STR(key), new_val); 117 | } 118 | 119 | bool kvdump_callback(void *unused, KeyValue kv) { 120 | fwrite(kv._0.data, sizeof(char), kv._0.len, stdout); 121 | printf(" : %zu\n", kv._1); 122 | return true; 123 | } 124 | 125 | void kvdump(FeaturesGroup *obj) { 126 | // Construct the simplest callback here that takes in the `kvdump_callback` function. 127 | featuresgroup_dump_key_values(obj, CALLBACK(KeyValue, NULL, kvdump_callback)); 128 | 129 | int ints[32]; 130 | 131 | for (int i = 0; i < 32; i++) { 132 | ints[i] = i * i; 133 | } 134 | 135 | // This macro takes the statically known array size, 136 | // and construct a CIterator with the buffer. 137 | // i32 is the rust type for int, thus we have to 138 | // specify the type. For structure types, this is 139 | // usually not needed, and we can use `BUF_ITER_ARR`. 140 | BUF_ITER_ARR_SPEC(i32, int, int_iter, ints); 141 | 142 | featuresgroup_print_ints(obj, int_iter); 143 | } 144 | 145 | -------------------------------------------------------------------------------- /examples/c-user-prefixed-bin/.gitignore: -------------------------------------------------------------------------------- 1 | bindings.h 2 | *.so 3 | *.out 4 | *.o 5 | -------------------------------------------------------------------------------- /examples/c-user-prefixed-bin/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -Wl,--no-as-needed -g -O0 -std=c99 -L../../target/release 3 | LIBS=-lm -ldl -lpthread -l:libplugin_api.a 4 | 5 | ODIR=./ 6 | 7 | bindings: 8 | sh ./bindgen.sh 9 | 10 | %.o: %.c $(DEPS) bindings 11 | $(CC) -c -o $@ $< $(CFLAGS) 12 | 13 | main.out: main.o 14 | cargo build --release 15 | cp ../../target/release/libplugin_lib.so $(ODIR)/ 16 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 17 | 18 | .PHONY: all 19 | all: main.out 20 | 21 | .DEFAULT_GOAL := all 22 | 23 | clean: 24 | rm -f libplugin_lib.so bindings.h $(ODIR)/*.o $(ODIR)/*.out 25 | -------------------------------------------------------------------------------- /examples/c-user-prefixed-bin/bindgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OUT=${PWD}/bindings.h 4 | CFG=${PWD}/cbindgen.toml 5 | CGLUE_CFG=${PWD}/cglue.toml 6 | 7 | cd ../plugin-api 8 | 9 | ../../target/release/cglue-bindgen +nightly -c $CGLUE_CFG -- --config $CFG --crate plugin-api --output $OUT -l C 10 | -------------------------------------------------------------------------------- /examples/c-user-prefixed-bin/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | 3 | tab_width = 4 4 | documentation_style = "doxy" 5 | style = "both" 6 | cpp_compat = true 7 | 8 | [parse] 9 | parse_deps = true 10 | 11 | include = ["cglue", "plugin-api"] 12 | 13 | [export] 14 | include = ["FeaturesGroupArcBox", "PluginInnerRef", "PluginInnerMut"] 15 | 16 | [macro_expansion] 17 | bitflags = true 18 | 19 | [fn] 20 | sort_by = "None" 21 | 22 | [parse.expand] 23 | crates = ["cglue", "plugin-api"] 24 | -------------------------------------------------------------------------------- /examples/c-user-prefixed-bin/cglue.toml: -------------------------------------------------------------------------------- 1 | default_container = "Box" 2 | default_context = "Arc" 3 | 4 | # This is only a test if adding a function prefix would work 5 | function_prefix = "cglue" 6 | -------------------------------------------------------------------------------- /examples/c-user-prefixed-bin/main.c: -------------------------------------------------------------------------------- 1 | // This is exactly the same as c-user-bin, but 2 | // we add a function prefix to avoid mixing up with stdlib 3 | 4 | #include 5 | #include 6 | #include "bindings.h" 7 | 8 | int trim(char *str); 9 | 10 | void use_kvstore(FeaturesGroup *obj); 11 | void kvdump(FeaturesGroup *obj); 12 | 13 | int main() { 14 | char name[256]; 15 | 16 | printf("Enter name of the plugin library [plugin_lib]:\n"); 17 | 18 | fgets(name, sizeof(name), stdin); 19 | int len = trim(name); 20 | 21 | PluginInner obj; 22 | int ret; 23 | 24 | if (ret = load_plugin(len > 0 ? name : "plugin_lib", &obj)) { 25 | printf("Failed to load plugin (%d)!\n", ret); 26 | cglue_plugininner_drop(obj); 27 | return 1; 28 | } 29 | 30 | { 31 | FeaturesGroup borrowed = cglue_borrow_features(&obj); 32 | 33 | cglue_featuresgroup_print_self(&borrowed); 34 | 35 | if (borrowed.vtbl_keyvaluestore != NULL) { 36 | printf("Using borrowed kvstore:\n"); 37 | use_kvstore(&borrowed); 38 | } 39 | 40 | if (borrowed.vtbl_keyvaluedumper != NULL) { 41 | printf("Dumping borrowed kvstore:\n"); 42 | kvdump(&borrowed); 43 | } 44 | 45 | printf("Borrowed done.\n"); 46 | 47 | cglue_featuresgroup_drop(borrowed); 48 | } 49 | 50 | { 51 | FeaturesGroup owned = cglue_into_features(obj); 52 | 53 | cglue_featuresgroup_print_self(&owned); 54 | 55 | if (owned.vtbl_keyvaluestore != NULL) { 56 | printf("Using owned kvstore:\n"); 57 | use_kvstore(&owned); 58 | } 59 | 60 | if (owned.vtbl_keyvaluedumper != NULL) { 61 | printf("Dumping owned kvstore:\n"); 62 | kvdump(&owned); 63 | } 64 | 65 | cglue_featuresgroup_drop(owned); 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | int trim(char *str) { 72 | int len = strlen(str); 73 | 74 | for (int i = 0; i < 2; i++) { 75 | char c = str[--len]; 76 | 77 | if (c == '\n' || c == '\r') { 78 | str[len] = '\0'; 79 | } else { 80 | len++; 81 | break; 82 | } 83 | } 84 | 85 | return len; 86 | } 87 | 88 | void use_kvstore(FeaturesGroup *obj) { 89 | char key[256]; 90 | 91 | printf("Enter key:\n"); 92 | 93 | fgets(key, sizeof(key), stdin); 94 | int len = trim(key); 95 | 96 | CSliceRef_u8 key_slice; 97 | key_slice.data = (unsigned char *)key; 98 | key_slice.len = len; 99 | 100 | printf("Cur val: %zu\n", cglue_featuresgroup_get_key_value(obj, key_slice)); 101 | 102 | size_t new_val = 0; 103 | 104 | printf("Enter value:\n"); 105 | scanf("%zu", &new_val); 106 | 107 | char nl[2]; 108 | fgets(nl, sizeof(nl), stdin); 109 | 110 | cglue_featuresgroup_write_key_value(obj, key_slice, new_val); 111 | } 112 | 113 | bool kvdump_callback(void *unused, KeyValue kv) { 114 | fwrite(kv._0.data, sizeof(char), kv._0.len, stdout); 115 | printf(" : %zu\n", kv._1); 116 | return true; 117 | } 118 | 119 | void kvdump(FeaturesGroup *obj) { 120 | cglue_featuresgroup_dump_key_values(obj, CALLBACK(KeyValue, NULL, kvdump_callback)); 121 | 122 | int ints[32]; 123 | 124 | for (int i = 0; i < 32; i++) { 125 | ints[i] = i * i; 126 | } 127 | 128 | BUF_ITER_ARR_SPEC(i32, int, int_iter, ints); 129 | 130 | cglue_featuresgroup_print_ints(obj, int_iter); 131 | } 132 | 133 | -------------------------------------------------------------------------------- /examples/cpp-plugin-lib/.gitignore: -------------------------------------------------------------------------------- 1 | bindings.h 2 | *.so 3 | *.out 4 | *.o 5 | -------------------------------------------------------------------------------- /examples/cpp-plugin-lib/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -pedantic -Wl,--no-as-needed -std=c++14 -g -O0 -shared -fpie -fPIC -L../../target/release 3 | LIBS=-lm -ldl -lpthread -l:libplugin_api.a -lstdc++ 4 | 5 | ODIR=./ 6 | 7 | bindings: 8 | sh ./bindgen.sh 9 | 10 | %.o: %.cpp $(DEPS) bindings 11 | $(CC) -c -xc++ -o $@ $< $(CFLAGS) 12 | 13 | libplugin_cpp.so: main.o 14 | cargo build --release 15 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 16 | cp libplugin_cpp.so ../../target/release/ 17 | cp libplugin_cpp.so ../../target/debug/ 18 | 19 | .PHONY: all 20 | all: libplugin_cpp.so 21 | 22 | .DEFAULT_GOAL := all 23 | 24 | clean: 25 | rm -f bindings.h $(ODIR)/*.o $(ODIR)/*.so 26 | -------------------------------------------------------------------------------- /examples/cpp-plugin-lib/bindgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OUT=${PWD}/bindings.h 4 | CFG=${PWD}/cbindgen.toml 5 | CGLUE_CFG=${PWD}/cglue.toml 6 | 7 | cd ../plugin-api 8 | 9 | ../../target/release/cglue-bindgen +nightly -c $CGLUE_CFG -- --config $CFG --crate plugin-api --output $OUT -l C++ 10 | -------------------------------------------------------------------------------- /examples/cpp-plugin-lib/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | 3 | tab_width = 4 4 | documentation_style = "doxy" 5 | style = "both" 6 | cpp_compat = true 7 | 8 | [parse] 9 | parse_deps = true 10 | 11 | include = ["cglue", "plugin-api"] 12 | 13 | [export] 14 | include = ["PluginHeader"] 15 | 16 | [macro_expansion] 17 | bitflags = true 18 | 19 | [fn] 20 | sort_by = "None" 21 | 22 | [parse.expand] 23 | crates = ["cglue", "plugin-api"] 24 | -------------------------------------------------------------------------------- /examples/cpp-plugin-lib/cglue.toml: -------------------------------------------------------------------------------- 1 | default_container = "Box" 2 | default_context = "Arc" 3 | -------------------------------------------------------------------------------- /examples/cpp-plugin-lib/main.cpp: -------------------------------------------------------------------------------- 1 | // This is an example of a plugin implemented in C++ 2 | // 3 | // It lacks certain features of the Rust library, but should be 4 | // detailed enough to describe every typical plugin usage. 5 | 6 | #include 7 | #include 8 | #include "bindings.h" 9 | #include 10 | #include 11 | 12 | // Data of the main features key value store. 13 | struct KvStore { 14 | std::unordered_map map; 15 | }; 16 | 17 | // This is an implementation of the `KeyValueStore + MainFeature` traits. 18 | // 19 | // We inherit from `FeaturesGroupContainer` so that implementation 20 | // functions can easily access the fields. 21 | template, typename C = CArc> 22 | struct KvStoreContainer : FeaturesGroupContainer { 23 | 24 | // Parent type must always be defined to the class we inherit. 25 | // This parent is used to know which type the implementation is actually for. 26 | using Parent = FeaturesGroupContainer; 27 | 28 | KvStoreContainer(T &&instance, C &&context) : Parent { instance, context } {} 29 | 30 | static void print_self(const Parent *self) { 31 | for (const auto &e : self->instance->map) { 32 | printf("%s: %zu\n", e.first.c_str(), e.second); 33 | } 34 | } 35 | 36 | static void write_key_value(Parent *self, CSliceRef name, size_t val) { 37 | self->instance->map[name] = val; 38 | } 39 | 40 | static size_t get_key_value(const Parent *self, CSliceRef name) { 41 | auto it = self->instance->map.find(name); 42 | if (it == self->instance->map.end()) { 43 | return 0; 44 | } else { 45 | return it->second; 46 | } 47 | } 48 | 49 | static void dump_key_values(const Parent *self, OpaqueCallback callback) { 50 | for (const auto &e : self->instance->map) { 51 | if (!callback.func(callback.context, KeyValue { e.first, e.second })) { 52 | break; 53 | } 54 | } 55 | } 56 | 57 | static void print_ints(const Parent *self, CIterator ints) { 58 | int i = 0; 59 | for (int v : ints) { 60 | printf("%d: %d\n", i++, v); 61 | } 62 | } 63 | }; 64 | 65 | // This is the actual object that gets boxed. 66 | // 67 | // Use it like a normal class/struct. 68 | struct PluginCPP { 69 | KvStore store; 70 | }; 71 | 72 | // Vtables inserted when borrowing or converting into features. 73 | static MainFeatureVtblImpl> main_feature_vtbl; 74 | static MainFeatureVtblImpl> main_feature_mut_vtbl; 75 | static KeyValueStoreVtblImpl> kvstore_vtbl; 76 | static KeyValueStoreVtblImpl> kvstore_mut_vtbl; 77 | static KeyValueDumperVtblImpl> kvdumper_vtbl; 78 | 79 | // This contains implementation for `PluginInner` trait. 80 | template, typename C = CArc> 81 | struct PluginCPPContainer : CGlueObjContainer> { 82 | 83 | // The same as in KvStoreContainer, define the parent. 84 | using Parent = CGlueObjContainer>; 85 | 86 | // Initialize the underlying container with instance and context objects. 87 | PluginCPPContainer(T &&instance, C &&context) : Parent { instance, context } {} 88 | 89 | using BorrowedType = FeaturesGroup, C>; 90 | using OwnedType = FeaturesGroup, C>; 91 | using OwnedTypeMut = FeaturesGroup; 92 | 93 | static BorrowedType borrow_features(Parent *self) { 94 | BorrowedType ret; 95 | // Need to manually opaquify the vtables (won't be needed in the future). 96 | ret.vtbl_mainfeature = (decltype(ret.vtbl_mainfeature))&main_feature_vtbl; 97 | ret.vtbl_keyvaluestore = (decltype(ret.vtbl_keyvaluestore))&kvstore_vtbl; 98 | ret.vtbl_keyvaluedumper = (decltype(ret.vtbl_keyvaluedumper))&kvdumper_vtbl; 99 | ret.container.instance = CBox(&self->instance.instance->store); 100 | return ret; 101 | } 102 | 103 | static OwnedType into_features(Parent self) { 104 | OwnedType ret; 105 | ret.vtbl_mainfeature = (decltype(ret.vtbl_mainfeature))&main_feature_vtbl; 106 | ret.vtbl_keyvaluestore = (decltype(ret.vtbl_keyvaluestore))&kvstore_vtbl; 107 | ret.vtbl_keyvaluedumper = (decltype(ret.vtbl_keyvaluedumper))&kvdumper_vtbl; 108 | ret.container.instance = CBox(std::move(self.instance.instance->store)); 109 | return ret; 110 | } 111 | 112 | static OwnedTypeMut *mut_features(Parent *self) { 113 | // Take the return value's address out of the ret_tmp storage. 114 | OwnedTypeMut *ret = &self->ret_tmp.assume_init().mut_features; 115 | ret->vtbl_mainfeature = (decltype(ret->vtbl_mainfeature))&main_feature_mut_vtbl; 116 | ret->vtbl_keyvaluestore = (decltype(ret->vtbl_keyvaluestore))&kvstore_mut_vtbl; 117 | // We do not need to box here - we are returning a reference (pointer). 118 | ret->container.instance = self->instance.instance; 119 | return ret; 120 | } 121 | }; 122 | 123 | // This is the root vtable that we put on the plugin object. 124 | static PluginInnerVtblImpl> plugin_vtbl; 125 | 126 | extern "C" { 127 | // Create a new plugin ArcBox object that clones the library into itself. 128 | PluginInnerBaseArcBox create_plugin(CArc &library) { 129 | PluginInnerBaseArcBox ret; 130 | 131 | ret.vtbl = &plugin_vtbl; 132 | ret.container = PluginCPPContainer<>(CBox::new_box(), library.clone()); 133 | 134 | return ret; 135 | } 136 | 137 | // Define the header and opaque cast the plugin creation function. 138 | PluginHeader PLUGIN_HEADER { ROOT_LAYOUT, (decltype(PLUGIN_HEADER.create))create_plugin }; 139 | } 140 | -------------------------------------------------------------------------------- /examples/cpp-user-bin/.gitignore: -------------------------------------------------------------------------------- 1 | bindings.h 2 | *.so 3 | *.out 4 | *.o 5 | -------------------------------------------------------------------------------- /examples/cpp-user-bin/Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | CFLAGS = -Wl,--no-as-needed -std=c++11 -pedantic -g -O0 -L../../target/release 3 | LIBS=-lm -ldl -lpthread -l:libplugin_api.a -lstdc++ 4 | 5 | ODIR=./ 6 | 7 | bindings: 8 | sh ./bindgen.sh 9 | 10 | %.o: %.cpp $(DEPS) bindings 11 | $(CC) -c -xc++ -o $@ $< $(CFLAGS) 12 | 13 | main.out: main.o 14 | cargo build --release 15 | cp ../../target/release/libplugin_lib.so $(ODIR)/ 16 | (cd ../cpp-plugin-lib/ && make) 17 | cp ../cpp-plugin-lib/libplugin_cpp.so $(ODIR)/ 18 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 19 | 20 | .PHONY: all 21 | all: main.out 22 | 23 | .DEFAULT_GOAL := all 24 | 25 | clean: 26 | rm -f libplugin_lib.so libplugin_cpp.so bindings.h $(ODIR)/*.o $(ODIR)/*.out 27 | -------------------------------------------------------------------------------- /examples/cpp-user-bin/bindgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OUT=${PWD}/bindings.h 4 | CFG=${PWD}/cbindgen.toml 5 | CGLUE_CFG=${PWD}/cglue.toml 6 | 7 | cd ../plugin-api 8 | 9 | ../../target/release/cglue-bindgen +nightly -c $CGLUE_CFG -- --config $CFG --crate plugin-api --output $OUT -l C++ 10 | -------------------------------------------------------------------------------- /examples/cpp-user-bin/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | 3 | tab_width = 4 4 | documentation_style = "doxy" 5 | style = "both" 6 | cpp_compat = true 7 | 8 | [parse] 9 | parse_deps = true 10 | 11 | include = ["cglue", "plugin-api"] 12 | 13 | [macro_expansion] 14 | bitflags = true 15 | 16 | [fn] 17 | sort_by = "None" 18 | 19 | [parse.expand] 20 | crates = ["cglue", "plugin-api"] 21 | -------------------------------------------------------------------------------- /examples/cpp-user-bin/cglue.toml: -------------------------------------------------------------------------------- 1 | default_container = "Box" 2 | default_context = "Arc" 3 | -------------------------------------------------------------------------------- /examples/cpp-user-bin/main.cpp: -------------------------------------------------------------------------------- 1 | // This example shows client-side usage from C++ 2 | 3 | #include 4 | #include 5 | #include "bindings.h" 6 | #include 7 | 8 | int trim(char *str); 9 | 10 | template 11 | void use_kvstore(T& obj); 12 | 13 | template 14 | void kvdump(T& obj); 15 | 16 | int main() { 17 | char name[256]; 18 | 19 | printf("Enter name of the plugin library [plugin_lib]:\n"); 20 | 21 | fgets(name, sizeof(name), stdin); 22 | int len = trim(name); 23 | 24 | PluginInner obj; 25 | int ret; 26 | 27 | // Load the plugin, ret is 0 on success. 28 | if (ret = load_plugin(len > 0 ? name : "plugin_lib", &obj)) { 29 | printf("Failed to load plugin (%d)!\n", ret); 30 | return 1; 31 | } 32 | 33 | // In this block we only use borrowed features. 34 | // After the block `borrowed` will be freed. 35 | { 36 | printf("%p %p\n", obj.container.instance.instance, obj.container.instance.drop_fn); 37 | // While `borrowed` exists, `obj` should never be used. 38 | auto borrowed = obj.borrow_features(); 39 | printf("%p %p\n", borrowed.container.instance.instance, borrowed.container.instance.drop_fn); 40 | 41 | // print_self is part of MainFeature, and always exists. 42 | borrowed.print_self(); 43 | 44 | // These are optional features we check to see if they exist. 45 | if (borrowed.vtbl_keyvaluestore != nullptr) { 46 | printf("Using borrowed kvstore:\n"); 47 | use_kvstore(borrowed); 48 | } 49 | 50 | if (borrowed.vtbl_keyvaluedumper != nullptr) { 51 | printf("Dumping borrowed kvstore:\n"); 52 | kvdump(borrowed); 53 | } 54 | 55 | printf("Borrowed done.\n"); 56 | } 57 | 58 | // In this block we convert the object into features. 59 | // `obj` becomes unusable, `owned` is the one to be used. 60 | // After the block `owned` gets freed, and so should the library. 61 | { 62 | printf("%p %p\n", obj.container.instance.instance, obj.container.instance.drop_fn); 63 | auto owned = std::move(obj).into_features(); 64 | 65 | owned.print_self(); 66 | 67 | if (owned.vtbl_keyvaluestore != nullptr) { 68 | printf("Using owned kvstore:\n"); 69 | use_kvstore(owned); 70 | } 71 | 72 | if (owned.vtbl_keyvaluedumper != nullptr) { 73 | printf("Dumping owned kvstore:\n"); 74 | kvdump(owned); 75 | } 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | int trim(char *str) { 82 | int len = strlen(str); 83 | 84 | for (int i = 0; i < 2; i++) { 85 | char c = str[--len]; 86 | 87 | if (c == '\n' || c == '\r') { 88 | str[len] = '\0'; 89 | } else { 90 | len++; 91 | break; 92 | } 93 | } 94 | 95 | return len; 96 | } 97 | 98 | template 99 | void use_kvstore(T& obj) { 100 | char key[256]; 101 | 102 | printf("Enter key:\n"); 103 | 104 | fgets(key, sizeof(key), stdin); 105 | trim(key); 106 | 107 | // CSliceRef gets constructed instead of key 108 | printf("Cur val: %zu\n", obj.get_key_value(key)); 109 | 110 | size_t new_val = 0; 111 | 112 | printf("Enter value:\n"); 113 | scanf("%zu", &new_val); 114 | 115 | char nl[2]; 116 | fgets(nl, sizeof(nl), stdin); 117 | 118 | // CSliceRef gets constructed instead of key 119 | obj.write_key_value(key, new_val); 120 | } 121 | 122 | template 123 | void kvdump(T& obj) { 124 | // This lambda gets converted into callback. 125 | // We could also collect the values into a 126 | // vector, by passing through its pointer. 127 | obj.dump_key_values([](KeyValue kv) { 128 | fwrite(kv._0.data, sizeof(char), kv._0.len, stdout); 129 | printf(" : %zu\n", kv._1); 130 | return true; 131 | }); 132 | 133 | std::vector ints; 134 | 135 | for (int i = 0; i < 32; i++) { 136 | ints.push_back(i * i); 137 | } 138 | 139 | // In C++17 we do not have to supply the vector type, 140 | // but here its sadly necessary. 141 | obj.print_ints(CPPIterator>(ints)); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /examples/plugin-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plugin-api" 3 | version = "0.1.0" 4 | authors = ["Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["lib", "cdylib", "staticlib"] 9 | 10 | [dependencies] 11 | cglue = { path = "../../cglue/", features = ["layout_checks"] } 12 | libloading = "0.7" 13 | abi_stable10 = { package = "abi_stable", version = "0.10", default-features = false } 14 | _abi_stable11 = { package = "abi_stable", version = "0.11", optional = true, default-features = false } 15 | 16 | [features] 17 | abi_stable11 = ["_abi_stable11", "cglue/abi_stable11"] 18 | -------------------------------------------------------------------------------- /examples/plugin-api/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is the main plugin API 2 | //! 3 | //! This crate is shared by plugins and users. 4 | 5 | #[cfg(not(feature = "abi_stable11"))] 6 | extern crate abi_stable10 as abi_stable; 7 | 8 | #[cfg(feature = "abi_stable11")] 9 | extern crate _abi_stable11 as abi_stable; 10 | 11 | pub use abi_stable::type_layout::TypeLayout; 12 | use abi_stable::StableAbi; 13 | use cglue::prelude::v1::{trait_group::compare_layouts, *}; 14 | use core::mem::MaybeUninit; 15 | use core::num::NonZeroI32; 16 | use libloading::{library_filename, Library, Symbol}; 17 | 18 | #[cglue_trait] 19 | pub trait PluginInner<'a> { 20 | #[wrap_with_group(FeaturesGroup)] 21 | type BorrowedType: MainFeature + 'a; 22 | #[wrap_with_group(FeaturesGroup)] 23 | type OwnedType: MainFeature + 'static; 24 | #[wrap_with_group_mut(FeaturesGroup)] 25 | type OwnedTypeMut: MainFeature + 'a; 26 | 27 | fn borrow_features(&'a mut self) -> Self::BorrowedType; 28 | 29 | fn into_features(self) -> Self::OwnedType; 30 | 31 | fn mut_features(&'a mut self) -> &'a mut Self::OwnedTypeMut; 32 | } 33 | 34 | /// Having the inner type with a lifetime allows to borrow features for any lifetime. 35 | /// 36 | /// This could be avoided with [GAT](https://rust-lang.github.io/rfcs/1598-generic_associated_types.html) 37 | pub trait Plugin: for<'a> PluginInner<'a> {} 38 | impl PluginInner<'a>> Plugin for T {} 39 | 40 | #[repr(C)] 41 | #[derive(::abi_stable::StableAbi)] 42 | pub struct KeyValue<'a>(pub CSliceRef<'a, u8>, pub usize); 43 | 44 | pub type KeyValueCallback<'a> = OpaqueCallback<'a, KeyValue<'a>>; 45 | 46 | #[cglue_trait] 47 | #[cglue_forward] 48 | pub trait MainFeature { 49 | fn print_self(&self); 50 | } 51 | 52 | #[cglue_trait] 53 | #[cglue_forward] 54 | pub trait KeyValueStore { 55 | fn write_key_value(&mut self, name: &str, val: usize); 56 | fn get_key_value(&self, name: &str) -> usize; 57 | } 58 | 59 | #[cglue_trait] 60 | pub trait KeyValueDumper { 61 | fn dump_key_values<'a>(&'a self, callback: KeyValueCallback<'a>); 62 | fn print_ints(&self, iter: CIterator); 63 | } 64 | 65 | cglue_trait_group!(FeaturesGroup, { 66 | MainFeature 67 | }, { 68 | KeyValueStore, 69 | KeyValueDumper, 70 | Clone 71 | }); 72 | 73 | /// Describes possible errors that can occur loading the library 74 | #[repr(u8)] 75 | #[derive(Debug, Clone, Copy)] 76 | pub enum Error { 77 | Path = 1, 78 | Loading = 2, 79 | Symbol = 3, 80 | Abi = 4, 81 | } 82 | 83 | impl IntError for Error { 84 | fn into_int_err(self) -> NonZeroI32 { 85 | NonZeroI32::new(self as u8 as _).unwrap() 86 | } 87 | 88 | fn from_int_err(err: NonZeroI32) -> Self { 89 | match err.get() { 90 | 1 => Self::Path, 91 | 2 => Self::Loading, 92 | 3 => Self::Symbol, 93 | 4 => Self::Abi, 94 | _ => unreachable!(), 95 | } 96 | } 97 | } 98 | 99 | impl std::fmt::Display for Error { 100 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 101 | write!(f, "{:?}", self) 102 | } 103 | } 104 | 105 | impl std::error::Error for Error {} 106 | 107 | /// Plugin header that the API looks for. 108 | /// 109 | /// Plugins should define the header with name `PLUGIN_HEADER` with no mangling. 110 | #[repr(C)] 111 | pub struct PluginHeader { 112 | pub layout: &'static TypeLayout, 113 | pub create: extern "C" fn(&CArc) -> PluginInnerArcBox<'static>, 114 | } 115 | 116 | /// Load a plugin from a given library. 117 | /// 118 | /// # Safety 119 | /// 120 | /// Input library must implement a correct `create_plugin` and `get_root_layout()` functions. 121 | /// Its signatures must be as follows: 122 | /// 123 | /// `extern "C" fn crate_plugin(&CArc) -> PluginInnerArcBox<'static>` 124 | /// `extern "C" fn get_root_layout() -> Option<&'static TypeLayout>` 125 | /// 126 | /// Where `T` is any type, since it's opaque. Meanwhile, `get_root_layout` should simply 127 | /// [call the one in this crate](self::get_root_layout). It is used to verify 128 | /// version mismatches. 129 | #[no_mangle] 130 | pub unsafe extern "C" fn load_plugin( 131 | name: ReprCStr<'_>, 132 | ok_out: &mut MaybeUninit>, 133 | ) -> i32 { 134 | load_plugin_impl(name.as_ref()).into_int_out_result(ok_out) 135 | } 136 | 137 | unsafe fn load_plugin_impl(name: &str) -> Result, Error> { 138 | let mut current_exe = std::env::current_exe().map_err(|_| Error::Path)?; 139 | current_exe.set_file_name(library_filename(name)); 140 | let lib = Library::new(current_exe).map_err(|e| { 141 | println!("{}", e); 142 | Error::Loading 143 | })?; 144 | 145 | let header: Symbol<&'static PluginHeader> = lib.get(b"PLUGIN_HEADER\0").map_err(|e| { 146 | println!("{}", e); 147 | Error::Symbol 148 | })?; 149 | let header = header.into_raw(); 150 | 151 | if !compare_layouts(Some(ROOT_LAYOUT), Some(header.layout)).is_valid_strict() { 152 | return Err(Error::Abi); 153 | } 154 | 155 | let arc = CArc::from(lib); 156 | Ok((header.create)(&arc.into_opaque())) 157 | } 158 | 159 | /// Layout for the root vtable. 160 | /// 161 | /// Layout that should be embedded to a `PluginHeader`. 162 | /// Other layouts are not necessary, because the very root depends on them already. 163 | #[no_mangle] 164 | pub static ROOT_LAYOUT: &TypeLayout = PluginInnerArcBox::LAYOUT; 165 | -------------------------------------------------------------------------------- /examples/plugin-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plugin-lib" 3 | version = "0.1.0" 4 | authors = ["Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["lib", "cdylib", "staticlib"] 9 | 10 | [dependencies] 11 | cglue = { path = "../../cglue/", features = ["layout_checks"] } 12 | plugin-api = { path = "../plugin-api" } 13 | libloading = "0.7" 14 | -------------------------------------------------------------------------------- /examples/plugin-lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Example plugin library. 2 | //! 3 | //! This plugin crate will not be known to the user, both parties will interact with the help of 4 | //! the shared plugin API. 5 | 6 | use cglue::prelude::v1::*; 7 | use plugin_api::*; 8 | use std::collections::HashMap; 9 | 10 | #[derive(Default)] 11 | struct KvRoot { 12 | store: KvStore, 13 | } 14 | 15 | impl<'a> PluginInner<'a> for KvRoot { 16 | type BorrowedType = Fwd<&'a mut KvStore>; 17 | type OwnedType = KvStore; 18 | type OwnedTypeMut = KvStore; 19 | 20 | fn borrow_features(&'a mut self) -> Self::BorrowedType { 21 | self.store.forward_mut() 22 | } 23 | 24 | fn into_features(self) -> Self::OwnedType { 25 | self.store 26 | } 27 | 28 | fn mut_features(&'a mut self) -> &'a mut Self::OwnedTypeMut { 29 | &mut self.store 30 | } 31 | } 32 | 33 | #[derive(Debug, Default, Clone)] 34 | struct KvStore { 35 | map: HashMap, 36 | } 37 | 38 | impl MainFeature for KvStore { 39 | fn print_self(&self) { 40 | println!("{:?}", self.map); 41 | } 42 | } 43 | 44 | impl KeyValueStore for KvStore { 45 | fn write_key_value(&mut self, name: &str, val: usize) { 46 | self.map.insert(name.to_string(), val); 47 | } 48 | 49 | fn get_key_value(&self, name: &str) -> usize { 50 | self.map.get(name).copied().unwrap_or(0) 51 | } 52 | } 53 | 54 | impl KeyValueDumper for KvStore { 55 | fn dump_key_values<'a>(&'a self, callback: KeyValueCallback<'a>) { 56 | self.map 57 | .iter() 58 | .map(|(k, v)| KeyValue(k.as_str().into(), *v)) 59 | .feed_into(callback); 60 | } 61 | 62 | fn print_ints(&self, iter: CIterator) { 63 | for (cnt, i) in iter.enumerate() { 64 | println!("{}: {}", cnt, i); 65 | } 66 | } 67 | } 68 | 69 | cglue_impl_group!(KvStore, FeaturesGroup, 70 | // Owned `KvStore` has these types 71 | { 72 | KeyValueStore, 73 | KeyValueDumper, 74 | }, 75 | // The forward type can not be cloned, and KeyValueDumper is not implemented 76 | { 77 | KeyValueStore, 78 | }); 79 | 80 | extern "C" fn create_plugin(lib: &CArc) -> PluginInnerArcBox<'static> { 81 | trait_obj!((KvRoot::default(), lib.clone()) as PluginInner) 82 | } 83 | 84 | #[no_mangle] 85 | pub static PLUGIN_HEADER: PluginHeader = PluginHeader { 86 | layout: ROOT_LAYOUT, 87 | create: create_plugin, 88 | }; 89 | -------------------------------------------------------------------------------- /examples/pregen-headers/bindgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | OUT_CPP=${PWD}/bindings.hpp 4 | OUT=${PWD}/bindings.h 5 | CFG=${PWD}/cbindgen.toml 6 | CGLUE_CFG=${PWD}/cglue.toml 7 | 8 | cd ../plugin-api 9 | 10 | ../../target/release/cglue-bindgen +nightly -c $CGLUE_CFG -- --config $CFG --crate plugin-api --output $OUT_CPP -l C++ 11 | 12 | ../../target/release/cglue-bindgen +nightly -c $CGLUE_CFG -- --config $CFG --crate plugin-api --output $OUT -l C 13 | -------------------------------------------------------------------------------- /examples/pregen-headers/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "C" 2 | 3 | tab_width = 4 4 | documentation_style = "doxy" 5 | style = "both" 6 | cpp_compat = true 7 | 8 | [parse] 9 | parse_deps = true 10 | 11 | include = ["cglue", "plugin-api"] 12 | 13 | [macro_expansion] 14 | bitflags = true 15 | 16 | [fn] 17 | sort_by = "None" 18 | 19 | [parse.expand] 20 | crates = ["cglue", "plugin-api"] 21 | -------------------------------------------------------------------------------- /examples/pregen-headers/cglue.toml: -------------------------------------------------------------------------------- 1 | default_container = "Box" 2 | default_context = "Arc" 3 | -------------------------------------------------------------------------------- /examples/user-bin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "user-bin" 3 | version = "0.1.0" 4 | authors = ["Aurimas Blažulionis <0x60@pm.me>"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | plugin-api = { path = "../plugin-api" } 9 | cglue = { path = "../../cglue" } 10 | -------------------------------------------------------------------------------- /examples/user-bin/src/main.rs: -------------------------------------------------------------------------------- 1 | //! The user of the plugin API. 2 | //! 3 | //! This crate loads binary plugins using the API, and performs some operations with mandatory and 4 | //! optional traits. 5 | 6 | use cglue::prelude::v1::{result::from_int_result, *}; 7 | use plugin_api::*; 8 | use std::ffi::CString; 9 | use std::io; 10 | use std::mem::MaybeUninit; 11 | 12 | type Result = std::result::Result>; 13 | 14 | fn main() -> Result<()> { 15 | let mut lib = String::new(); 16 | 17 | println!("Enter name of the plugin library [plugin_lib]:"); 18 | 19 | io::stdin().read_line(&mut lib)?; 20 | 21 | if lib.trim().is_empty() { 22 | lib = "plugin_lib".to_string(); 23 | } 24 | 25 | let mut obj = MaybeUninit::uninit(); 26 | let res = unsafe { 27 | load_plugin( 28 | CString::new(lib.trim()).unwrap().as_c_str().into(), 29 | &mut obj, 30 | ) 31 | }; 32 | let mut obj = unsafe { from_int_result::<_, Error>(res, obj) }?; 33 | 34 | { 35 | let mut borrowed = obj.borrow_features(); 36 | 37 | borrowed.print_self(); 38 | 39 | if let Some(obj) = as_mut!(borrowed impl KeyValueStore) { 40 | println!("Using borrowed kvstore:"); 41 | use_kvstore(obj)?; 42 | } 43 | 44 | if let Some(obj) = as_mut!(borrowed impl KeyValueDumper) { 45 | println!("Dumping borrowed kvstore:"); 46 | kvdump(obj); 47 | } 48 | 49 | println!("Borrowed done."); 50 | } 51 | 52 | { 53 | let mut owned = obj.into_features(); 54 | 55 | owned.print_self(); 56 | 57 | if let Some(obj) = as_mut!(owned impl KeyValueStore) { 58 | println!("Using owned kvstore:"); 59 | use_kvstore(obj)?; 60 | } 61 | 62 | // Casting can be combined with a multiple of optional traits. 63 | if let Some(mut obj) = cast!(owned impl KeyValueDumper + KeyValueStore) { 64 | println!("Dumping owned kvstore:"); 65 | kvdump(&mut obj); 66 | 67 | // You can still use the mandatory traits. 68 | obj.print_self(); 69 | } 70 | 71 | println!("Owned done."); 72 | } 73 | 74 | println!("Quitting"); 75 | 76 | Ok(()) 77 | } 78 | 79 | fn use_kvstore(obj: &mut impl KeyValueStore) -> Result<()> { 80 | let mut buf = String::new(); 81 | 82 | println!("Enter key:"); 83 | io::stdin().read_line(&mut buf)?; 84 | 85 | let key = buf.trim().to_string(); 86 | 87 | println!("Cur val: {}", obj.get_key_value(&key)); 88 | 89 | buf.clear(); 90 | println!("Enter value:"); 91 | io::stdin().read_line(&mut buf)?; 92 | 93 | let new_val = buf.trim().parse::()?; 94 | obj.write_key_value(&key, new_val); 95 | 96 | Ok(()) 97 | } 98 | 99 | fn kvdump(obj: &mut impl KeyValueDumper) { 100 | let callback = &mut |KeyValue(key, value)| { 101 | println!("{} : {}", key, value); 102 | true 103 | }; 104 | 105 | obj.dump_key_values(callback.into()); 106 | } 107 | -------------------------------------------------------------------------------- /version-hack/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "version-hack" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # Need to uncomment these before cargo update to keep hashbrown on rust 2018 edition 7 | [dependencies] 8 | indexmap = "~1.7" 9 | once_cell = "~1.14" 10 | thiserror = "=1.0.24" 11 | thiserror-impl = "=1.0.24" 12 | serde = "=1.0.136" 13 | proc-macro2 = "=1.0.65" 14 | memchr = "=2.4.1" 15 | log = "=0.4.18" 16 | crossbeam-utils = "=0.8.16" 17 | 18 | [dev-dependencies] 19 | pollster = "=0.2.0" 20 | -------------------------------------------------------------------------------- /version-hack/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | use super::*; 8 | 9 | #[test] 10 | fn it_works() { 11 | let result = add(2, 2); 12 | assert_eq!(result, 4); 13 | } 14 | } 15 | --------------------------------------------------------------------------------