Skip to content

Commit

Permalink
Merge pull request #486 from godot-rust/qol/gd-constructors
Browse files Browse the repository at this point in the history
Rework `Gd` constructors, add `default()` to engine classes
  • Loading branch information
Bromeon authored Nov 19, 2023
2 parents 3605f0f + 8281437 commit 8ec6a22
Show file tree
Hide file tree
Showing 19 changed files with 309 additions and 158 deletions.
37 changes: 27 additions & 10 deletions godot-codegen/src/class_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,44 +441,58 @@ fn make_module_doc(class_name: &TyName) -> String {
)
}

fn make_constructor(class: &Class, ctx: &Context) -> TokenStream {
fn make_constructor_and_default(
class: &Class,
class_name: &TyName,
ctx: &Context,
) -> (TokenStream, TokenStream) {
let godot_class_name = &class.name;
let godot_class_stringname = make_string_name(godot_class_name);
// Note: this could use class_name() but is not yet done due to upcoming lazy-load refactoring.
//let class_name_obj = quote! { <Self as crate::obj::GodotClass>::class_name() };

let (constructor, godot_default_impl);
if ctx.is_singleton(godot_class_name) {
// Note: we cannot return &'static mut Self, as this would be very easy to mutably alias.
// &'static Self would be possible, but we would lose the whole mutability information (even if that is best-effort and
// not strict Rust mutability, it makes the API much more usable).
// As long as the user has multiple Gd smart pointers to the same singletons, only the internal raw pointers are aliased.
// See also Deref/DerefMut impl for Gd.
quote! {
constructor = quote! {
pub fn singleton() -> Gd<Self> {
unsafe {
let __class_name = #godot_class_stringname;
let __object_ptr = sys::interface_fn!(global_get_singleton)(__class_name.string_sys());
Gd::from_obj_sys(__object_ptr)
}
}
}
};
godot_default_impl = TokenStream::new();
} else if !class.is_instantiable {
// Abstract base classes or non-singleton classes without constructor
TokenStream::new()
constructor = TokenStream::new();
godot_default_impl = TokenStream::new();
} else if class.is_refcounted {
// RefCounted, Resource, etc
quote! {
constructor = quote! {
pub fn new() -> Gd<Self> {
unsafe {
let class_name = #godot_class_stringname;
let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys());
Gd::from_obj_sys(object_ptr)
}
}
}
};
godot_default_impl = quote! {
impl crate::obj::cap::GodotDefault for #class_name {
fn __godot_default() -> crate::obj::Gd<Self> {
Self::new()
}
}
};
} else {
// Manually managed classes: Object, Node etc
quote! {
constructor = quote! {
#[must_use]
pub fn new_alloc() -> Gd<Self> {
unsafe {
Expand All @@ -487,8 +501,11 @@ fn make_constructor(class: &Class, ctx: &Context) -> TokenStream {
Gd::from_obj_sys(object_ptr)
}
}
}
};
godot_default_impl = TokenStream::new();
}

(constructor, godot_default_impl)
}

fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> GeneratedClass {
Expand All @@ -506,7 +523,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
None => (quote! { () }, None),
};

let constructor = make_constructor(class, ctx);
let (constructor, godot_default_impl) = make_constructor_and_default(class, class_name, ctx);
let api_level = util::get_api_level(class);
let init_level = api_level.to_init_level();

Expand Down Expand Up @@ -640,7 +657,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
)*

#exportable_impl

#godot_default_impl
#deref_impl

#[macro_export]
Expand Down
16 changes: 8 additions & 8 deletions godot-core/src/builtin/string/gstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,6 @@ impl GString {
.expect("Godot hashes are uint32_t")
}

/// Move `self` into a system pointer. This transfers ownership and thus does not call the destructor.
///
/// # Safety
/// `dst` must be a pointer to a `GString` which is suitable for ffi with Godot.
pub unsafe fn move_string_ptr(self, dst: sys::GDExtensionStringPtr) {
self.move_return_ptr(dst as *mut _, sys::PtrcallType::Standard);
}

