Skip to content
This repository was archived by the owner on Mar 4, 2024. It is now read-only.

Commit efbba43

Browse files
author
Matteo Biggio
committed
glib: glib-macros: add SharedType and Shared derive macro
Added a SharedType trait that allows implementing reference-counted types in terms os std::sync::Arc. In particular, the ffi methods that increment reference counting can return always the same pointer value. Also, added a `Shared` derive macro, similar to the existing `GBoxed` one, that simplifies the creation of Shared objects.
1 parent 3b154eb commit efbba43

File tree

6 files changed

+509
-1
lines changed

6 files changed

+509
-1
lines changed

glib-macros/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod gerror_domain_derive;
88
mod gflags_attribute;
99
mod object_interface_attribute;
1010
mod object_subclass_attribute;
11+
mod shared_derive;
1112
mod utils;
1213

1314
use proc_macro::TokenStream;
@@ -300,6 +301,34 @@ pub fn gboxed_derive(input: TokenStream) -> TokenStream {
300301
gen.into()
301302
}
302303

304+
/// Derive macro for defining a [`SharedType`]`::get_type` function and
305+
/// the [`glib::Value`] traits.
306+
///
307+
/// # Example
308+
///
309+
/// ```
310+
/// use glib::prelude::*;
311+
/// use glib::subclass::prelude::*;
312+
///
313+
/// #[derive(Clone, Debug, PartialEq, Eq)]
314+
/// struct MySharedInner {
315+
/// foo: String,
316+
/// }
317+
/// #[derive(Clone, Debug, PartialEq, Eq, glib::Shared)]
318+
/// #[shared(type_name = "MyShared")]
319+
/// struct MyShared(std::sync::Arc::<MySharedInner>);
320+
/// ```
321+
///
322+
/// [`SharedType`]: subclass/shared/trait.SharedType.html
323+
/// [`glib::Value`]: value/struct.Value.html
324+
#[proc_macro_derive(Shared, attributes(shared))]
325+
#[proc_macro_error]
326+
pub fn shared_derive(input: TokenStream) -> TokenStream {
327+
let input = parse_macro_input!(input as DeriveInput);
328+
let gen = shared_derive::impl_shared(&input);
329+
gen.into()
330+
}
331+
303332
/// Attribute macro for defining flags using the `bitflags` crate.
304333
/// This macro will also define a `GFlags::get_type` function and
305334
/// the [`glib::Value`] traits.

