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

Commit 36e0256

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 36e0256

File tree

6 files changed

+416
-1
lines changed

6 files changed

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

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() {
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_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::SharedType;
259262
pub use super::types::{
260263
ClassStruct, InstanceStruct, IsImplementable, IsSubclassable, ObjectSubclass,
261264
ObjectSubclassExt, ObjectSubclassType,

0 commit comments

Comments
 (0)