/// Gets the internal chars slice from a [`GString`].
///
/// Note: This operation is *O*(*n*). Consider using [`chars_unchecked`][Self::chars_unchecked]
Expand Down Expand Up @@ -140,6 +132,14 @@ impl GString {
fn string_sys = sys;
}

/// Move `self` into a system pointer. This transfers ownership and thus does not call the destructor.
///
/// # Safety
/// `dst` must be a pointer to a `GString` which is suitable for ffi with Godot.
pub(crate) unsafe fn move_string_ptr(self, dst: sys::GDExtensionStringPtr) {
self.move_return_ptr(dst as *mut _, sys::PtrcallType::Standard);
}

#[doc(hidden)]
pub fn as_inner(&self) -> inner::InnerString {
inner::InnerString::from_outer(self)
Expand Down
145 changes: 106 additions & 39 deletions godot-core/src/obj/gd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::ops::{Deref, DerefMut};
use std::ptr;

use godot_ffi as sys;
use godot_ffi::VariantType;
Expand All @@ -23,28 +22,65 @@ use super::RawGd;

/// Smart pointer to objects owned by the Godot engine.
///
/// See also [chapter about objects][book] in the book.
///
/// This smart pointer can only hold _objects_ in the Godot sense: instances of Godot classes (`Node`, `RefCounted`, etc.)
/// or user-declared structs (`#[derive(GodotClass)]`). It does **not** hold built-in types (`Vector3`, `Color`, `i32`).
/// or user-declared structs (declared with `#[derive(GodotClass)]`). It does **not** hold built-in types (`Vector3`, `Color`, `i32`).
///
/// `Gd<T>` never holds null objects. If you need nullability, use `Option<Gd<T>>`.
///
/// # Memory management
///
/// This smart pointer behaves differently depending on `T`'s associated types, see [`GodotClass`] for their documentation.
/// In particular, the memory management strategy is fully dependent on `T`:
///
/// * Objects of type [`RefCounted`] or inherited from it are **reference-counted**. This means that every time a smart pointer is
/// - **Reference-counted**<br>
/// Objects of type [`RefCounted`] or inherited from it are **reference-counted**. This means that every time a smart pointer is
/// shared using [`Clone::clone()`], the reference counter is incremented, and every time one is dropped, it is decremented.
/// This ensures that the last reference (either in Rust or Godot) will deallocate the object and call `T`'s destructor.
/// This ensures that the last reference (either in Rust or Godot) will deallocate the object and call `T`'s destructor.<br><br>
///
/// * Objects inheriting from [`Object`] which are not `RefCounted` (or inherited) are **manually-managed**.
/// - **Manual**<br>
/// Objects inheriting from [`Object`] which are not `RefCounted` (or inherited) are **manually-managed**.
/// Their destructor is not automatically called (unless they are part of the scene tree). Creating a `Gd<T>` means that
/// you are responsible of explicitly deallocating such objects using [`Gd::free()`].
/// you are responsible of explicitly deallocating such objects using [`free()`][Self::free].<br><br>
///
/// * For `T=Object`, the memory strategy is determined **dynamically**. Due to polymorphism, a `Gd<T>` can point to either
/// - **Dynamic**<br>
/// For `T=Object`, the memory strategy is determined **dynamically**. Due to polymorphism, a `Gd<Object>` can point to either
/// reference-counted or manually-managed types at runtime. The behavior corresponds to one of the two previous points.
/// Note that if the dynamic type is also `Object`, the memory is manually-managed.
///
/// [`Object`]: crate::engine::Object
/// [`RefCounted`]: crate::engine::RefCounted
/// # Construction
///
/// To construct default instances of various `Gd<T>` types, there are extension methods on the type `T` itself:
///
/// | Type \ Memory Strategy | Ref-counted | Manually managed | Singleton |
/// |------------------------|----------------------|-----------------------|-------------------------|
/// | **Engine type** | `Resource::new()` | `Node::new_alloc()` | `Os::singleton()` |
/// | **User type** | `MyClass::new_gd()` | `MyClass::alloc_gd()` | _(not yet implemented)_ |
///
/// In addition, the smart pointer can be constructed in multiple ways:
///
/// * [`Gd::default()`] for reference-counted types that are constructible. For user types, this means they must expose an `init` function
/// or have a generated one. `Gd::<T>::default()` is equivalent to the shorter `T::new_gd()` and primarily useful for derives or generics.
/// * [`Gd::from_init_fn(function)`][Gd::from_init_fn] for Rust objects with `#[base]` field, which are constructed inside the smart pointer.
/// This is a very handy function if you want to pass extra parameters to your object upon construction.
/// * [`Gd::from_object(rust_obj)`][Gd::from_object] for existing Rust objects without a `#[base]` field that are moved _into_ the smart pointer.
/// * [`Gd::from_instance_id(id)`][Gd::from_instance_id] and [`Gd::try_from_instance_id(id)`][Gd::try_from_instance_id]
/// to obtain a pointer to an object which is already alive in the engine.
///
/// # Binds
///
/// The [`bind()`][Self::bind] and [`bind_mut()`][Self::bind_mut] methods allow you to obtain a shared or exclusive guard to the user instance.
/// These provide interior mutability similar to [`RefCell`][std::cell::RefCell], with the addition that `Gd` simultaneously handles reference
/// counting (for some types `T`).
///
/// When you declare a `#[func]` method on your own class and it accepts `&self` or `&mut self`, an implicit `bind()` or `bind_mut()` call
/// on the owning `Gd<T>` is performed. This is important to keep in mind, as you can get into situations that violate dynamic borrow rules; for
/// example if you are inside a `&mut self` method, make a call to GDScript and indirectly call another method on the same object (re-entrancy).
///
/// [book]: https://godot-rust.github.io/book/intro/objects.html
/// [`Object`]: engine::Object
/// [`RefCounted`]: engine::RefCounted
#[repr(C)] // must be layout compatible with engine classes
pub struct Gd<T: GodotClass> {
// Note: `opaque` has the same layout as GDExtensionObjectPtr == Object* in C++, i.e. the bytes represent a pointer
Expand All @@ -68,27 +104,6 @@ impl<T> Gd<T>
where
T: GodotClass<Declarer = dom::UserDomain>,
{
/// Moves a user-created object into this smart pointer, submitting ownership to the Godot engine.
///
/// This is only useful for types `T` which do not store their base objects (if they have a base,
/// you cannot construct them standalone).
pub fn new(user_object: T) -> Self {
Self::with_base(move |_base| user_object)
}

/// Creates a default-constructed instance of `T` inside a smart pointer.
///
/// This is equivalent to the GDScript expression `T.new()`.
pub fn new_default() -> Self
where
T: cap::GodotInit,
{
unsafe {
let object_ptr = callbacks::create::<T>(ptr::null_mut());
Gd::from_obj_sys(object_ptr)
}
}

/// Creates a `Gd<T>` using a function that constructs a `T` from a provided base.
///
/// Imagine you have a type `T`, which has a `#[base]` field that you cannot default-initialize.
Expand All @@ -106,19 +121,48 @@ where
/// other_field: i32,
/// }
///
/// let obj = Gd::<MyClass>::with_base(|my_base| {
/// let obj = Gd::from_init_fn(|my_base| {
/// // accepts the base and returns a constructed object containing it
/// MyClass { my_base, other_field: 732 }
/// });
/// ```
pub fn with_base<F>(init: F) -> Self
pub fn from_init_fn<F>(init: F) -> Self
where
F: FnOnce(crate::obj::Base<T::Base>) -> T,
{
let object_ptr = callbacks::create_custom(init);
unsafe { Gd::from_obj_sys(object_ptr) }
}

/// Moves a user-created object into this smart pointer, submitting ownership to the Godot engine.
///
/// This is only useful for types `T` which do not store their base objects (if they have a base,
/// you cannot construct them standalone).
pub fn from_object(user_object: T) -> Self {
Self::from_init_fn(move |_base| user_object)
}

#[deprecated = "Use `Gd::from_object()` instead."]
pub fn new(user_object: T) -> Self {
Self::from_object(user_object)
}

#[deprecated = "Use `Gd::default()` or the short-hands `T::new_gd()` and `T::alloc_gd()` instead."]
pub fn new_default() -> Self
where
T: cap::GodotDefault,
{
Self::default_instance()
}

#[deprecated = "Use `Gd::from_init_fn()` instead."]
pub fn with_base<F>(init: F) -> Self
where
F: FnOnce(crate::obj::Base<T::Base>) -> T,
{
Self::from_init_fn(init)
}

/// Hands out a guard for a shared borrow, through which the user instance can be read.
///
/// The pattern is very similar to interior mutability with standard [`RefCell`][std::cell::RefCell].
Expand Down Expand Up @@ -242,7 +286,7 @@ impl<T: GodotClass> Gd<T> {
/// #[class(init, base=Node2D)]
/// struct MyClass {}
///
/// let obj: Gd<MyClass> = Gd::new_default();
/// let obj: Gd<MyClass> = MyClass::alloc_gd();
/// let base = obj.clone().upcast::<Node>();
/// ```
pub fn upcast<Base>(self) -> Gd<Base>
Expand Down Expand Up @@ -295,7 +339,19 @@ impl<T: GodotClass> Gd<T> {
.map_err(Self::from_ffi)
}

#[doc(hidden)]
/// Create default instance for all types that have `GodotDefault`.
///
/// Deliberately more loose than `Gd::default()`, does not require ref-counted memory strategy for user types.
pub(crate) fn default_instance() -> Self
where
T: cap::GodotDefault,
{
unsafe {
let object_ptr = crate::callbacks::create::<T>(std::ptr::null_mut());
Gd::from_obj_sys(object_ptr)
}
}

pub(crate) unsafe fn from_obj_sys_or_none(ptr: sys::GDExtensionObjectPtr) -> Option<Self> {
Self::try_from_ffi(RawGd::from_obj_sys(ptr))
}
Expand All @@ -305,26 +361,21 @@ impl<T: GodotClass> Gd<T> {
///
/// This is the default for most initializations from FFI. In cases where reference counter
/// should explicitly **not** be updated, [`Self::from_obj_sys_weak`] is available.
#[doc(hidden)]
pub(crate) unsafe fn from_obj_sys(ptr: sys::GDExtensionObjectPtr) -> Self {
Self::from_obj_sys_or_none(ptr).unwrap()
}

#[doc(hidden)]
pub(crate) unsafe fn from_obj_sys_weak_or_none(ptr: sys::GDExtensionObjectPtr) -> Option<Self> {
Self::try_from_ffi(RawGd::from_obj_sys_weak(ptr))
}

#[doc(hidden)]
pub(crate) unsafe fn from_obj_sys_weak(ptr: sys::GDExtensionObjectPtr) -> Self {
Self::from_obj_sys_weak_or_none(ptr).unwrap()
}

#[doc(hidden)]
pub(crate) fn obj_sys(&self) -> sys::GDExtensionObjectPtr {
self.raw.obj_sys()
}

/// Returns a callable referencing a method from this object named `method_name`.
pub fn callable<S: Into<StringName>>(&self, method_name: S) -> Callable {
Callable::from_object_method(self.clone(), method_name)
Expand Down Expand Up @@ -486,6 +537,22 @@ impl<T: GodotClass> GodotType for Gd<T> {
}
}

impl<T> Default for Gd<T>
where
T: cap::GodotDefault + GodotClass<Mem = mem::StaticRefCount>,
{
/// Creates a default-constructed `T` inside a smart pointer.
///
/// This is equivalent to the GDScript expression `T.new()`, and to the shorter Rust expression `T::new_gd()`.
///
/// This trait is only implemented for reference-counted classes. Classes with manually-managed memory (e.g. `Node`) are not covered,
/// because they need explicit memory management, and deriving `Default` has a high chance of the user forgetting to call `free()` on those.
/// `T::alloc_gd()` should be used for those instead.
fn default() -> Self {
T::__godot_default()
}
}

impl<T: GodotClass> Clone for Gd<T> {
fn clone(&self) -> Self {
out!("Gd::clone");
Expand Down
Loading

0 comments on commit 8ec6a22

Please sign in to comment.