glib-macros/src/shared_derive.rs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use crate::utils::{crate_ident_new, find_attribute_meta, find_nested_meta, parse_type_name};
4+
use proc_macro2::{Ident, TokenStream};
5+
use proc_macro_error::abort_call_site;
6+
use quote::quote;
7+
8+
fn gen_impl_set_value_optional(
9+
name: &Ident,
10+
refcounted_type: &syn::TypePath,
11+
crate_ident: &Ident,
12+
) -> TokenStream {
13+
quote! {
14+
impl #crate_ident::value::SetValueOptional for #name {
15+
unsafe fn set_value_optional(value: &mut #crate_ident::value::Value, this: Option<&Self>) {
16+
let ptr: *mut <#refcounted_type as #crate_ident::subclass::shared::RefCounted>::InnerType = match this {
17+
Some(this) => #refcounted_type::into_ptr(this.0.clone()) as *mut _,
18+
None => std::ptr::null_mut(),
19+
};
20+
21+
#crate_ident::gobject_ffi::g_value_take_boxed(
22+
#crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
23+
ptr as *mut _,
24+
);
25+
}
26+
}
27+
}
28+
}
29+
30+
fn gen_impl_from_value(
31+
name: &Ident,
32+
refcounted_type: &syn::TypePath,
33+
crate_ident: &Ident,
34+
) -> TokenStream {
35+
quote! {
36+
impl<'a> #crate_ident::value::FromValue<'a> for #name {
37+
unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self {
38+
let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(
39+
#crate_ident::translate::ToGlibPtr::to_glib_none(value).0,
40+
);
41+
assert!(!ptr.is_null());
42+
#name(#refcounted_type::from_ptr(ptr as *const <#refcounted_type as #crate_ident::subclass::shared::RefCounted>::InnerType))
43+
}
44+
}
45+
}
46+
}
47+
48+
fn gen_ptr_to_option(
49+
name: &Ident,
50+
refcounted_type: &syn::TypePath,
51+
nullable: bool,
52+
crate_ident: &Ident,
53+
) -> TokenStream {
54+
if nullable {
55+
quote! {
56+
if ptr.is_null() {
57+
None
58+
} else {
59+
Some(#name(#refcounted_type::from_ptr(ptr as *const <#refcounted_type as #crate_ident::subclass::shared::RefCounted>::InnerType)))
60+
}
61+
}
62+
} else {
63+
quote! {
64+
assert!(!ptr.is_null());
65+
Some(#name(#refcounted_type::from_ptr(ptr as *const <#refcounted_type as #crate_ident::subclass::shared::RefCounted>::InnerType)))
66+
}
67+
}
68+
}
69+
70+
fn refcounted_type(input: &syn::DeriveInput) -> Option<&syn::TypePath> {
71+
let fields = match &input.data {
72+
syn::Data::Struct(s) => &s.fields,
73+
_ => return None,
74+
};
75+
76+
let unnamed = match fields {
77+
syn::Fields::Unnamed(u) if u.unnamed.len() == 1 => &u.unnamed[0],
78+
_ => return None,
79+
};
80+
81+
let refcounted = match &unnamed.ty {
82+
syn::Type::Path(p) => p,
83+
_ => return None,
84+
};
85+
86+
Some(refcounted)
87+
}
88+
89+
pub fn impl_shared(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
90+
let refcounted_type = match refcounted_type(input) {
91+
Some(p) => p,
92+
_ => abort_call_site!("derive(Shared) requires struct MyStruct(Arc<MyInnerStruct>)"),
93+
};
94+
95+
let name = &input.ident;
96+
let gtype_name = match parse_type_name(&input, "shared") {
97+
Ok(v) => v,
98+
Err(e) => abort_call_site!(
99+
"{}: derive(Shared) requires #[shared(type_name = \"SharedTypeName\")]",
100+
e
101+
),
102+
};
103+
104+
let meta = find_attribute_meta(&input.attrs, "shared")
105+
.unwrap()
106+
.unwrap();
107+
let nullable = find_nested_meta(&meta, "nullable").is_some();
108+
let crate_ident = crate_ident_new();
109+
let ptr_to_option = gen_ptr_to_option(name, refcounted_type, nullable, &crate_ident);
110+
111+
let impl_from_value = if !nullable {
112+
gen_impl_from_value(name, refcounted_type, &crate_ident)
113+
} else {
114+
quote! {}
115+
};
116+
117+
let impl_set_value_optional = if nullable {
118+
gen_impl_set_value_optional(name, refcounted_type, &crate_ident)
119+
} else {
120+
quote! {}
121+
};
122+
123+
quote! {
124+
impl #crate_ident::subclass::shared::SharedType for #name {
125+
const NAME: &'static str = #gtype_name;
126+
127+
type RefCountedType = #refcounted_type;
128+
129+
fn get_type() -> glib::Type {
130+
static mut TYPE_: glib::Type = glib::Type::INVALID;
131+
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
132+
133+
ONCE.call_once(|| {
134+
let type_ = #crate_ident::subclass::shared::register_shared_type::<Self>();
135+
unsafe {
136+
TYPE_ = type_;
137+
}
138+
});
139+
140+
unsafe { TYPE_ }
141+
}
142+
143+
fn from_refcounted(this: Self::RefCountedType) -> Self {
144+
Self(this)
145+
}
146+
147+
fn into_refcounted(self) -> Self::RefCountedType {
148+
self.0
149+
}
150+
}
151+
152+
impl #crate_ident::StaticType for #name {
153+
fn static_type() -> #crate_ident::Type {
154+
<#name as #crate_ident::subclass::shared::SharedType>::get_type()
155+
}
156+
}
157+
158+
impl #crate_ident::value::SetValue for #name {
159+
unsafe fn set_value(value: &mut #crate_ident::value::Value, this: &Self) {
160+
let ptr: *mut <#refcounted_type as #crate_ident::subclass::shared::RefCounted>::InnerType = #refcounted_type::into_ptr(this.0.clone()) as *mut _;
161+
#crate_ident::gobject_ffi::g_value_take_boxed(
162+
#crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
163+
ptr as *mut _,
164+
);
165+
}
166+
}
167+
168+
#impl_set_value_optional
169+
170+
impl<'a> #crate_ident::value::FromValueOptional<'a> for #name {
171+
unsafe fn from_value_optional(value: &'a #crate_ident::value::Value) -> Option<Self> {
172+
let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(
173+
#crate_ident::translate::ToGlibPtr::to_glib_none(value).0,
174+
);
175+
#ptr_to_option
176+
}
177+
}
178+
179+
#impl_from_value
180+
}
181+
}

