Skip to content

Commit 189bdee

Browse files
committed
NewAlloc + NewGd traits to unify construction across engine and user classes
Deprecate <EngineT>::new(), <UserT>::alloc_gd(). Remove <EngineT>::new_alloc(), <UserT>::new_gd() -- same functions in different trait.
1 parent 0ef2ad3 commit 189bdee

File tree

23 files changed

+165
-102
lines changed

23 files changed

+165
-102
lines changed

examples/dodge-the-creeps/rust/src/main_scene.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ impl Main {
119119
impl INode for Main {
120120
fn init(base: Base<Node>) -> Self {
121121
Main {
122-
mob_scene: PackedScene::new(),
122+
mob_scene: PackedScene::new_gd(),
123123
score: 0,
124124
base,
125125
music: None,

godot-codegen/src/class_generator.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ fn make_constructor_and_default(
452452
// Note: this could use class_name() but is not yet done due to upcoming lazy-load refactoring.
453453
//let class_name_obj = quote! { <Self as crate::obj::GodotClass>::class_name() };
454454

455-
let (constructor, godot_default_impl);
455+
let (constructor, has_godot_default_impl);
456456
if ctx.is_singleton(godot_class_name) {
457457
// Note: we cannot return &'static mut Self, as this would be very easy to mutably alias.
458458
// &'static Self would be possible, but we would lose the whole mutability information (even if that is best-effort and
@@ -468,43 +468,38 @@ fn make_constructor_and_default(
468468
}
469469
}
470470
};
471-
godot_default_impl = TokenStream::new();
471+
has_godot_default_impl = false;
472472
} else if !class.is_instantiable {
473473
// Abstract base classes or non-singleton classes without constructor
474474
constructor = TokenStream::new();
475-
godot_default_impl = TokenStream::new();
475+
has_godot_default_impl = false;
476476
} else if class.is_refcounted {
477477
// RefCounted, Resource, etc
478478
constructor = quote! {
479+
#[deprecated = "Replaced with `new_gd` in extension trait `NewGd`."]
479480
pub fn new() -> Gd<Self> {
480-
unsafe {
481-
let class_name = #godot_class_stringname;
482-
let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys());
483-
Gd::from_obj_sys(object_ptr)
484-
}
481+
// <Self as crate::obj::NewGd>::new_gd()
482+
crate::obj::Gd::default()
485483
}
486484
};
487-
godot_default_impl = quote! {
485+
has_godot_default_impl = true;
486+
} else {
487+
// Manually managed classes: Object, Node etc
488+
constructor = quote! {};
489+
has_godot_default_impl = true;
490+
}
491+
492+
let godot_default_impl = if has_godot_default_impl {
493+
quote! {
488494
impl crate::obj::cap::GodotDefault for #class_name {
489495
fn __godot_default() -> crate::obj::Gd<Self> {
490-
Self::new()
496+
crate::engine::construct_engine_object::<Self>()
491497
}
492498
}
493-
};
499+
}
494500
} else {
495-
// Manually managed classes: Object, Node etc
496-
constructor = quote! {
497-
#[must_use]
498-
pub fn new_alloc() -> Gd<Self> {
499-
unsafe {
500-
let class_name = #godot_class_stringname;
501-
let object_ptr = sys::interface_fn!(classdb_construct_object)(class_name.string_sys());
502-
Gd::from_obj_sys(object_ptr)
503-
}
504-
}
505-
};
506-
godot_default_impl = TokenStream::new();
507-
}
501+
TokenStream::new()
502+
};
508503

509504
(constructor, godot_default_impl)
510505
}

godot-core/src/engine/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,16 @@ pub(crate) fn object_ptr_from_id(instance_id: InstanceId) -> sys::GDExtensionObj
161161
unsafe { sys::interface_fn!(object_get_instance_from_id)(instance_id.to_u64()) }
162162
}
163163

164-
// ----------------------------------------------------------------------------------------------------------------------------------------------
165-
// Implementation of this file
164+
pub(crate) fn construct_engine_object<T>() -> Gd<T>
165+
where
166+
T: GodotClass<Declarer = EngineDomain>,
167+
{
168+
// SAFETY: adhere to Godot API; valid class name and returned pointer is an object.
169+
unsafe {
170+
let object_ptr = sys::interface_fn!(classdb_construct_object)(T::class_name().string_sys());
171+
Gd::from_obj_sys(object_ptr)
172+
}
173+
}
166174

