This repository has been archived by the owner on Aug 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This allows cxx::UniquePtr::emplace of any New made using moveit norms. UniquePtr is different from the other smart pointers supported by moveit, in that UniquePtr is _intrinsically_ pinned. You can't get an unpinned mutable reference to its contents; therefore there is no need to use Pin<UniquePtr>. This commit therefore splits the Emplace trait into Emplace and EmplaceUnpinned, with the goal that users can naturally call {Box, Arc, Rc, UniquePtr}::emplace in a similar fashion, so long as both traits are in scope. UniquePtr<T> emplacement support requires the T to implement a new trait, MakeCppStorage, which can provide uninitialized space for the T in some heap cell which can later be deleted using the std::unique_ptr default destructor. It's not expected that most users will implement this trait, but that it will instead be generated automatically by code generators atop moveit and cxx (for instance, autocxx). Testing these cxx bindings requires inclusion of C++ code, and we don't want to link against C++ code for the production rlib - therefore a separate module is created within the workspace to host these tests.
- Loading branch information
Showing
11 changed files
with
386 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Copyright 2022 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
[package] | ||
name = "moveit-cxx-tests" | ||
version = "0.0.0" | ||
authors = ["Miguel Young de la Sota <mcyoung@google.com>"] | ||
edition = "2018" | ||
repository = "https://github.com/google/moveit" | ||
publish = false | ||
|
||
[dependencies] | ||
cxx = "1.0" | ||
|
||
[dependencies.moveit] | ||
path = ".." | ||
features = ["cxx"] | ||
|
||
[build-dependencies] | ||
cxx-build = "1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
fn main() { | ||
cxx_build::bridge("src/tests.rs") | ||
.flag_if_supported("-std=c++14") | ||
.include("src") | ||
.compile("moveit-cxx-tests"); | ||
|
||
println!("cargo:rerun-if-changed=src/tests.rs"); | ||
println!("cargo:rerun-if-changed=src/cxx_support_test_cpp.h"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Build in test mode only to test cxx integration. | ||
|
||
#ifndef CXX_SUPPORT_TEST_CPP | ||
#define CXX_SUPPORT_TEST_CPP | ||
|
||
#include <cstdint> | ||
#include <cstring> | ||
#include <memory> | ||
|
||
constexpr uint8_t kUninitialized = 0; | ||
constexpr uint8_t kInitialized = 1; | ||
constexpr uint8_t kMethodCalled = 2; | ||
|
||
class Foo { | ||
public: | ||
Foo() { data[0] = kInitialized; } | ||
uint8_t get_status() const { return data[0]; } | ||
void modify() { data[0] = kMethodCalled; } | ||
|
||
private: | ||
uint32_t data[4]; // exactly match layout declared in Rust. | ||
}; | ||
|
||
inline Foo* CreateUninitializedFoo() { | ||
std::allocator<Foo> alloc; | ||
Foo* data = alloc.allocate(1); | ||
std::memcpy(data, &kUninitialized, 1); | ||
return data; | ||
} | ||
|
||
inline void FreeUninitializedFoo(Foo* foo) { | ||
std::allocator<Foo> alloc; | ||
alloc.deallocate(foo, 1); | ||
} | ||
|
||
inline void foo_constructor(Foo& foo) { new (&foo) Foo(); } | ||
|
||
#endif // CXX_SUPPORT_TEST_CPP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
//! Tests for `cxx` support in `moveit`. This is a separate crate because | ||
//! these tests require linking C++ code in a `build.rs`, which we do not | ||
//! want to apply to normal users of `moveit`. | ||
#[cfg(test)] | ||
mod tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use cxx::UniquePtr; | ||
use moveit::moveit; | ||
use moveit::Emplace; | ||
use moveit::EmplaceUnpinned; | ||
|
||
// Shared with C++ | ||
const UNINITIALIZED: u8 = 0; | ||
const INITIALIZED: u8 = 1; | ||
const METHOD_CALLED: u8 = 2; | ||
|
||
#[cxx::bridge] | ||
mod ffi { | ||
unsafe extern "C++" { | ||
include!("cxx_support_test_cpp.h"); | ||
type Foo = super::bindgenish::Foo; | ||
fn CreateUninitializedFoo() -> *mut Foo; | ||
unsafe fn FreeUninitializedFoo(ptr: *mut Foo); | ||
|
||
fn foo_constructor(_this: Pin<&mut Foo>); | ||
|
||
fn get_status(self: &Foo) -> u8; | ||
fn modify(self: Pin<&mut Foo>); | ||
} | ||
// Ensures that cxx creates bindings for UniquePtr<Foo> | ||
// even though that isn't used in any of the above APIs. | ||
impl UniquePtr<Foo> {} | ||
} | ||
|
||
mod bindgenish { | ||
use std::marker::PhantomData; | ||
use std::marker::PhantomPinned; | ||
|
||
use cxx::kind::Opaque; | ||
use cxx::type_id; | ||
use cxx::ExternType; | ||
|
||
use moveit::MakeCppStorage; | ||
use moveit::New; | ||
|
||
#[repr(C)] | ||
pub struct Foo { | ||
// opaque | ||
_pin: PhantomData<PhantomPinned>, | ||
_data: [u32; 4], | ||
} | ||
|
||
unsafe impl ExternType for Foo { | ||
type Id = type_id!("Foo"); | ||
type Kind = Opaque; | ||
} | ||
|
||
unsafe impl MakeCppStorage for Foo { | ||
unsafe fn allocate_uninitialized_cpp_storage() -> *mut Self { | ||
let foo = super::ffi::CreateUninitializedFoo(); | ||
assert_eq!(foo.as_ref().unwrap().get_status(), super::UNINITIALIZED); | ||
foo | ||
} | ||
|
||
unsafe fn free_uninitialized_cpp_storage(ptr: *mut Self) { | ||
super::ffi::FreeUninitializedFoo(ptr); | ||
} | ||
} | ||
|
||
impl Foo { | ||
pub fn new() -> impl New<Output = Self> { | ||
unsafe { | ||
moveit::new::by_raw(|space| { | ||
// TODO can we get rid of the transmute? | ||
let space = std::mem::transmute(space); | ||
super::ffi::foo_constructor(space) | ||
}) | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_stack_emplacement() { | ||
moveit! { | ||
let mut foo = bindgenish::Foo::new(); | ||
} | ||
assert_eq!(foo.get_status(), INITIALIZED); | ||
foo.as_mut().modify(); | ||
assert_eq!(foo.get_status(), METHOD_CALLED); | ||
} | ||
|
||
#[test] | ||
fn test_box_emplacement() { | ||
let mut foo = Box::emplace(bindgenish::Foo::new()); | ||
assert_eq!(foo.get_status(), INITIALIZED); | ||
foo.as_mut().modify(); | ||
assert_eq!(foo.get_status(), METHOD_CALLED); | ||
} | ||
|
||
#[test] | ||
fn test_unique_ptr_emplacement() { | ||
let mut foo = UniquePtr::emplace(bindgenish::Foo::new()); | ||
assert_eq!(foo.get_status(), INITIALIZED); | ||
foo.pin_mut().modify(); | ||
assert_eq!(foo.get_status(), METHOD_CALLED); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2022 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
//! Support for `cxx` types. | ||
use std::mem::MaybeUninit; | ||
use std::pin::Pin; | ||
|
||
use cxx::memory::UniquePtrTarget; | ||
use cxx::UniquePtr; | ||
|
||
use crate::EmplaceUnpinned; | ||
use crate::TryNew; | ||
|
||
/// A type which has the ability to create heap storage space | ||
/// for itself in C++, without initializing that storage. | ||
/// | ||
/// # Safety | ||
/// | ||
/// Implementers must ensure that the pointer returned by | ||
/// `allocate_uninitialized_cpp_storage` is a valid, non-null, | ||
/// pointer to a new but uninitialized storage block, and that | ||
/// such blocks must be freeable using either of these routes: | ||
/// | ||
/// * before they're initialized, using `free_uninitialized_cpp_storage` | ||
/// * after they're initialized, via a delete expression like `delete p;` | ||
pub unsafe trait MakeCppStorage: Sized { | ||
/// Allocates heap space for this type in C++ and return a pointer | ||
/// to that space, but do not initialize that space (i.e. do not | ||
/// yet call a constructor). | ||
/// | ||
/// # Safety | ||
/// | ||
/// To avoid memory leaks, callers must ensure that this space is | ||
/// freed using `free_uninitialized_cpp_storage`, or is converted into | ||
/// a [`UniquePtr`] such that it can later be freed by | ||
/// `std::unique_ptr<T, std::default_delete<T>>`. | ||
unsafe fn allocate_uninitialized_cpp_storage() -> *mut Self; | ||
|
||
/// Frees a C++ allocation which has not yet | ||
/// had a constructor called. | ||
/// | ||
/// # Safety | ||
/// | ||
/// Callers guarantee that the pointer here was allocated by | ||
/// `allocate_uninitialized_cpp_storage` and has not been | ||
/// initialized. | ||
unsafe fn free_uninitialized_cpp_storage(ptr: *mut Self); | ||
} | ||
|
||
impl<T: MakeCppStorage + UniquePtrTarget> EmplaceUnpinned<T> for UniquePtr<T> { | ||
fn try_emplace<N: TryNew<Output = T>>(n: N) -> Result<Self, N::Error> { | ||
unsafe { | ||
let uninit_ptr = T::allocate_uninitialized_cpp_storage(); | ||
let uninit = | ||
Pin::new_unchecked(&mut *(uninit_ptr as *mut MaybeUninit<T>)); | ||
// FIXME - this is not panic safe. | ||
let result = n.try_new(uninit); | ||
if let Err(err) = result { | ||
T::free_uninitialized_cpp_storage(uninit_ptr); | ||
return Err(err); | ||
} | ||
Ok(UniquePtr::from_raw(uninit_ptr)) | ||
} | ||
} | ||
} |
Oops, something went wrong.