glib-macros/tests/test.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use glib::prelude::*;
66
use glib::subclass::prelude::*;
77
use glib::translate::{FromGlib, ToGlib};
8-
use glib::{gflags, GBoxed, GEnum, GErrorDomain};
8+
use glib::{gflags, GBoxed, GEnum, GErrorDomain, Shared};
99

1010
#[test]
1111
fn derive_gerror_domain() {
@@ -22,6 +22,72 @@ fn derive_gerror_domain() {
2222
assert!(matches!(err.kind::<TestError>(), Some(TestError::Bad)));
2323
}
2424

25+
#[test]
26+
fn derive_shared_arc() {
27+
#[derive(Debug, Eq, PartialEq, Clone)]
28+
struct MyInnerShared {
29+
foo: String,
30+
}
31+
#[derive(Debug, Eq, PartialEq, Clone, Shared)]
32+
#[shared(type_name = "MySharedType")]
33+
struct MyShared(std::sync::Arc<MyInnerShared>);
34+
35+
assert_eq!(MyShared::get_type().name(), "MySharedType");
36+
37+
let p = MyShared(std::sync::Arc::new(MyInnerShared {
38+
foo: String::from("bar"),
39+
}));
40+
41+
assert_eq!(std::sync::Arc::strong_count(&p.0), 1);
42+
let v = p.to_value();
43+
assert_eq!(std::sync::Arc::strong_count(&p.0), 2);
44+
let p_clone = v.get::<MyShared>().unwrap().unwrap();
45+
assert_eq!(std::sync::Arc::strong_count(&p.0), 3);
46+
drop(p_clone);
47+
assert_eq!(std::sync::Arc::strong_count(&p.0), 2);
48+
drop(v);
49+
assert_eq!(std::sync::Arc::strong_count(&p.0), 1);
50+
}
51+
52+
#[test]
53+
fn derive_shared_arc_nullable() {
54+
#[derive(Debug, Eq, PartialEq, Clone)]
55+
struct MyInnerNullableShared {
56+
foo: String,
57+
}
58+
#[derive(Clone, Debug, PartialEq, Eq, Shared)]
59+
#[shared(type_name = "MyNullableSharedType", nullable)]
60+
struct MyNullableShared(std::sync::Arc<MyInnerNullableShared>);
61+
62+
assert_eq!(MyNullableShared::get_type().name(), "MyNullableSharedType");
63+
64+
let p = MyNullableShared(std::sync::Arc::new(MyInnerNullableShared {
65+
foo: String::from("bar"),
66+
}));
67+
68+
assert_eq!(std::sync::Arc::strong_count(&p.0), 1);
69+
let _v = p.to_value();
70+
assert_eq!(std::sync::Arc::strong_count(&p.0), 2);
71+
72+
let p = Some(MyNullableShared(std::sync::Arc::new(
73+
MyInnerNullableShared {
74+
foo: String::from("foo"),
75+
},
76+
)));
77+
78+
assert_eq!(std::sync::Arc::strong_count(&p.as_ref().unwrap().0), 1);
79+
let v = p.to_value();
80+
assert_eq!(std::sync::Arc::strong_count(&p.as_ref().unwrap().0), 2);
81+
assert_eq!(
82+
p.as_ref().unwrap().0.foo,
83+
v.get::<MyNullableShared>().unwrap().unwrap().0.foo
84+
);
85+
86+
let b: Option<MyNullableShared> = None;
87+
let v = b.to_value();
88+
assert_eq!(None, v.get::<MyNullableShared>().unwrap());
89+
}
90+
2591
#[test]
2692
fn derive_genum() {
2793
#[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]

glib/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ pub use once_cell;
8888

8989
pub use glib_macros::{
9090
clone, gflags, object_interface, object_subclass, Downgrade, GBoxed, GEnum, GErrorDomain,
91+
Shared,
9192
};
9293

9394
pub use self::array::Array;

glib/src/subclass/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,16 @@ pub mod object;
249249
#[macro_use]
250250
pub mod boxed;
251251

252+
pub mod shared;
253+
252254
pub mod signal;
253255

254256
pub mod prelude {
255257
//! Prelude that re-exports all important traits from this crate.
256258
pub use super::boxed::BoxedType;
257259
pub use super::interface::{ObjectInterface, ObjectInterfaceExt, ObjectInterfaceType};
258260
pub use super::object::{ObjectClassSubclassExt, ObjectImpl, ObjectImplExt};
261+
pub use super::shared::{RefCounted, SharedType};
259262
pub use super::types::{
260263
ClassStruct, InstanceStruct, IsImplementable, IsSubclassable, ObjectSubclass,
261264
ObjectSubclassExt, ObjectSubclassType,

0 commit comments

Comments
 (0)