Index: AUTHORS =================================================================== --- AUTHORS +++ AUTHORS @@ -1,4 +1,5 @@ Code: Jos van den Oever + rhn Art: Alessandro Longo Index: demo/rust/src/interface.rs =================================================================== --- demo/rust/src/interface.rs +++ demo/rust/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; Index: demo/src/Bindings.h =================================================================== --- demo/src/Bindings.h +++ demo/src/Bindings.h @@ -17,6 +17,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Fibonacci* const m_fibonacci; FibonacciList* const m_fibonacciList; @@ -58,6 +61,9 @@ friend class Demo; public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; @@ -81,6 +87,9 @@ friend class Demo; public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; @@ -124,6 +133,9 @@ friend class Demo; public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; @@ -175,6 +187,9 @@ friend class Demo; public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; @@ -227,6 +242,9 @@ friend class Demo; public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; Index: src/cpp.cpp =================================================================== --- src/cpp.cpp +++ src/cpp.cpp @@ -1,5 +1,6 @@ /* * Copyright 2017 Jos van den Oever + * Copyright 2018 rhn * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -578,16 +579,29 @@ } h << R"(public: class Private; +)"; + + h << R"( +private Q_SLOTS: +)"; + for (auto p: o.properties) { + if (p.type.type == BindingType::Object && p.write) { + h << " void destroyed" << upperInitial(p.name) << "();" << endl; + } + } + + h << R"( private: )"; for (auto p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { h << " " << p.type.name << "* const m_" << p.name << ";\n"; } } h << R"( Private * m_d; bool m_ownsPrivate; )"; + for (auto p: o.properties) { bool obj = p.type.type == BindingType::Object; auto t = p.type.name; @@ -605,10 +619,14 @@ explicit %1(QObject *parent = nullptr); ~%1(); )").arg(o.name); + for (auto p: o.properties) { if (p.type.type == BindingType::Object) { h << " const " << p.type.name << "* " << p.name << "() const;" << endl; h << " " << p.type.name << "* " << p.name << "();" << endl; + if (p.write) { + h << " void set" << upperInitial(p.name) << "(" << p.type.cppSetType << "* v);" << endl; + } } else { auto t = p.type.name; auto t2 = p.type.cppSetType; @@ -646,7 +664,7 @@ void constructorArgsDecl(QTextStream& cpp, const Object& o, const Configuration& conf) { cpp << o.name << "*"; for (auto p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { cpp << QString(", "); constructorArgsDecl(cpp, conf.findObject(p.type.name), conf); } else { @@ -692,7 +710,7 @@ void constructorArgs(QTextStream& cpp, const QString& prefix, const Object& o, const Configuration& conf) { const QString lcname(snakeCase(o.name)); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { cpp << ", " << prefix << "m_" << p.name; constructorArgs(cpp, "m_" + p.name + "->", conf.findObject(p.type.name), conf); @@ -855,8 +873,13 @@ for (const Property& p: o.properties) { const QString base = QString("%1_%2").arg(lcname, snakeCase(p.name)); if (p.type.type == BindingType::Object) { - cpp << QString(" %3::Private* %2_get(const %1::Private*);") - .arg(o.name, base, p.type.name) << endl; + if (p.write) { + cpp << QString(" %3* %2_get(const %1::Private*);") + .arg(o.name, base, p.type.name) << endl; + } else { + cpp << QString(" %3::Private* %2_get(const %1::Private*);") + .arg(o.name, base, p.type.name) << endl; + } } else if (p.type.isComplex()) { cpp << QString(" void %2_get(const %1::Private*, %3);") .arg(o.name, base, cGetType(p.type)) << endl; @@ -869,16 +892,21 @@ } if (p.write) { QString t = p.type.cSetType; - if (t == "qstring_t") { - t = "const ushort *str, int len"; - } else if (t == "qbytearray_t") { - t = "const char* bytes, int len"; - } - cpp << QString(" void %2_set(%1::Private*, %3);") - .arg(o.name, base, t) << endl; - if (p.optional) { - cpp << QString(" void %2_set_none(%1::Private*);") - .arg(o.name, base) << endl; + if (p.type.type == BindingType::Object) { + cpp << QString(" void %2_set(%1::Private*, %3::Private*);") + .arg(o.name, base, t) << endl; + } else { + if (t == "qstring_t") { + t = "const ushort *str, int len"; + } else if (t == "qbytearray_t") { + t = "const char* bytes, int len"; + } + cpp << QString(" void %2_set(%1::Private*, %3);") + .arg(o.name, base, t) << endl; + if (p.optional) { + cpp << QString(" void %2_set_none(%1::Private*);") + .arg(o.name, base) << endl; + } } } } @@ -902,7 +930,7 @@ void initializeMembersZero(QTextStream& cpp, const Object& o) { for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { cpp << QString(" m_%1(new %2(false, this)),\n") .arg(p.name, p.type.name); } @@ -913,10 +941,12 @@ { for (const Property& p: o.properties) { if (p.type.type == BindingType::Object) { - cpp << QString(" %1m_%2->m_d = %3_%4_get(%1m_d);\n") - .arg(prefix, p.name, snakeCase(o.name), snakeCase(p.name)); - initializeMembers(cpp, "m_" + p.name + "->", + if (!p.write) { + cpp << QString(" %1m_%2->m_d = %3_%4_get(%1m_d);\n") + .arg(prefix, p.name, snakeCase(o.name), snakeCase(p.name)); + initializeMembers(cpp, "m_" + p.name + "->", conf.findObject(p.type.name), conf); + } } } } @@ -940,7 +970,7 @@ cpp << QString("%1::%1(bool /*owned*/, QObject *parent):\n %2(parent),") .arg(o.name, baseType(o)) << endl; for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { cpp << QString(" m_%1(new %2(false, this)),\n") .arg(p.name, p.type.name); } @@ -986,7 +1016,11 @@ for (const Property& p: o.properties) { const QString base = QString("%1_%2").arg(lcname, snakeCase(p.name)); if (p.type.type == BindingType::Object) { - cpp << QString(R"(const %3* %1::%2() const + if (p.write) { + cpp << QString("%3* %1::%2()\n{\n").arg(o.name, p.name, p.type.name); + cpp << QString(" return %1_get(m_d);\n}\n").arg(base); + } else { + cpp << QString(R"(const %3* %1::%2() const { return m_%2; } @@ -995,6 +1029,7 @@ return m_%2; } )").arg(o.name, p.name, p.type.name); + } } else if (p.type.isComplex()) { cpp << QString("%3 %1::%2() const\n{\n").arg(o.name, p.name, p.type.name); cpp << " " << p.type.name << " v;\n"; @@ -1015,35 +1050,54 @@ cpp << QString(" return %1_get(m_d);\n}\n").arg(base); } if (p.write) { - auto t = p.type.cppSetType; - if (p.optional && !p.type.isComplex()) { - t = "const QVariant&"; - } - cpp << "void " << o.name << "::set" << upperInitial(p.name) << "(" << t << " v) {" << endl; - if (p.optional) { - if (p.type.isComplex()) { - cpp << " if (v.isNull()) {" << endl; - } else { - cpp << QString(" if (v.isNull() || !v.canConvert<%1>()) {").arg(p.type.name) << endl; + if (p.type.type == BindingType::Object) { + cpp << QString(R"( +void %1::set%2(%4 *v) { + %4::Private *data = nullptr; + %4 *existing = %3_get(m_d); + if (existing) { + disconnect(existing, &%4::destroyed, this, &%1::destroyed%2); + } + if (v) { + data = v->m_d; + connect(v, &%4::destroyed, this, &%1::destroyed%2); + } + %3_set(m_d, data); +} +void %1::destroyed%2() { + %3_set(m_d, nullptr); +)").arg(o.name, upperInitial(p.name), base, p.type.cppSetType); + } else { + auto t = p.type.cppSetType; + if (p.optional && !p.type.isComplex()) { + t = "const QVariant&"; } - cpp << QString(" %1_set_none(m_d);").arg(base) << endl; - cpp << QString(" } else {") << endl; - if (p.type.name == "QString") { + cpp << "void " << o.name << "::set" << upperInitial(p.name) << "(" << t << " v) {" << endl; + if (p.optional) { + if (p.type.isComplex()) { + cpp << " if (v.isNull()) {" << endl; + } else { + cpp << QString(" if (v.isNull() || !v.canConvert<%1>()) {").arg(p.type.name) << endl; + } + cpp << QString(" %1_set_none(m_d);").arg(base) << endl; + cpp << QString(" } else {") << endl; + if (p.type.name == "QString") { + cpp << QString(" %1_set(m_d, reinterpret_cast(v.data()), v.size());").arg(base) << endl; + } else if (p.type.name == "QByteArray") { + cpp << QString(" %1_set(m_d, v.data(), v.size());").arg(base) << endl; + } else if (p.optional) { + cpp << QString(" %1_set(m_d, v.value<%2>());").arg(base, p.type.name) << endl; + } else { + cpp << QString(" %1_set(m_d, v);").arg(base) << endl; + } + cpp << QString(" }") << endl; + } else if (p.type.name == "QString") { cpp << QString(" %1_set(m_d, reinterpret_cast(v.data()), v.size());").arg(base) << endl; } else if (p.type.name == "QByteArray") { cpp << QString(" %1_set(m_d, v.data(), v.size());").arg(base) << endl; - } else if (p.optional) { - cpp << QString(" %1_set(m_d, v.value<%2>());").arg(base, p.type.name) << endl; } else { - cpp << QString(" %1_set(m_d, v);").arg(base) << endl; + cpp << QString(" %1_set(m_d, v);").arg(base) << endl; } - cpp << QString(" }") << endl; - } else if (p.type.name == "QString") { - cpp << QString(" %1_set(m_d, reinterpret_cast(v.data()), v.size());").arg(base) << endl; - } else if (p.type.name == "QByteArray") { - cpp << QString(" %1_set(m_d, v.data(), v.size());").arg(base) << endl; - } else { - cpp << QString(" %1_set(m_d, v);").arg(base) << endl; } cpp << "}" << endl; } @@ -1169,7 +1223,7 @@ for (auto o: conf.objects) { for (auto p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { continue; } cpp << " inline void " << changedF(o, p) << "(" << o.name << "* o)\n"; Index: src/rust.cpp =================================================================== --- src/rust.cpp +++ src/rust.cpp @@ -1,5 +1,6 @@ /* * Copyright 2017 Jos van den Oever + * Copyright 2018 rhn * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -58,6 +59,16 @@ return p.type.rustType; } +QString rustQObjectType(const Object& o) +{ + switch(o.type) { + case ObjectType::Object: return "QObject"; + case ObjectType::List: return "List"; + case ObjectType::Tree: return "Tree"; + } + return ""; +} + template QString rustTypeInit(const T& p) { @@ -70,7 +81,7 @@ void rConstructorArgsDecl(QTextStream& r, const QString& name, const Object& o, const Configuration& conf) { r << QString(" %2: *mut %1QObject").arg(o.name, snakeCase(name)); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { r << QString(",\n"); rConstructorArgsDecl(r, p.name, conf.findObject(p.type.name), conf); } else { @@ -113,7 +124,7 @@ void rConstructorArgs(QTextStream& r, const QString& name, const Object& o, const Configuration& conf) { const QString lcname(snakeCase(o.name)); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { rConstructorArgs(r, p.name, conf.findObject(p.type.name), conf); } } @@ -121,7 +132,7 @@ qobject: Arc::new(Mutex::new(%2)), )").arg(o.name, snakeCase(name)); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) continue; + if (p.type.type == BindingType::Object && !p.write) continue; r << QString(" %1_changed: %2_%1_changed,\n").arg(snakeCase(p.name), snakeCase(name)); } if (o.type != ObjectType::Object) { @@ -151,7 +162,7 @@ r << QString(" };\n let d_%3 = %1::new(%3_emit%2") .arg(o.name, model, snakeCase(name)); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { r << ",\n d_" << snakeCase(p.name); } } @@ -223,7 +234,7 @@ qobject: Arc>, )").arg(o.name); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { continue; } r << QString(" %2_changed: fn(*const %1QObject),\n") @@ -246,7 +257,7 @@ } )").arg(o.name); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { continue; } r << QString(R"( pub fn %1_changed(&self) { @@ -276,8 +287,9 @@ } QString modelStruct = ""; + QString type = "QObject"; if (o.type != ObjectType::Object) { - QString type = o.type == ObjectType::List ? "List" : "Tree"; + type = o.type == ObjectType::List ? "List" : "Tree"; modelStruct = ", model: " + o.name + type; QString index; QString indexDecl; @@ -353,7 +365,7 @@ pub trait %1Trait { fn new(emit: %1Emitter%2)").arg(o.name, modelStruct); for (const Property& p: o.properties) { - if (p.type.type == BindingType::Object) { + if (p.type.type == BindingType::Object && !p.write) { r << ",\n " << snakeCase(p.name) << ": " << p.type.name; } } @@ -364,7 +376,13 @@ const QString lc(snakeCase(p.name)); if (p.type.type == BindingType::Object) { r << QString(" fn %1(&self) -> &%2;\n").arg(lc, rustType(p)); - r << QString(" fn %1_mut(&mut self) -> &mut %2;\n").arg(lc, rustType(p)); + if (p.write) { + r << QString(" fn %1_mut(&mut self) -> Option<&mut %2>;\n").arg(lc, rustType(p)); + r << QString(" fn set_%1(&mut self, value: &%2);\n").arg(lc, rustType(p)); + r << QString(" fn clear_%1(&mut self);\n").arg(lc); + } else { + r << QString(" fn %1_mut(&mut self) -> &mut %2;\n").arg(lc, rustType(p)); + } } else { if (p.rustByFunction) { r << QString(" fn %1(&self, getter: F) where F: FnOnce(%2);").arg(lc, rustReturnType(p)); @@ -460,13 +478,33 @@ const QString base = QString("%1_%2").arg(lcname, snakeCase(p.name)); QString ret = ") -> " + rustType(p); if (p.type.type == BindingType::Object) { - r << QString(R"( + if (p.write) { + r << QString(R"( +#[no_mangle] +pub unsafe extern "C" fn %2_get(ptr: *mut %1) -> *mut %4%5 { + if let Some(v) = (&mut *ptr).%3_mut() { + *v.emit().qobject.lock().unwrap().deref_mut() as *mut %4%5 + } else { + null_mut() + } +} +#[no_mangle] +pub unsafe extern "C" fn %2_set(ptr: *mut %1, v: *mut %4) { + if v.is_null() { + (&mut *ptr).clear_%3(); + } else { + (&mut *ptr).set_%3(&mut *v); + } +} +)").arg(o.name, base, snakeCase(p.name), rustType(p), rustQObjectType(conf.findObject(p.type.name))); + } else { + r << QString(R"( #[no_mangle] pub unsafe extern "C" fn %2_get(ptr: *mut %1) -> *mut %4 { (&mut *ptr).%3_mut() } )").arg(o.name, base, snakeCase(p.name), rustType(p)); - + } } else if (p.type.isComplex() && !p.optional) { if (p.rustByFunction) { r << QString(R"( @@ -952,6 +990,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use %1::*; )").arg(conf.implementationModule); @@ -1028,6 +1070,13 @@ &mut self.%1 } )").arg(lc, rustReturnType(p)); + if (p.write) { + r << QString(R"( fn set_%1(&mut self, value: *mut *%2) { + self.%1 = value; + self.emit.%1_changed(); + } +)").arg(lc, rustType(p)); + } } else { r << QString(" fn %1(&self) -> %2 {\n").arg(lc, rustReturnType(p)); if (p.type.isComplex()) { Index: tests/rust_functions/src/interface.rs =================================================================== --- tests/rust_functions/src/interface.rs +++ tests/rust_functions/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; Index: tests/rust_list/src/interface.rs =================================================================== --- tests/rust_list/src/interface.rs +++ tests/rust_list/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; Index: tests/rust_list_types/src/interface.rs =================================================================== --- tests/rust_list_types/src/interface.rs +++ tests/rust_list_types/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; Index: tests/rust_object/src/interface.rs =================================================================== --- tests/rust_object/src/interface.rs +++ tests/rust_object/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; Index: tests/rust_object_types/src/interface.rs =================================================================== --- tests/rust_object_types/src/interface.rs +++ tests/rust_object_types/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; Index: tests/rust_objects/src/implementation.rs =================================================================== --- tests/rust_objects/src/implementation.rs +++ tests/rust_objects/src/implementation.rs @@ -26,6 +26,7 @@ } } +#[derive(Clone)] // FIXME: probably not such a good idea, but if this contains anything nontrivial, it must be wrapped in an Arc anyway pub struct InnerObject { emit: InnerObjectEmitter, description: String, @@ -53,6 +54,7 @@ pub struct Person { emit: PersonEmitter, object: InnerObject, + replaceable_object: Option, } impl PersonTrait for Person { @@ -60,6 +62,7 @@ Person { emit: emit, object: object, + replaceable_object: None } } fn emit(&self) -> &PersonEmitter { @@ -71,5 +74,24 @@ fn object_mut(&mut self) -> &mut InnerObject { &mut self.object } + fn replaceable_object(&self) -> &InnerObject { + panic!("this is not used..."); + } + fn replaceable_object_mut(&mut self) -> Option<&mut InnerObject> { + if let Some(ref mut o) = self.replaceable_object { + Some(o) + } else { + None + } + } + fn set_replaceable_object(&mut self, value: &InnerObject) { + self.replaceable_object = Some(value.clone()); + self.emit.replaceable_object_changed(); + } + fn clear_replaceable_object(&mut self) { + if let Some(_) = &self.replaceable_object { + self.emit.replaceable_object_changed(); + } + self.replaceable_object = None; + } } - Index: tests/rust_objects/src/interface.rs =================================================================== --- tests/rust_objects/src/interface.rs +++ tests/rust_objects/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; @@ -68,6 +72,7 @@ person: *mut PersonQObject, object: *mut InnerObjectQObject, object_description_changed: fn(*const InnerObjectQObject), + person_replaceable_object_changed: fn(*const PersonQObject), ) -> *mut Group { let object_emit = InnerObjectEmitter { qobject: Arc::new(Mutex::new(object)), @@ -76,6 +81,7 @@ let d_object = InnerObject::new(object_emit); let person_emit = PersonEmitter { qobject: Arc::new(Mutex::new(person)), + replaceable_object_changed: person_replaceable_object_changed, }; let d_person = Person::new(person_emit, d_object); @@ -169,6 +175,7 @@ #[derive(Clone)] pub struct PersonEmitter { qobject: Arc>, + replaceable_object_changed: fn(*const PersonQObject), } unsafe impl Send for PersonEmitter {} @@ -177,6 +184,12 @@ fn clear(&self) { *self.qobject.lock().unwrap() = null(); } + pub fn replaceable_object_changed(&self) { + let ptr = *self.qobject.lock().unwrap(); + if !ptr.is_null() { + (self.replaceable_object_changed)(ptr); + } + } } pub trait PersonTrait { @@ -185,6 +198,10 @@ fn emit(&self) -> &PersonEmitter; fn object(&self) -> &InnerObject; fn object_mut(&mut self) -> &mut InnerObject; + fn replaceable_object(&self) -> &InnerObject; + fn replaceable_object_mut(&mut self) -> Option<&mut InnerObject>; + fn set_replaceable_object(&mut self, value: &InnerObject); + fn clear_replaceable_object(&mut self); } #[no_mangle] @@ -192,6 +209,7 @@ person: *mut PersonQObject, object: *mut InnerObjectQObject, object_description_changed: fn(*const InnerObjectQObject), + person_replaceable_object_changed: fn(*const PersonQObject), ) -> *mut Person { let object_emit = InnerObjectEmitter { qobject: Arc::new(Mutex::new(object)), @@ -200,6 +218,7 @@ let d_object = InnerObject::new(object_emit); let person_emit = PersonEmitter { qobject: Arc::new(Mutex::new(person)), + replaceable_object_changed: person_replaceable_object_changed, }; let d_person = Person::new(person_emit, d_object); @@ -215,3 +234,20 @@ pub unsafe extern "C" fn person_object_get(ptr: *mut Person) -> *mut InnerObject { (&mut *ptr).object_mut() } + +#[no_mangle] +pub unsafe extern "C" fn person_replaceable_object_get(ptr: *mut Person) -> *mut InnerObjectQObject { + if let Some(v) = (&mut *ptr).replaceable_object_mut() { + *v.emit().qobject.lock().unwrap().deref_mut() as *mut InnerObjectQObject + } else { + null_mut() + } +} +#[no_mangle] +pub unsafe extern "C" fn person_replaceable_object_set(ptr: *mut Person, v: *mut InnerObject) { + if v.is_null() { + (&mut *ptr).clear_replaceable_object(); + } else { + (&mut *ptr).set_replaceable_object(&mut *v); + } +} Index: tests/rust_tree/src/interface.rs =================================================================== --- tests/rust_tree/src/interface.rs +++ tests/rust_tree/src/interface.rs @@ -7,6 +7,10 @@ use std::sync::{Arc, Mutex}; use std::ptr::null; +#[allow(unused_imports)] +use std::ptr::null_mut; +#[allow(unused_imports)] +use std::ops::DerefMut; use implementation::*; Index: tests/test_functions_rust.h =================================================================== --- tests/test_functions_rust.h +++ tests/test_functions_rust.h @@ -12,6 +12,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; Index: tests/test_list_rust.h =================================================================== --- tests/test_list_rust.h +++ tests/test_list_rust.h @@ -13,6 +13,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; @@ -58,6 +61,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; Index: tests/test_list_types_rust.h =================================================================== --- tests/test_list_types_rust.h +++ tests/test_list_types_rust.h @@ -12,6 +12,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; Index: tests/test_object_rust.h =================================================================== --- tests/test_object_rust.h +++ tests/test_object_rust.h @@ -12,6 +12,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; Index: tests/test_object_types_rust.h =================================================================== --- tests/test_object_types_rust.h +++ tests/test_object_types_rust.h @@ -12,6 +12,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; Index: tests/test_objects.cpp =================================================================== --- tests/test_objects.cpp +++ tests/test_objects.cpp @@ -32,6 +32,11 @@ void testTwoLevelsConstructor(); void testTwoLevelsStringGetter(); void testTwoLevelsStringSetter(); + void testObjectGetterInitial(); + void testObjectSetter(); + void testObjectGetter(); + void testObjectSetterClear(); + void testObjectSetterDelete(); }; void TestRustObjects::testOneLevelConstructor() @@ -86,5 +91,70 @@ QCOMPARE(group.person()->object()->description(), QString("Konqi")); } +void TestRustObjects::testObjectGetterInitial() +{ + Person person; + + QCOMPARE(person.replaceableObject(), nullptr); +} + +void TestRustObjects::testObjectSetter() +{ + Person person; + InnerObject inner; + + person.setReplaceableObject(&inner); +} + +void TestRustObjects::testObjectGetter() +{ + // GIVEN + Person person; + InnerObject inner; + QSignalSpy spy(&person, &Person::replaceableObjectChanged); + + // WHEN + person.setReplaceableObject(&inner); + + // THEN + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 1); + QCOMPARE(person.replaceableObject(), &inner); +} + +void TestRustObjects::testObjectSetterClear() +{ + // GIVEN + Person person; + InnerObject inner; + QSignalSpy spy(&person, &Person::replaceableObjectChanged); + + // WHEN + person.setReplaceableObject(&inner); + person.setReplaceableObject(nullptr); + + // THEN + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 2); + QCOMPARE(person.replaceableObject(), nullptr); +} + +void TestRustObjects::testObjectSetterDelete() +{ + // GIVEN + Person person; + InnerObject* inner = new InnerObject(); + QSignalSpy spy(&person, &Person::replaceableObjectChanged); + + // WHEN + person.setReplaceableObject(inner); + delete inner; + + // THEN + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 2); + QCOMPARE(person.replaceableObject(), nullptr); +} + QTEST_MAIN(TestRustObjects) #include "test_objects.moc" Index: tests/test_objects.json =================================================================== --- tests/test_objects.json +++ tests/test_objects.json @@ -20,6 +20,10 @@ "properties": { "object": { "type": "InnerObject" + }, + "replaceableObject": { + "type": "InnerObject", + "write": true } } }, Index: tests/test_objects_rust.h =================================================================== --- tests/test_objects_rust.h +++ tests/test_objects_rust.h @@ -15,6 +15,9 @@ friend class Person; public: class Private; + +private Q_SLOTS: + private: Person* const m_person; Private * m_d; @@ -37,6 +40,9 @@ friend class Person; public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate; @@ -57,18 +63,27 @@ friend class Group; public: class Private; + +private Q_SLOTS: + void destroyedReplaceableObject(); + private: InnerObject* const m_object; Private * m_d; bool m_ownsPrivate; Q_PROPERTY(InnerObject* object READ object NOTIFY objectChanged FINAL) + Q_PROPERTY(InnerObject* replaceableObject READ replaceableObject WRITE setReplaceableObject NOTIFY replaceableObjectChanged FINAL) explicit Person(bool owned, QObject *parent); public: explicit Person(QObject *parent = nullptr); ~Person(); const InnerObject* object() const; InnerObject* object(); + const InnerObject* replaceableObject() const; + InnerObject* replaceableObject(); + void setReplaceableObject(InnerObject* v); Q_SIGNALS: void objectChanged(); + void replaceableObjectChanged(); }; #endif // TEST_OBJECTS_RUST_H Index: tests/test_objects_rust.cpp =================================================================== --- tests/test_objects_rust.cpp +++ tests/test_objects_rust.cpp @@ -11,9 +11,13 @@ { Q_EMIT o->descriptionChanged(); } + inline void personReplaceableObjectChanged(Person* o) + { + Q_EMIT o->replaceableObjectChanged(); + } } extern "C" { - Group::Private* group_new(Group*, Person*, InnerObject*, void (*)(InnerObject*)); + Group::Private* group_new(Group*, Person*, InnerObject*, void (*)(InnerObject*), void (*)(Person*)); void group_free(Group::Private*); Person::Private* group_person_get(const Group::Private*); }; @@ -26,9 +30,11 @@ }; extern "C" { - Person::Private* person_new(Person*, InnerObject*, void (*)(InnerObject*)); + Person::Private* person_new(Person*, InnerObject*, void (*)(InnerObject*), void (*)(Person*)); void person_free(Person::Private*); InnerObject::Private* person_object_get(const Person::Private*); + InnerObject* person_replaceable_object_get(const Person::Private*); + void person_replaceable_object_set(Person::Private*, InnerObject::Private*); }; Group::Group(bool /*owned*/, QObject *parent): @@ -43,7 +49,8 @@ QObject(parent), m_person(new Person(false, this)), m_d(group_new(this, m_person, m_person->m_object, - innerObjectDescriptionChanged)), + innerObjectDescriptionChanged, + personReplaceableObjectChanged)), m_ownsPrivate(true) { m_person->m_d = group_person_get(m_d); @@ -104,7 +111,8 @@ QObject(parent), m_object(new InnerObject(false, this)), m_d(person_new(this, m_object, - innerObjectDescriptionChanged)), + innerObjectDescriptionChanged, + personReplaceableObjectChanged)), m_ownsPrivate(true) { m_object->m_d = person_object_get(m_d); @@ -123,3 +131,23 @@ { return m_object; } +InnerObject* Person::replaceableObject() +{ + return person_replaceable_object_get(m_d); +} + +void Person::setReplaceableObject(InnerObject *v) { + InnerObject::Private *data = nullptr; + InnerObject *existing = person_replaceable_object_get(m_d); + if (existing) { + disconnect(existing, &InnerObject::destroyed, this, &Person::destroyedReplaceableObject); + } + if (v) { + data = v->m_d; + connect(v, &InnerObject::destroyed, this, &Person::destroyedReplaceableObject); + } + person_replaceable_object_set(m_d, data); +} +void Person::destroyedReplaceableObject() { + person_replaceable_object_set(m_d, nullptr); +} Index: tests/test_tree_rust.h =================================================================== --- tests/test_tree_rust.h +++ tests/test_tree_rust.h @@ -12,6 +12,9 @@ Q_OBJECT public: class Private; + +private Q_SLOTS: + private: Private * m_d; bool m_ownsPrivate;