167175
pub(crate) fn ensure_object_alive(
168176
instance_id: InstanceId,
@@ -203,6 +211,9 @@ pub(crate) fn ensure_object_inherits(
203211
)
204212
}
205213

214+
// ----------------------------------------------------------------------------------------------------------------------------------------------
215+
// Implementation of this file
216+
206217
/// Checks if `derived` inherits from `base`, using a cache for _successful_ queries.
207218
#[cfg(debug_assertions)]
208219
fn is_derived_base_cached(derived: ClassName, base: ClassName) -> bool {

godot-core/src/obj/gd.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,9 @@ use super::RawGd;
5656
///
5757
/// To construct default instances of various `Gd<T>` types, there are extension methods on the type `T` itself:
5858
///
59-
/// | Type \ Memory Strategy | Ref-counted | Manually managed | Singleton |
60-
/// |------------------------|----------------------|-----------------------|-------------------------|
61-
/// | **Engine type** | `Resource::new()` | `Node::new_alloc()` | `Os::singleton()` |
62-
/// | **User type** | `MyClass::new_gd()` | `MyClass::alloc_gd()` | _(not yet implemented)_ |
59+
/// - Manually managed: [`NewAlloc::new_alloc()`][crate::obj::NewAlloc::new_alloc]
60+
/// - Reference-counted: [`NewGd::new_gd()`][crate::obj::NewGd::new_gd]
61+
/// - Singletons: `T::singleton()` (inherent)
6362
///
6463
/// In addition, the smart pointer can be constructed in multiple ways:
6564
///
@@ -150,7 +149,7 @@ where
150149
Self::from_object(user_object)
151150
}
152151

