Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.