Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

To/FromGodot has now specific impls for native structs; trait docs #457

Merged
merged 4 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions godot-codegen/src/class_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -880,15 +880,52 @@ fn make_native_structure(

let imports = util::make_imports();
let fields = make_native_structure_fields(&structure.format, ctx);
let doc = format!("[`ToGodot`] and [`FromGodot`] are implemented for `*mut {class_name}` and `*const {class_name}`.");

// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
let tokens = quote! {
#imports
use crate::builtin::meta::{GodotConvert, FromGodot, ToGodot};

/// Native structure; can be passed via pointer in APIs that are not exposed to GDScript.
///
#[doc = #doc]
#[repr(C)]
pub struct #class_name {
#fields
}

impl GodotConvert for *mut #class_name {
type Via = i64;
}

impl ToGodot for *mut #class_name {
fn to_godot(&self) -> Self::Via {
*self as i64
}
}

impl FromGodot for *mut #class_name {
fn try_from_godot(via: Self::Via) -> Option<Self> {
Some(via as Self)
}
}

impl GodotConvert for *const #class_name {
type Via = i64;
}

impl ToGodot for *const #class_name {
fn to_godot(&self) -> Self::Via {
*self as i64
}
}

impl FromGodot for *const #class_name {
fn try_from_godot(via: Self::Via) -> Option<Self> {
Some(via as Self)
}
}
};
// note: TypePtr -> ObjectPtr conversion OK?

