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

Commit 1bb1375

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 7028482 commit 1bb1375

File tree

6 files changed

+507
-1
lines changed

6 files changed

+507
-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::GSharedBoxed)]
318+
/// #[gshared_boxed(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(GSharedBoxed, attributes(gshared_boxed))]
325+
#[proc_macro_error]
326+
pub fn gshared_boxed_derive(input: TokenStream) -> TokenStream {
327+
let input = parse_macro_input!(input as DeriveInput);
328+
let gen = shared_derive::impl_gshared_boxed(&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(name: &Ident, crate_ident: &Ident) -> TokenStream {
9+
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
10+
11+
quote! {
12+
impl #crate_ident::value::SetValueOptional for #name {
13+
unsafe fn set_value_optional(value: &mut #crate_ident::value::Value, this: Option<&Self>) {
14+
let ptr = match this {
15+
Some(this) => #refcounted_type_prefix::into_raw(this.0.clone()),
16+
None => std::ptr::null(),
17+
};
18+
19+
#crate_ident::gobject_ffi::g_value_take_boxed(
20+
#crate_ident::translate::ToGlibPtrMut::to_glib_none_mut(value).0,
21+
ptr as *mut _,
22+
);
23+
}
24+
}
25+
}
26+
}
27+
28+
fn gen_impl_from_value(name: &Ident, crate_ident: &Ident) -> TokenStream {
29+
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
30+
31+
quote! {
32+
impl<'a> #crate_ident::value::FromValue<'a> for #name {
33+
unsafe fn from_value(value: &'a #crate_ident::value::Value) -> Self {
34+
let ptr = #crate_ident::gobject_ffi::g_value_dup_boxed(
35+
#crate_ident::translate::ToGlibPtr::to_glib_none(value).0,
36+
);
37+
assert!(!ptr.is_null());
38+
#name(#refcounted_type_prefix::from_raw(ptr as *mut _))
39+
}
40+
}
41+
}
42+
}
43+
44+
fn gen_ptr_to_option(name: &Ident, nullable: bool, crate_ident: &Ident) -> TokenStream {
45+
let refcounted_type_prefix = refcounted_type_prefix(name, crate_ident);
46+
47+
if nullable {
48+
quote! {
49+
if ptr.is_null() {
50+
None
51+
} else {
52+
Some(#name(#refcounted_type_prefix::from_raw(ptr as *mut _)))
53+
}
54+
}
55+
} else {
56+
quote! {
57+
assert!(!ptr.is_null());
58+
Some(#name(#refcounted_type_prefix::from_raw(ptr as *mut _)))
59+
}
60+
}
61+
}
62+
63+
fn refcounted_type(input: &syn::DeriveInput) -> Option<&syn::TypePath> {
64+
let fields = match &input.data {
65+
syn::Data::Struct(s) => &s.fields,
66+
_ => return None,
67+
};
68+
69+
let unnamed = match fields {
70+
syn::Fields::Unnamed(u) if u.unnamed.len() == 1 => &u.unnamed[0],
71+
_ => return None,
72+
};
73+
74+
let refcounted = match &unnamed.ty {
75+
syn::Type::Path(p) => p,
76+
_ => return None,
77+
};
78+
79+
Some(refcounted)
80+
}
81+
82+
fn refcounted_type_prefix(name: &Ident, crate_ident: &Ident) -> proc_macro2::TokenStream {
83+
quote! {
84+
<<#name as #crate_ident::subclass::shared::SharedType>::RefCountedType as #crate_ident::subclass::shared::RefCounted>
85+
}
86+
}
87+
88+
pub fn impl_gshared_boxed(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
89+
let refcounted_type = match refcounted_type(input) {
90+
Some(p) => p,
91+
_ => abort_call_site!("derive(GSharedBoxed) requires struct MyStruct(T: RefCounted)"),
92+
};
93+
94+
let name = &input.ident;
95+
let gtype_name = match parse_type_name(&input, "gshared_boxed") {
96+
Ok(v) => v,
97+
Err(e) => abort_call_site!(
98+
"{}: derive(GSharedBoxed) requires #[gshared_boxed(type_name = \"SharedTypeName\")]",
99+
e
100+
),
101+
};
102+
103+
let meta = find_attribute_meta(&input.attrs, "gshared_boxed")
104+
.unwrap()
105+
.unwrap();
106+
let nullable = find_nested_meta(&meta, "nullable").is_some();
107+
let crate_ident = crate_ident_new();
108+
let refcounted_type_prefix = refcounted_type_prefix(name, &crate_ident);
109+
let ptr_to_option = gen_ptr_to_option(name, nullable, &crate_ident);
110+
111+
let impl_from_value = if !nullable {
112+
gen_impl_from_value(name, &crate_ident)
113+
} else {
114+
quote! {}
115+
};
116+
117+
let impl_set_value_optional = if nullable {
118+
gen_impl_set_value_optional(name, &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() -> #crate_ident::Type {
130+
static mut TYPE_: #crate_ident::Type = #crate_ident::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 = #refcounted_type_prefix::into_raw(this.0.clone());
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, GSharedBoxed};
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, GSharedBoxed)]
32+
#[gshared_boxed(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, GSharedBoxed)]
59+
#[gshared_boxed(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+
GSharedBoxed,
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)