Rust bindings for C++ functions
Rust code can call (non-member) functions defined in C++, provided that the parameter and return types are supported by Crubit:
- If a parameter or return type is a primitive type, then the bindings for the function use the corresponding Rust type.
- Similarly, if a parameter or return type is a pointer type, then the bindings for the function use the corresponding Rust pointer type.
- If the type is a user-defined type, such as a class type or enum, then the bindings for the function use the bindings for that type.
Additionally, code can call member functions defined in C++ if the parameter and return types are supported by Crubit (see above). Currently, member functions are translated as non-method associated functions.
Examples
Functions
Given the following C++ header:
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef THIRD_PARTY_CRUBIT_EXAMPLES_CPP_FUNCTION_EXAMPLE_H_
#define THIRD_PARTY_CRUBIT_EXAMPLES_CPP_FUNCTION_EXAMPLE_H_
#include <stdint.h>
namespace gshoe {
inline int32_t add_two_integers(int32_t x, int32_t y) { return x + y; }
} // namespace gshoe
#endif // THIRD_PARTY_CRUBIT_EXAMPLES_CPP_FUNCTION_EXAMPLE_H_
Crubit will generate the following bindings, with a safe public function that calls into the corresponding FFI glue:
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Automatically @generated Rust bindings for the following C++ target:
// //examples/cpp/function:example_lib
// Features: custom_ffi_types, non_unpin_ctor, std_unique_ptr, std_vector, supported
#![rustfmt::skip]
#![feature(allocator_api, cfg_sanitize, custom_inner_attributes)]
#![allow(stable_features)]
#![no_std]
#![allow(improper_ctypes)]
#![allow(nonstandard_style)]
#![allow(dead_code, unused_mut)]
#![deny(warnings)]
pub mod gshoe {
/// Generated from: examples/cpp/function/example.h;l=12
#[inline(always)]
pub fn add_two_integers(x: i32, y: i32) -> i32 {
unsafe { crate::detail::__rust_thunk___ZN5gshoe16add_two_integersEii(x, y) }
}
}
// namespace gshoe
mod detail {
#[allow(unused_imports)]
use super::*;
unsafe extern "C" {
pub(crate) unsafe fn __rust_thunk___ZN5gshoe16add_two_integersEii(x: i32, y: i32) -> i32;
}
}
Methods
Given the following C++ header:
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef THIRD_PARTY_CRUBIT_EXAMPLES_CPP_METHOD_EXAMPLE_H_
#define THIRD_PARTY_CRUBIT_EXAMPLES_CPP_METHOD_EXAMPLE_H_
#include <stdint.h>
namespace foo {
struct Bar {
int x;
void MyMethod() {};
};
} // namespace foo
#endif // THIRD_PARTY_CRUBIT_EXAMPLES_CPP_METHOD_EXAMPLE_H_
Crubit will generate the following bindings:
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Automatically @generated Rust bindings for the following C++ target:
// //examples/cpp/method:example_lib
// Features: custom_ffi_types, non_unpin_ctor, std_unique_ptr, std_vector, supported
#![rustfmt::skip]
#![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
#![allow(stable_features)]
#![no_std]
#![allow(improper_ctypes)]
#![allow(nonstandard_style)]
#![allow(dead_code, unused_mut)]
#![deny(warnings)]
pub mod foo {
/// Generated from: examples/cpp/method/example.h;l=12
#[derive(Clone, Copy, ::ctor::MoveAndAssignViaCopy)]
#[repr(C)]
///CRUBIT_ANNOTATE: cpp_type=foo :: Bar
pub struct Bar {
pub x: ::ffi_11::c_int,
}
impl !Send for Bar {}
impl !Sync for Bar {}
unsafe impl ::cxx::ExternType for Bar {
type Id = ::cxx::type_id!("foo :: Bar");
type Kind = ::cxx::kind::Trivial;
}
impl Bar {
/// # Safety
///
/// The caller must ensure that the following unsafe arguments are not misused by the function:
/// * `__this`: raw pointer
///
/// Generated from: examples/cpp/method/example.h;l=14
#[inline(always)]
pub unsafe fn MyMethod(__this: *mut Self) {
crate::detail::__rust_thunk___ZN3foo3Bar8MyMethodEv(__this)
}
}
/// Generated from: examples/cpp/method/example.h;l=12
impl Default for Bar {
#[inline(always)]
fn default() -> Self {
let mut tmp = ::core::mem::MaybeUninit::<Self>::zeroed();
unsafe {
crate::detail::__rust_thunk___ZN3foo3BarC1Ev(&raw mut tmp as *mut _);
tmp.assume_init()
}
}
}
}
// namespace foo
mod detail {
#[allow(unused_imports)]
use super::*;
unsafe extern "C" {
pub(crate) unsafe fn __rust_thunk___ZN3foo3BarC1Ev(__this: *mut ::core::ffi::c_void);
pub(crate) unsafe fn __rust_thunk___ZN3foo3Bar8MyMethodEv(__this: *mut crate::foo::Bar);
}
}
const _: () = {
assert!(::core::mem::size_of::<crate::foo::Bar>() == 4);
assert!(::core::mem::align_of::<crate::foo::Bar>() == 4);
static_assertions::assert_impl_all!(crate::foo::Bar: Copy,Clone);
static_assertions::assert_not_impl_any!(crate::foo::Bar: Drop);
assert!(::core::mem::offset_of!(crate::foo::Bar, x) == 0);
};
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Automatically @generated Rust bindings for the following C++ target:
// //examples/cpp/method:example_lib
// Features: custom_ffi_types, non_unpin_ctor, std_unique_ptr, std_vector, supported
#![rustfmt::skip]
#![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)]
#![allow(stable_features)]
#![no_std]
#![allow(improper_ctypes)]
#![allow(nonstandard_style)]
#![allow(dead_code, unused_mut)]
#![deny(warnings)]
pub mod foo {
/// Generated from: examples/cpp/method/example.h;l=12
#[derive(Clone, Copy, ::ctor::MoveAndAssignViaCopy)]
#[repr(C)]
///CRUBIT_ANNOTATE: cpp_type=foo :: Bar
pub struct Bar {
pub x: ::ffi_11::c_int,
}
impl !Send for Bar {}
impl !Sync for Bar {}
unsafe impl ::cxx::ExternType for Bar {
type Id = ::cxx::type_id!("foo :: Bar");
type Kind = ::cxx::kind::Trivial;
}
impl Bar {
/// # Safety
///
/// The caller must ensure that the following unsafe arguments are not misused by the function:
/// * `__this`: raw pointer
///
/// Generated from: examples/cpp/method/example.h;l=14
#[inline(always)]
pub unsafe fn MyMethod(__this: *mut Self) {
crate::detail::__rust_thunk___ZN3foo3Bar8MyMethodEv(__this)
}
}
/// Generated from: examples/cpp/method/example.h;l=12
impl Default for Bar {
#[inline(always)]
fn default() -> Self {
let mut tmp = ::core::mem::MaybeUninit::<Self>::zeroed();
unsafe {
crate::detail::__rust_thunk___ZN3foo3BarC1Ev(&raw mut tmp as *mut _);
tmp.assume_init()
}
}
}
}
// namespace foo
mod detail {
#[allow(unused_imports)]
use super::*;
unsafe extern "C" {
pub(crate) unsafe fn __rust_thunk___ZN3foo3BarC1Ev(__this: *mut ::core::ffi::c_void);
pub(crate) unsafe fn __rust_thunk___ZN3foo3Bar8MyMethodEv(__this: *mut crate::foo::Bar);
}
}
const _: () = {
assert!(::core::mem::size_of::<crate::foo::Bar>() == 4);
assert!(::core::mem::align_of::<crate::foo::Bar>() == 4);
static_assertions::assert_impl_all!(crate::foo::Bar: Copy,Clone);
static_assertions::assert_not_impl_any!(crate::foo::Bar: Drop);
assert!(::core::mem::offset_of!(crate::foo::Bar, x) == 0);
};
unsafe functions
Which C++ functions are marked unsafe in Rust?
By default, the Rust binding to a C++ function is marked as safe or unsafe
based on the types of its parameters. If a C++ function accepts only simple
types like integers, the resulting Rust binding will be marked as safe.
Functions which accept a raw pointer are automatically marked as unsafe.
This behavior can be overridden using the CRUBIT_UNSAFE,
CRUBIT_UNSAFE_MARK_SAFE and CRUBIT_OVERRIDE_UNSAFE(is_unsafe) macros.
For example, given the following C++ header:
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_FUNCTION_UNSAFE_ATTRIBUTES_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_FUNCTION_UNSAFE_ATTRIBUTES_H_
#include "support/annotations.h"
inline void SafeSignatureWithoutAnnotation() {}
CRUBIT_UNSAFE inline void SafeSignatureButAnnotatedUnsafe() {}
inline void UnsafeSignatureWithoutAnnotation(void*) {}
CRUBIT_UNSAFE_MARK_SAFE inline void UnsafeSignatureButAnnotatedSafe(void*) {}
CRUBIT_OVERRIDE_UNSAFE(/*is_unsafe=*/false) inline void SafeBasedOnBoolean() {}
CRUBIT_OVERRIDE_UNSAFE(/*is_unsafe=*/true) inline void UnsafeBasedOnBoolean() {}
#endif // CRUBIT_RS_BINDINGS_FROM_CC_TEST_FUNCTION_UNSAFE_ATTRIBUTES_H_
Crubit will generate the following bindings:
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Automatically @generated Rust bindings for the following C++ target:
// //examples/cpp/unsafe_attributes:example_lib
// Features: custom_ffi_types, non_unpin_ctor, std_unique_ptr, std_vector, supported
#![rustfmt::skip]
#![feature(allocator_api, cfg_sanitize, custom_inner_attributes)]
#![allow(stable_features)]
#![no_std]
#![allow(improper_ctypes)]
#![allow(nonstandard_style)]
#![allow(dead_code, unused_mut)]
#![deny(warnings)]
/// Generated from: examples/cpp/unsafe_attributes/example.h;l=10
#[inline(always)]
pub fn SafeSignatureWithoutAnnotation() {
unsafe { crate::detail::__rust_thunk___Z30SafeSignatureWithoutAnnotationv() }
}
/// # Safety
///
/// The C++ function is explicitly annotated as unsafe. Ensure that its safety requirements are upheld.
///
/// Generated from: examples/cpp/unsafe_attributes/example.h;l=11
#[inline(always)]
pub unsafe fn SafeSignatureButAnnotatedUnsafe() {
crate::detail::__rust_thunk___Z31SafeSignatureButAnnotatedUnsafev()
}
/// # Safety
///
/// The caller must ensure that the following unsafe arguments are not misused by the function:
/// * `__param_0`: raw pointer
///
/// Generated from: examples/cpp/unsafe_attributes/example.h;l=13
#[inline(always)]
pub unsafe fn UnsafeSignatureWithoutAnnotation(__param_0: *mut ::ffi_11::c_void) {
crate::detail::__rust_thunk___Z32UnsafeSignatureWithoutAnnotationPv(__param_0)
}
/// Generated from: examples/cpp/unsafe_attributes/example.h;l=14
#[inline(always)]
pub fn UnsafeSignatureButAnnotatedSafe(__param_0: *mut ::ffi_11::c_void) {
unsafe { crate::detail::__rust_thunk___Z31UnsafeSignatureButAnnotatedSafePv(__param_0) }
}
// is_unsafe=
/// Generated from: examples/cpp/unsafe_attributes/example.h;l=16
#[inline(always)]
pub fn SafeBasedOnBoolean() {
unsafe { crate::detail::__rust_thunk___Z18SafeBasedOnBooleanv() }
}
// is_unsafe=
/// # Safety
///
/// The C++ function is explicitly annotated as unsafe. Ensure that its safety requirements are upheld.
///
/// Generated from: examples/cpp/unsafe_attributes/example.h;l=17
#[inline(always)]
pub unsafe fn UnsafeBasedOnBoolean() {
crate::detail::__rust_thunk___Z20UnsafeBasedOnBooleanv()
}
// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/integral_constant.h;l=21
// Error while generating bindings for struct 'std::integral_constant<bool, false>':
// Can't generate bindings for std::integral_constant<bool, false>, because of missing required features (crubit.rs-features):
// //examples/cpp/unsafe_attributes:example_lib needs [//features:wrapper] for std::integral_constant<bool, false> (crate::__CcTemplateInstNSt3__u17integral_constantIbLb0EEE is a template instantiation)
// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/integral_constant.h;l=21
// Error while generating bindings for struct 'std::integral_constant<bool, true>':
// Can't generate bindings for std::integral_constant<bool, true>, because of missing required features (crubit.rs-features):
// //examples/cpp/unsafe_attributes:example_lib needs [//features:wrapper] for std::integral_constant<bool, true> (crate::__CcTemplateInstNSt3__u17integral_constantIbLb1EEE is a template instantiation)
mod detail {
#[allow(unused_imports)]
use super::*;
unsafe extern "C" {
pub(crate) unsafe fn __rust_thunk___Z30SafeSignatureWithoutAnnotationv();
pub(crate) unsafe fn __rust_thunk___Z31SafeSignatureButAnnotatedUnsafev();
pub(crate) unsafe fn __rust_thunk___Z32UnsafeSignatureWithoutAnnotationPv(
__param_0: *mut ::ffi_11::c_void,
);
pub(crate) unsafe fn __rust_thunk___Z31UnsafeSignatureButAnnotatedSafePv(
__param_0: *mut ::ffi_11::c_void,
);
pub(crate) unsafe fn __rust_thunk___Z18SafeBasedOnBooleanv();
pub(crate) unsafe fn __rust_thunk___Z20UnsafeBasedOnBooleanv();
}
}
Correct usage of unsafe
Functions marked unsafe cannot be called outside of an unsafe block. In
order to avoid undefined behavior when using unsafe, callers must:
-
Ensure that the pointer being passed to C++ is a valid C++ pointer. In particular, it must not be dangling (e.g.
Nonnull::dangling()). -
Ensure that the safety conditions documented in C++ are upheld. For example, if the C++ function accepts a reference or non-null pointer, then do not pass in
0 as *const _.
Soundness
Note that many “safe” C++ functions may still trigger undefined behavior if used
incorrectly. Regardless of whether a C++ function is marked as unsafe, calls
into C++ will only be memory-safe if the caller verifies that all function
preconditions are met.
Function Attributes
Function attributes are not currently supported. Functions marked
[[noreturn]], [[nodiscard]], etc. do not have bindings.