Skip to content

Commit

Permalink
Merge pull request #1297 from google/dyn-trait-test
Browse files Browse the repository at this point in the history
Testing and documenting dynamic dispatch for CppRef.
  • Loading branch information
adetaylor authored Jun 15, 2023
2 parents ad8fd56 + 0386ad9 commit 5492cb2
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 28 deletions.
27 changes: 27 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// It would be nice to use the rustversion crate here instead,
// but that doesn't work with inner attributes.
fn main() {
if let Some(ver) = rustc_version() {
if ver.contains("nightly") {
println!("cargo:rustc-cfg=nightly")
}
}
}

fn rustc_version() -> Option<String> {
let rustc = std::env::var_os("RUSTC")?;
let output = std::process::Command::new(rustc)
.arg("--version")
.output()
.ok()?;
let version = String::from_utf8(output.stdout).ok()?;
Some(version)
}
30 changes: 27 additions & 3 deletions examples/reference-wrappers/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#![feature(arbitrary_self_types)]

use autocxx::prelude::*;
use std::pin::Pin;

include_cpp! {
#include "input.h"
Expand Down Expand Up @@ -58,6 +59,16 @@ impl FarmProduce for ffi::Goat {
}
}

trait FarmArea {
fn maintain(self: CppRef<Self>);
}

impl FarmArea for ffi::Field {
fn maintain(self: CppRef<Self>) {
println!("Maintaining");
}
}