Expand Down
4 changes: 4 additions & 0 deletions godot-codegen/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ fn test_pascal_conversion() {
("JSONRPC", "JsonRpc"),
("NetworkedMultiplayerENet", "NetworkedMultiplayerENet"),
("ObjectID", "ObjectId"),
("OpenXRAPIExtension", "OpenXrApiExtension"),
("OpenXRIPBinding", "OpenXrIpBinding"),
("PackedFloat32Array", "PackedFloat32Array"),
("PCKPacker", "PckPacker"),
("PHashTranslation", "PHashTranslation"),
Expand Down Expand Up @@ -77,6 +79,8 @@ fn test_snake_conversion() {
("JSONRPC", "json_rpc"),
("NetworkedMultiplayerENet", "networked_multiplayer_e_net"),
("ObjectID", "object_id"),
("OpenXRAPIExtension", "open_xr_api_extension"),
("OpenXRIPBinding", "open_xr_ip_binding"),
("PackedFloat32Array", "packed_float32_array"),
("PCKPacker", "pck_packer"),
("PHashTranslation", "p_hash_translation"),
Expand Down
10 changes: 6 additions & 4 deletions godot-codegen/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,10 @@ pub fn to_snake_case(class_name: &str) -> String {
use heck::ToSnakeCase;

// Special cases
#[allow(clippy::single_match)]
match class_name {
"JSONRPC" => return "json_rpc".to_string(),
"OpenXRAPIExtension" => return "open_xr_api_extension".to_string(),
"OpenXRIPBinding" => return "open_xr_ip_binding".to_string(),
_ => {}
}

Expand All @@ -448,10 +449,11 @@ pub fn to_snake_case(class_name: &str) -> String {
pub fn to_pascal_case(class_name: &str) -> String {
use heck::ToPascalCase;

// Special cases
#[allow(clippy::single_match)]
// Special cases: reuse snake_case impl to ensure at least consistency between those 2.
match class_name {
"JSONRPC" => return "JsonRpc".to_string(),
"JSONRPC" | "OpenXRAPIExtension" | "OpenXRIPBinding" => {
return to_snake_case(class_name).to_pascal_case()
}
_ => {}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
use crate::builtin::meta::{impl_godot_as_self, FromGodot, GodotConvert, GodotType, ToGodot};
use godot_ffi as sys;

// The following ToGodot/FromGodot/Convert impls are auto-generated for each engine type, co-located with their definitions:
// - enum
// - const/mut pointer to native struct

// ----------------------------------------------------------------------------------------------------------------------------------------------
// Option

Expand Down Expand Up @@ -89,41 +93,6 @@ impl FromGodot for sys::VariantOperator {
}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
// Pointers

impl<T> GodotConvert for *mut T {
type Via = i64;
}

impl<T> ToGodot for *mut T {
fn to_godot(&self) -> Self::Via {
*self as i64
}
}

impl<T> FromGodot for *mut T {
fn try_from_godot(via: Self::Via) -> Option<Self> {
Some(via as Self)
}
}

impl<T> GodotConvert for *const T {
type Via = i64;
}

impl<T> ToGodot for *const T {
fn to_godot(&self) -> Self::Via {
*self as i64
}
}

impl<T> FromGodot for *const T {
fn try_from_godot(via: Self::Via) -> Option<Self> {
Some(via as Self)
}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
// Scalars

Expand Down Expand Up @@ -239,7 +208,6 @@ impl_godot_scalar!(
u8 as i64,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8
);

impl_godot_scalar!(
u64 as i64,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64;
Expand All @@ -250,3 +218,25 @@ impl_godot_scalar!(
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT;
lossy
);

// ----------------------------------------------------------------------------------------------------------------------------------------------
// Raw pointers

// const void* is used in some APIs like OpenXrApiExtension::transform_from_pose().
// Other impls for raw pointers are generated for native structures.

impl GodotConvert for *const std::ffi::c_void {
type Via = i64;
}

impl ToGodot for *const std::ffi::c_void {
fn to_godot(&self) -> Self::Via {
*self as i64
}
}

impl FromGodot for *const std::ffi::c_void {
fn try_from_godot(via: Self::Via) -> Option<Self> {
Some(via as Self)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ use crate::builtin::{Variant, VariantConversionError};

use super::{GodotFfiVariant, GodotType};

/// Indicates that a type has some canonical Godot type that can represent it.
/// Indicates that a type can be passed to/from Godot, either directly or through an intermediate "via" type.
///
/// The type specified here is what will be used to pass this type across to ffi-boundary to/from Godot.
/// Generally [`ToGodot`] needs to be implemented to pass a type to Godot, and [`FromGodot`] to receive this
/// type from Godot.
/// The associated type `Via` specifies _how_ this type is passed across the FFI boundary to/from Godot.
/// Generally [`ToGodot`] needs to be implemented to pass a type to Godot, and [`FromGodot`] to receive this type from Godot.
///
/// [`GodotType`] is a stronger bound than [`GodotConvert`], since it expresses that a type is _directly_ representable
/// in Godot (without intermediate "via"). Every `GodotType` also implements `GodotConvert` with `Via = Self`.
pub trait GodotConvert {
/// The type used for ffi-passing.
/// The type through which `Self` is represented in Godot.
type Via: GodotType;
}

Expand All @@ -33,8 +35,7 @@ pub trait ToGodot: Sized + GodotConvert {

/// Converts this type to the Godot type.
///
/// This can in some cases enable some optimizations, such as avoiding reference counting for
/// reference-counted values.
/// This can in some cases enable minor optimizations, such as avoiding reference counting operations.
fn into_godot(self) -> Self::Via {
self.to_godot()
}
Expand Down
41 changes: 27 additions & 14 deletions godot-core/src/builtin/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
pub mod registration;

mod class_name;
mod godot_compatible;
mod godot_convert;
mod return_marshal;
mod signature;

pub use class_name::*;
pub use godot_compatible::*;
pub use godot_convert::*;
#[doc(hidden)]
pub use return_marshal::*;
#[doc(hidden)]
Expand All @@ -25,7 +25,8 @@ use crate::builtin::*;
use crate::engine::global;
use registration::method::MethodParamOrReturnInfo;

/// Conversion of GodotFfi-types into/from [`Variant`].
/// Conversion of [`GodotFfi`] types to/from [`Variant`].
#[doc(hidden)]
pub trait GodotFfiVariant: Sized + GodotFfi {
fn ffi_to_variant(&self) -> Variant;
fn ffi_from_variant(variant: &Variant) -> Result<Self, VariantConversionError>;
Expand Down Expand Up @@ -98,38 +99,48 @@ mod sealed {
}
}

/// Types that can represent some Godot type.
///
/// This trait cannot be implemented for custom user types, for that you should see [`GodotConvert`]
/// instead. A type implements `GodotType` when it can directly represent some primitive type exposed by
/// Godot. For instance, [`i64`] implements `GodotType`, since it can be directly represented by Godot's
/// `int` type. But [`VariantType`] does not implement `GodotType`. Since while it is an enum Godot uses, we
/// have no native way to indicate to Godot that a value should be one of the variants of `VariantType`.
/// Type that is directly representable in the engine.
///
/// Unlike [`GodotFfi`], types implementing this trait don't need to fully represent its corresponding Godot
/// type. For instance [`i32`] does not implement [`GodotFfi`] because it cannot represent all values of
/// Godot's `int` type, however it does implement `GodotType` because we can set the metadata of values with
/// this type to indicate that they are 32 bits large.
/// This trait cannot be implemented for custom user types; for those, [`GodotConvert`] exists instead.
/// A type implements `GodotType` when Godot has a direct, native representation for it. For instance:
/// - [`i64`] implements `GodotType`, since it can be directly represented by Godot's `int` type.
/// - But [`VariantType`] does not implement `GodotType`. While it is an enum Godot uses, we have no native way to indicate
/// to Godot that a value should be one of the variants of `VariantType`.
//
// Unlike `GodotFfi`, types implementing this trait don't need to fully represent its corresponding Godot
// type. For instance [`i32`] does not implement `GodotFfi` because it cannot represent all values of
// Godot's `int` type, however it does implement `GodotType` because we can set the metadata of values with
// this type to indicate that they are 32 bits large.
pub trait GodotType: GodotConvert<Via = Self> + ToGodot + FromGodot + sealed::Sealed {
#[doc(hidden)]
type Ffi: GodotFfiVariant;

#[doc(hidden)]
fn to_ffi(&self) -> Self::Ffi;

#[doc(hidden)]
fn into_ffi(self) -> Self::Ffi;

#[doc(hidden)]
fn try_from_ffi(ffi: Self::Ffi) -> Option<Self>;

#[doc(hidden)]
fn from_ffi(ffi: Self::Ffi) -> Self {
Self::try_from_ffi(ffi).unwrap()
}

#[doc(hidden)]
fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
Self::Ffi::default_param_metadata()
}

#[doc(hidden)]
fn class_name() -> ClassName {
// If we use `ClassName::of::<()>()` then this type shows up as `(no base)` in documentation.
ClassName::none()
}

#[doc(hidden)]
fn property_info(property_name: &str) -> PropertyInfo {
PropertyInfo {
variant_type: Self::Ffi::variant_type(),
Expand All @@ -141,10 +152,12 @@ pub trait GodotType: GodotConvert<Via = Self> + ToGodot + FromGodot + sealed::Se
}
}

#[doc(hidden)]
fn argument_info(property_name: &str) -> MethodParamOrReturnInfo {
MethodParamOrReturnInfo::new(Self::property_info(property_name), Self::param_metadata())
}

#[doc(hidden)]
fn return_info() -> Option<MethodParamOrReturnInfo> {
Some(MethodParamOrReturnInfo::new(
Self::property_info(""),
Expand Down
3 changes: 3 additions & 0 deletions godot-core/src/builtin/meta/return_marshal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub trait PtrcallReturn {

// ----------------------------------------------------------------------------------------------------------------------------------------------

#[doc(hidden)]
pub struct PtrcallReturnOptionGdT<R> {
_marker: std::marker::PhantomData<R>,
}
Expand All @@ -33,6 +34,7 @@ impl<T: GodotClass> PtrcallReturn for PtrcallReturnOptionGdT<Gd<T>> {

// ----------------------------------------------------------------------------------------------------------------------------------------------

#[doc(hidden)]
pub struct PtrcallReturnT<R> {
_marker: std::marker::PhantomData<R>,
}
Expand All @@ -53,6 +55,7 @@ impl<T: FromGodot> PtrcallReturn for PtrcallReturnT<T> {

// ----------------------------------------------------------------------------------------------------------------------------------------------

#[doc(hidden)]
pub enum PtrcallReturnUnit {}

impl PtrcallReturn for PtrcallReturnUnit {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use venial::Declaration;
use crate::util::{decl_get_info, DeclInfo};
use crate::ParseResult;

pub fn derive_godot_compatible(decl: Declaration) -> ParseResult<TokenStream> {
pub fn derive_godot_convert(decl: Declaration) -> ParseResult<TokenStream> {
let DeclInfo {
where_,
generic_params,
Expand Down
4 changes: 2 additions & 2 deletions godot-macros/src/derive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

mod derive_export;
mod derive_from_variant;
mod derive_godot_compatible;
mod derive_godot_convert;
mod derive_property;
mod derive_to_variant;

pub(crate) use derive_export::*;
pub(crate) use derive_from_variant::*;
pub(crate) use derive_godot_compatible::*;
pub(crate) use derive_godot_convert::*;
pub(crate) use derive_property::*;
pub(crate) use derive_to_variant::*;
4 changes: 2 additions & 2 deletions godot-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,8 @@ pub fn godot_api(_meta: TokenStream, input: TokenStream) -> TokenStream {
}

#[proc_macro_derive(GodotConvert)]
pub fn derive_godot_compatible(input: TokenStream) -> TokenStream {
translate(input, derive::derive_godot_compatible)
pub fn derive_godot_convert(input: TokenStream) -> TokenStream {
translate(input, derive::derive_godot_convert)
}

/// Derive macro for [ToGodot](../builtin/meta/trait.ToGodot.html) on structs or enums.
Expand Down