153-
#[deprecated = "Use `Gd::default()` or the short-hands `T::new_gd()` and `T::alloc_gd()` instead."]
152+
#[deprecated = "Use `Gd::default()` or the short-hands `T::new_gd()` and `T::new_alloc()` instead."]
154153
pub fn new_default() -> Self
155154
where
156155
T: cap::GodotDefault,
@@ -351,7 +350,7 @@ impl<T: GodotClass> Gd<T> {
351350
T: cap::GodotDefault,
352351
{
353352
unsafe {
354-
let object_ptr = crate::callbacks::create::<T>(std::ptr::null_mut());
353+
let object_ptr = callbacks::create::<T>(std::ptr::null_mut());
355354
Gd::from_obj_sys(object_ptr)
356355
}
357356
}

godot-core/src/obj/traits.rs

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,12 @@ impl<T: GodotClass> Inherits<T> for T {}
131131
pub trait ExportableObject: GodotClass {}
132132

133133
/// Implemented for all user-defined classes, providing extensions on the raw object to interact with `Gd`.
134+
// #[deprecated = "Use `NewGd` and `NewAlloc` traits instead."]
134135
pub trait UserClass: GodotClass<Declarer = dom::UserDomain> {
135136
/// Return a new Gd which contains a default-constructed instance.
136137
///
137138
/// `MyClass::new_gd()` is equivalent to `Gd::<MyClass>::default()`.
138-
fn new_gd() -> Gd<Self>
139-
where
140-
Self: cap::GodotDefault + GodotClass<Mem = mem::StaticRefCount>,
141-
{
142-
Gd::default()
143-
}
144-
145-
/// Return a new Gd which contains a default-constructed instance.
146-
///
147-
/// `MyClass::new_gd()` is equivalent to `Gd::<MyClass>::default()`.
139+
#[deprecated = "Use `NewAlloc::new_alloc()` instead."]
148140
#[must_use]
149141
fn alloc_gd<U>() -> Gd<Self>
150142
where
@@ -389,6 +381,45 @@ pub trait WithBaseField: GodotClass<Declarer = dom::UserDomain> {
389381
}
390382
}
391383

384+
/// Extension trait for all reference-counted classes.
385+
pub trait NewGd: GodotClass {
386+
/// Return a new Gd which contains a default-constructed instance.
387+
///
388+
/// `MyClass::new_gd()` is equivalent to `Gd::<MyClass>::default()`.
389+
fn new_gd() -> Gd<Self>;
390+
}
391+
392+
impl<T> NewGd for T
393+
where
394+
T: cap::GodotDefault + GodotClass<Mem = mem::StaticRefCount>,
395+
{
396+
fn new_gd() -> Gd<Self> {
397+
Gd::default()
398+
}
399+
}
400+
401+
/// Extension trait for all manually managed classes.
402+
pub trait NewAlloc: GodotClass {
403+
/// Return a new Gd which contains a default-constructed instance.
404+
///
405+
/// The result must be manually managed, e.g. by attaching it to the scene tree or calling `free()` after usage.
406+
/// Failure to do so will result in memory leaks.
407+
#[must_use]
408+
fn new_alloc() -> Gd<Self>;
409+
}
410+
411+
impl<T, U> NewAlloc for T
412+
where
413+
T: cap::GodotDefault + GodotClass<Mem = U>,
414+
U: mem::PossiblyManual,
415+
{
416+
fn new_alloc() -> Gd<Self> {
417+
use crate::obj::dom::Domain as _;
418+
419+
<Self as GodotClass>::Declarer::create_gd()
420+
}
421+
}
422+
392423
// ----------------------------------------------------------------------------------------------------------------------------------------------
393424

394425
/// Capability traits, providing dedicated functionalities for Godot classes
@@ -425,7 +456,7 @@ pub mod cap {
425456
debug_assert_eq!(
426457
std::any::TypeId::of::<<Self as GodotClass>::Declarer>(),
427458
std::any::TypeId::of::<dom::UserDomain>(),
428-
"__godot_default() called on engine class; must be overridden for user classes"
459+
"__godot_default() called on engine class; must be overridden for engine classes"
429460
);
430461

431462
Gd::default_instance()
@@ -490,10 +521,10 @@ mod private {
490521

491522
pub mod dom {
492523
use super::private::Sealed;
493-
use crate::{
494-
obj::{GodotClass, RawGd},
495-
storage::Storage,
496-
};
524+
use crate::obj::cap::GodotDefault;
525+
use crate::obj::{Gd, GodotClass, RawGd};
526+
use crate::{callbacks, sys};
527+
use crate::storage::Storage;
497528

498529
/// Trait that specifies who declares a given `GodotClass`.
499530
pub trait Domain: Sealed {
@@ -513,6 +544,11 @@ pub mod dom {
513544
unsafe fn is_currently_bound<T>(obj: &RawGd<T>) -> bool
514545
where
515546
T: GodotClass<Declarer = Self>;
547+
548+
#[doc(hidden)]
549+
fn create_gd<T>() -> Gd<T>
550+
where
551+
T: GodotDefault + GodotClass<Declarer = Self>;
516552
}
517553

518554
/// Expresses that a class is declared by the Godot engine.
@@ -538,6 +574,17 @@ pub mod dom {
538574
{
539575
false
540576
}
577+
578+
fn create_gd<T>() -> Gd<T>
579+
where
580+
T: GodotDefault + GodotClass<Declarer = Self>,
581+
{
582+
unsafe {
583+
let object_ptr =
584+
sys::interface_fn!(classdb_construct_object)(T::class_name().string_sys());
585+
Gd::from_obj_sys(object_ptr)
586+
}
587+
}
541588
}
542589

543590
/// Expresses that a class is declared by the user.
@@ -561,6 +608,16 @@ pub mod dom {
561608
{
562609
obj.storage().unwrap_unchecked().is_bound()
563610
}
611+
612+
fn create_gd<T>() -> Gd<T>
613+
where
614+
T: GodotDefault + GodotClass<Declarer = Self>,
615+
{
616+
unsafe {
617+
let object_ptr = callbacks::create::<T>(std::ptr::null_mut());
618+
Gd::from_obj_sys(object_ptr)
619+
}
620+
}
564621
}
565622
}
566623

godot/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,14 @@ pub mod prelude {
235235
};
236236
pub use super::init::{gdextension, ExtensionLibrary, InitLevel};
237237
pub use super::log::*;
238-
pub use super::obj::{
239-
Base, Gd, GdMut, GdRef, GodotClass, Inherits, InstanceId, OnReady,
240-
};
238+
pub use super::obj::{Base, Gd, GdMut, GdRef, GodotClass, Inherits, InstanceId, OnReady};
241239

242240
// Make trait methods available
243241
pub use super::engine::NodeExt as _;
244242
pub use super::obj::EngineBitfield as _;
245243
pub use super::obj::EngineEnum as _;
246-
pub use super::obj::UserClass as _; // new_gd(), alloc_gd()
244+
pub use super::obj::NewAlloc as _;
245+
pub use super::obj::NewGd as _;
246+
pub use super::obj::UserClass as _; // new_gd(), alloc_gd() -- TODO: remove (exposed functions are deprecated)
247247
pub use super::obj::WithBaseField as _; // to_gd()
248248
}

itest/rust/src/benchmarks/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use godot::bind::GodotClass;
1313
use godot::builtin::inner::InnerRect2i;
1414
use godot::builtin::{GString, Rect2i, StringName, Vector2i};
1515
use godot::engine::{Node3D, Os, RefCounted};
16-
use godot::obj::{Gd, InstanceId};
16+
use godot::obj::{Gd, InstanceId, NewAlloc, NewGd};
1717

1818
use crate::framework::bench;
1919

@@ -57,7 +57,7 @@ fn class_node_life() -> InstanceId {
5757

5858
#[bench(repeat = 25)]
5959
fn class_refcounted_life() -> Gd<RefCounted> {
60-
RefCounted::new()
60+
RefCounted::new_gd()
6161
}
6262

6363
#[bench(repeat = 25)]

itest/rust/src/builtin_tests/containers/array_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ fn array_mixed_values() {
323323
let typed_array = array![1, 2];
324324
let object = Object::new_alloc();
325325
let node = Node::new_alloc();
326-
let ref_counted = RefCounted::new();
326+
let ref_counted = RefCounted::new_gd();
327327

328328
let array = varray![
329329
int,
@@ -398,7 +398,7 @@ fn typed_array_pass_to_godot_func() {
398398
use godot::engine::image::Format;
399399
use godot::engine::{Image, Texture2DArray};
400400

401-
let mut image = Image::new();
401+
let mut image = Image::new_gd();
402402
image.set_data(
403403
2,
404404
4,
@@ -407,7 +407,7 @@ fn typed_array_pass_to_godot_func() {
407407
PackedByteArray::from(&[255, 0, 255, 0, 0, 255, 0, 255]),
408408
);
409409
let images = array![image];
410-
let mut texture = Texture2DArray::new();
410+
let mut texture = Texture2DArray::new_gd();
411411
let error = texture.create_from_images(images);
412412

413413
assert_eq!(error, Error::OK);

itest/rust/src/builtin_tests/containers/callable_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use godot::builtin::inner::InnerCallable;
1010
use godot::builtin::meta::ToGodot;
1111
use godot::builtin::{varray, Callable, GString, StringName, Variant};
1212
use godot::engine::{Node2D, Object};
13-
use godot::obj::UserClass;
13+
use godot::obj::{NewAlloc, NewGd};
1414

1515
use crate::framework::itest;
1616

itest/rust/src/builtin_tests/containers/signal_test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use godot::bind::{godot_api, GodotClass};
1111
use godot::builtin::{GString, Variant};
1212

1313
use godot::engine::Object;
14-
use godot::obj::{Base, Gd, UserClass, WithBaseField};
14+
use godot::obj::{Base, Gd, NewAlloc, WithBaseField};
1515
use godot::sys;
1616

1717
use crate::framework::itest;
@@ -63,8 +63,8 @@ const SIGNAL_ARG_STRING: &str = "Signal string arg";
6363
#[itest]
6464
/// Test that godot can call a method that is connect with a signal
6565
fn signals() {
66-
let mut emitter = Emitter::alloc_gd();
67-
let receiver = Receiver::alloc_gd();
66+
let mut emitter = Emitter::new_alloc();
67+
let receiver = Receiver::new_alloc();
6868

6969
let args = [
7070
vec![],

itest/rust/src/builtin_tests/containers/variant_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use godot::builtin::meta::{FromGodot, ToGodot};
1212
use godot::builtin::{dict, varray, GString, NodePath, StringName, Variant, Vector2, Vector3};
1313
use godot::builtin::{Basis, Dictionary, VariantArray, VariantOperator, VariantType};
1414
use godot::engine::Node2D;
15-
use godot::obj::InstanceId;
15+
use godot::obj::{InstanceId, NewAlloc};
1616
use godot::sys::GodotFfi;
1717

1818
use crate::common::roundtrip;

itest/rust/src/builtin_tests/convert_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use godot::builtin::{
1010
dict, Array, Dictionary, GString, Variant, VariantArray, Vector2, Vector2Axis,
1111
};
1212
use godot::engine::{Node, Resource};
13-
use godot::obj::Gd;
13+
use godot::obj::{Gd, NewAlloc};
1414

1515
use crate::framework::itest;
1616

0 commit comments

Comments
 (0)