fn main() {
// Create a cxx::UniquePtr as normal for a Field object.
let field = ffi::Field::new().within_unique_ptr();
Expand All @@ -76,12 +87,20 @@ fn main() {
// We can no longer take Rust references to the field...
// let _field_rust_ref = field.as_ref();
// However, we can take C++ references. And use such references
// to call methods...
// to call methods in C++. Quite often those methods will
// return other references, like this.
let another_goat = field.as_cpp_ref().get_goat();
another_goat.clone().bleat();
another_goat.sell();
// The 'get_goat' method in C++ returns a reference, so this is
// another CppRef, not a Rust reference.

// We can still use these C++ references to call Rust methods,
// so long as those methods have a "self" type of a
// C++ reference not a Rust reference.
another_goat.clone().bleat();
another_goat.sell();

// But most commonly, C++ references are simply used as the 'this'
// type when calling other C++ methods, like this.
assert_eq!(
another_goat
.describe() // returns a UniquePtr<CxxString>, there
Expand All @@ -91,4 +110,9 @@ fn main() {
.to_string_lossy(),
"This goat has 0 horns."
);

// We can even use trait objects, though it's a bit of a fiddle.
let farm_area: Pin<Box<dyn FarmArea>> = ffi::Field::new().within_box();
let farm_area = CppPin::from_pinned_box(farm_area);
farm_area.as_cpp_ref().maintain();
}
16 changes: 16 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(nightly, feature(unsize))]
#![cfg_attr(nightly, feature(dispatch_from_dyn))]

// Copyright 2020 Google LLC
//
Expand Down Expand Up @@ -575,6 +577,7 @@ pub trait PinMut<T>: AsRef<T> {
/// and implemented by any (autocxx-related) [`moveit::New`].
pub trait WithinUniquePtr {
type Inner: UniquePtrTarget + MakeCppStorage;
/// Create this item within a [`cxx::UniquePtr`].
fn within_unique_ptr(self) -> cxx::UniquePtr<Self::Inner>;
}

Expand All @@ -583,7 +586,17 @@ pub trait WithinUniquePtr {
/// and implemented by any (autocxx-related) [`moveit::New`].
pub trait WithinBox {
type Inner;
/// Create this item inside a pinned box. This is a good option if you
/// want to own this object within Rust, and want to create Rust references
/// to it.
fn within_box(self) -> Pin<Box<Self::Inner>>;
/// Create this item inside a [`CppPin`]. This is a good option if you
/// want to own this option within Rust, but you want to create [`CppRef`]
/// C++ references to it. You'd only want to choose that option if you have
/// enabled the C++ reference wrapper support by using the
/// `safety!(unsafe_references_wrapped`) directive. If you haven't done
/// that, ignore this function.
fn within_cpp_pin(self) -> CppPin<Self::Inner>;
}

use cxx::kind::Trivial;
Expand All @@ -610,6 +623,9 @@ where
fn within_box(self) -> Pin<Box<T>> {
Box::emplace(self)
}
fn within_cpp_pin(self) -> CppPin<Self::Inner> {
CppPin::from_pinned_box(Box::emplace(self))
}
}

/// Emulates the [`WithinUniquePtr`] trait, but for trivial (plain old data) types.
Expand Down
102 changes: 77 additions & 25 deletions src/reference_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

use core::{marker::PhantomData, ops::Deref, pin::Pin};

#[cfg(nightly)]
use std::{marker::Unsize, ops::DispatchFromDyn};

use cxx::{memory::UniquePtrTarget, UniquePtr};

/// A C++ const reference. These are different from Rust's `&T` in that
Expand All @@ -18,6 +21,10 @@ use cxx::{memory::UniquePtrTarget, UniquePtr};
/// UB here cannot manifest within Rust, but only across in C++, and therefore
/// they are equivalently safe to using C++ references in pure-C++ codebases.
///
/// *Important*: you might be wondering why you've never encountered this type.
/// These exist in autocxx-generated bindings only if the `unsafe_references_wrapped`
/// safety policy is given. This may become the default in future.
///
/// # Usage
///
/// These types of references are pretty useless in Rust. You can't do
Expand Down Expand Up @@ -79,6 +86,29 @@ use cxx::{memory::UniquePtrTarget, UniquePtr};
/// unfortunate because it means `Option<CppRef<T>>`
/// occupies more space than `CppRef<T>`.
///
/// # Dynamic dispatch
///
/// You might wonder if you can do this:
/// ```ignore
/// let CppRef<dyn Trait> = ...; // obtain some CppRef<concrete type>
/// ```
/// Dynamic dispatch works so long as you're using nightly (we require another
/// unstable feature, `dispatch_from_dyn`). But we need somewhere to store
/// the trait object, and `CppRef` isn't it -- a `CppRef` can only store a
/// simple pointer to something else. So, you need to store the trait object
/// in a `Box` or similar:
/// ```ignore
/// trait SomeTrait {
/// fn some_method(self: CppRef<Self>)
/// }
/// impl SomeTrait for ffi::Concrete {
/// fn some_method(self: CppRef<Self>) {}
/// }
/// let obj: Pin<Box<dyn SomeTrait>> = ffi::Concrete::new().within_box();
/// let obj = CppPin::from_pinned_box(obj);
/// farm_area.as_cpp_ref().some_method();
/// ```
///
/// # Implementation notes
///
/// Internally, this is represented as a raw pointer in Rust. See the note above
Expand All @@ -89,7 +119,7 @@ pub struct CppRef<'a, T: ?Sized> {
phantom: PhantomData<&'a T>,
}

impl<'a, T> CppRef<'a, T> {
impl<'a, T: ?Sized> CppRef<'a, T> {
/// Retrieve the underlying C++ pointer.
pub fn as_ptr(&self) -> *const T {
self.ptr
Expand Down Expand Up @@ -162,14 +192,14 @@ impl<'a, T> CppRef<'a, T> {
}
}

impl<'a, T> Deref for CppRef<'a, T> {
impl<'a, T: ?Sized> Deref for CppRef<'a, T> {
type Target = *const T;
#[inline]
fn deref(&self) -> &Self::Target {
// With `inline_const` we can simplify this to:
// const { panic!("you shouldn't deref CppRef!") }
struct C<T>(T);
impl<T> C<T> {
struct C<T: ?Sized>(T);
impl<T: ?Sized> C<T> {
const V: core::convert::Infallible = panic!(
"You cannot directly obtain a Rust reference from a CppRef. Use CppRef::as_ref."
);
Expand All @@ -178,7 +208,7 @@ impl<'a, T> Deref for CppRef<'a, T> {
}
}

impl<'a, T> Clone for CppRef<'a, T> {
impl<'a, T: ?Sized> Clone for CppRef<'a, T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
Expand All @@ -187,6 +217,9 @@ impl<'a, T> Clone for CppRef<'a, T> {
}
}

#[cfg(nightly)]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppRef<'_, U>> for CppRef<'_, T> {}

/// A C++ non-const reference. These are different from Rust's `&mut T` in that
/// several C++ references can exist to the same underlying data ("aliasing")
/// and that's not permitted for regular Rust references.
Expand All @@ -200,7 +233,7 @@ pub struct CppMutRef<'a, T: ?Sized> {
phantom: PhantomData<&'a T>,
}

impl<'a, T> CppMutRef<'a, T> {
impl<'a, T: ?Sized> CppMutRef<'a, T> {
/// Retrieve the underlying C++ pointer.
pub fn as_mut_ptr(&self) -> *mut T {
self.ptr
Expand Down Expand Up @@ -236,21 +269,21 @@ impl<'a, T> CppMutRef<'a, T> {
}
}

impl<'a, T> Deref for CppMutRef<'a, T> {
impl<'a, T: ?Sized> Deref for CppMutRef<'a, T> {
type Target = *const T;
#[inline]
fn deref(&self) -> &Self::Target {
// With `inline_const` we can simplify this to:
// const { panic!("you shouldn't deref CppRef!") }
struct C<T>(T);
impl<T> C<T> {
struct C<T: ?Sized>(T);
impl<T: ?Sized> C<T> {
const V: core::convert::Infallible = panic!("You cannot directly obtain a Rust reference from a CppMutRef. Use CppMutRef::as_mut.");
}
match C::<T>::V {}
}
}

impl<'a, T> Clone for CppMutRef<'a, T> {
impl<'a, T: ?Sized> Clone for CppMutRef<'a, T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
Expand All @@ -268,19 +301,22 @@ impl<'a, T> From<CppMutRef<'a, T>> for CppRef<'a, T> {
}
}

#[cfg(nightly)]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppMutRef<'_, U>> for CppMutRef<'_, T> {}

/// Any type which can return a C++ reference to its contents.
pub trait AsCppRef<T> {
pub trait AsCppRef<T: ?Sized> {
/// Returns a reference which obeys C++ reference semantics
fn as_cpp_ref(&self) -> CppRef<T>;
}

/// Any type which can return a C++ reference to its contents.
pub trait AsCppMutRef<T>: AsCppRef<T> {
pub trait AsCppMutRef<T: ?Sized>: AsCppRef<T> {
/// Returns a mutable reference which obeys C++ reference semantics
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T>;
}

impl<'a, T> AsCppRef<T> for CppMutRef<'a, T> {
impl<'a, T: ?Sized> AsCppRef<T> for CppMutRef<'a, T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.ptr)
}
Expand All @@ -289,9 +325,9 @@ impl<'a, T> AsCppRef<T> for CppMutRef<'a, T> {
/// Workaround for the inability to use std::ptr::addr_of! on the contents
/// of a box.
#[repr(transparent)]
struct CppPinContents<T>(T);
struct CppPinContents<T: ?Sized>(T);

impl<T> CppPinContents<T> {
impl<T: ?Sized> CppPinContents<T> {
fn addr_of(&self) -> *const T {
std::ptr::addr_of!(self.0)
}
Expand Down Expand Up @@ -348,14 +384,17 @@ impl<T> CppPinContents<T> {
///
/// See also [`CppUniquePtrPin`], which is equivalent for data which is in
/// a [`cxx::UniquePtr`].
pub struct CppPin<T>(Box<CppPinContents<T>>);
pub struct CppPin<T: ?Sized>(Box<CppPinContents<T>>);

impl<T> CppPin<T> {
impl<T: ?Sized> CppPin<T> {
/// Imprison the Rust data within a `CppPin`. This eliminates any remaining
/// Rust references (since we take the item by value) and this object
/// subsequently only vends C++ style references, not Rust references,
/// until or unless `extract` is called.
pub fn new(item: T) -> Self {
pub fn new(item: T) -> Self
where
T: Sized,
{
Self(Box::new(CppPinContents(item)))
}

Expand All @@ -377,6 +416,19 @@ impl<T> CppPin<T> {
Self(contents)
}

// Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
/// Rust references (since we take the item by value) and this object
/// subsequently only vends C++ style references, not Rust references,
/// until or unless `extract` is called.
///
/// If the item is already in a `Box`, this is slightly more efficient than
/// `new` because it will avoid moving/reallocating it.
pub fn from_pinned_box(item: Pin<Box<T>>) -> Self {
// Safety: it's OK to un-pin the Box because we'll be putting it
// into a CppPin which upholds the same pinned-ness contract.
Self::from_box(unsafe { Pin::into_inner_unchecked(item) })
}

/// Get an immutable pointer to the underlying object.
pub fn as_ptr(&self) -> *const T {
self.0.addr_of()
Expand Down Expand Up @@ -429,13 +481,13 @@ impl<T> CppPin<T> {
}
}

impl<T> AsCppRef<T> for CppPin<T> {
impl<T: ?Sized> AsCppRef<T> for CppPin<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.as_ptr())
}
}

impl<T> AsCppMutRef<T> for CppPin<T> {
impl<T: ?Sized> AsCppMutRef<T> for CppPin<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
CppMutRef::from_ptr(self.as_mut_ptr())
}
Expand Down Expand Up @@ -488,9 +540,9 @@ impl<T: UniquePtrTarget> AsCppMutRef<T> for CppUniquePtrPin<T> {
/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
/// See [`CppRef::lifetime_cast`].
#[repr(transparent)]
pub struct PhantomReferent<T>(*const T);
pub struct PhantomReferent<T: ?Sized>(*const T);

impl<T> AsCppRef<T> for PhantomReferent<T> {
impl<T: ?Sized> AsCppRef<T> for PhantomReferent<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.0)
}
Expand All @@ -500,15 +552,15 @@ impl<T> AsCppRef<T> for PhantomReferent<T> {
/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
/// See [`CppRef::lifetime_cast`].
#[repr(transparent)]
pub struct PhantomReferentMut<T>(*mut T);
pub struct PhantomReferentMut<T: ?Sized>(*mut T);

impl<T> AsCppRef<T> for PhantomReferentMut<T> {
impl<T: ?Sized> AsCppRef<T> for PhantomReferentMut<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.0)
}
}

impl<T> AsCppMutRef<T> for PhantomReferentMut<T> {
impl<T: ?Sized> AsCppMutRef<T> for PhantomReferentMut<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
CppMutRef::from_ptr(self.0)
}
Expand Down

0 comments on commit 5492cb2

Please sign in to comment.