Constructor
trait to allow non-Default
QObjects
#339
-
Currently all ProposalAdd a API suggestion#[cxx_qt::bridge]
mod ffi {
#[cxx_qt::qobject]
pub struct MyObject {
#[qproperty]
number: i32,
#[qproperty]
string: QString,
}
impl Constructor<(i32, &QString)> for MyObject {
fn construct((number, string): (i32, &QString)) -> Self {
MyObject {
number,
string
}
}
}
} Which would then generate an constructor: class MyObject : public QObject {
MyObject(int number, const QString &string, QObject *parent = nullptr /*We should probably auto-add the parent pointer*/)
: QObject(parent)
, m_rustObj(constructRust(number, string)) {
}
} Additional NotesThe trait would need to be defined like this: trait Constructor<T> {
fn construct(args: T) -> Self;
} We could however also define it with an additional // where cxx_qt::QObject is an auto-generated trait that links T to qobject::T
trait Constructor<T>: cxx_qt::QObject {
fn construct(args: T) -> Self;
fn intialize(self: Pin<&mut <Self as cxx_qt::QObject>::Qt>, args: T) {
// By default, do nothing in the constructor
}
} Which would result in this constructor: class MyObject : public QObject {
MyObject(int number, const QString &string, QObject *parent = nullptr /*We should probably auto-add the parent pointer*/)
: QObject(parent)
, m_rustObj(constructRust(number, string)) {
initializeRust(number,String);
}
} Open Question: Do we also want to give the args to the initialize function? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 1 reply
-
Note that it would also be useful to be able to do something when constructing/destructing in the context of the C++ obj in Rust. I've had a few cases where i want to start a background thread / timer in Rust that needs to be able to talk to qt_thread(). So then i've had to create a start method that is called from Component.onCompleted. But also for this we decided that you should just connect up Component.onCompleted: start() or something in #13. So maybe we don't need to think about this. |
Beta Was this translation helpful? Give feedback.
-
(Also see #474 for discussions around how to have a different parent pointer) |
Beta Was this translation helpful? Give feedback.
-
I think we have to, otherwise where do they go ? The other problem becomes, if this object with extra custom parameters ever goes to QML what happens then as QML elements normally have a constructor without custom parameters 🤔 |
Beta Was this translation helpful? Give feedback.
-
We can expand this to fix a few issues:
// auto-generate this trait impl for any qobject::T
trait QObject {
type Inner;
}
trait Constructor<Args>: QObject {
type ConstructArguments = Args;
type initializeArguments = ();
type BaseArgs = (*mut QObject);
fn route_args(args: Args) -> (ConstructArguments, InitializeArguments, baseArgs) {
(args, (), (nullptr))
}
fn construct(args: ConstructArguments) -> <Self as QObject>::Inner {
Default::default()
}
fn intialize(self: Pin<&mut Self>, args: InitializeArgs) {
// By default, do nothing in the constructor
}
}
// Case 1 Reroute arguments (i.e. parent) to base class (including default nullptr impl)
impl Constructor<(*mut QQuickItem)> for qobject::T {
type ConstructArguments = ();
type BaseArgs = (*mut QQickItem);
fn route_args((item): BaseArgs) -> (ConstructArguments, InitializeArguments, BaseArgs) {
((), (), (item))
}
}
impl Constructor<()> for qobject::T {
type ConstructArguments = ();
type BaseArgs = (*mut QQickItem);
fn route_args((): BaseArgs) -> (ConstructArguments, InitializeArguments, BaseArgs) {
<Self as Constructor<(*mut QQuickItem)>>::route_args((nullptr))
}
}
// Case 2: Custom constructor with default parent arguments
impl Constructor<(i32, *mut QObject)> for qobject::T {
type ConstructArguments = (i32);
type BaseArgs = (*mut QObject);
fn route_args((i, parent): Args) -> (ConstructArguments, InitializeArguments, BaseArgs) {
(i, (), (parent))
}
fn construct((i): (i32)) -> T {
T { thing: i }
}
}
// Case 3: Custom C++ initialize stuff
impl Constructor<(i32, *mut QObject)> for qobject::T {
type InitializeArguments = (i32);
type BaseArgs = (*mut QObject);
fn route_args((i, parent): Args) -> (ConstructArguments, InitializeArguments, BaseArgs) {
(i, (), (parent))
}
fn initialize(self: Pin<&mut Self>, (i): (i32)) {
self.as_mut().something(i);
}
}
impl Constructor<(i32)> for qobject::T {
type InitializeArguments = (i32);
type BaseArgs = (*mut QObject);
fn route_args((i): Args) -> (ConstructArguments, InitializeArguments, BaseArgs) {
<Self as Constructor<(i32, *mut QObject)>>::route_args((i, nullptr))
}
fn initialize(self: Pin<&mut Self>, (i): (i32)) {
<Self as Constructor<(i32, *mut QObject)>>::initialize(self, (i))
}
} We'd have to auto-generate types for all of these pairs unfortunately. #include <iostream>
#include <string>
class MyBase {
public:
MyBase(int x, std::string y) : m_x(x), m_y(y) {}
virtual ~MyBase() {}
int m_x;
std::string m_y;
};
struct MyBaseArgs {
int x;
std::string &&y;
};
struct MyClassArgs {
int z;
};
struct MyArgs {
MyBaseArgs baseArgs;
MyClassArgs classArgs;
};
MyArgs rustArgs(int newZ, int newX, std::string &&newY) {
return {.baseArgs = {.x = newX, .y = std::move(newY)},
.classArgs = {.z = newZ}};
}
class MyClass : public MyBase {
public:
MyClass(int z, int x, std::string &&y)
: MyClass(rustArgs(std::move(z), std::move(x), std::move(y))) {}
~MyClass() {}
void foo() {
std::cout << "x: " << m_x << std::endl;
std::cout << "y: " << m_y << std::endl;
std::cout << "z: " << m_z << std::endl;
}
private:
MyClass(MyArgs &&args)
: MyBase(std::move(args.baseArgs.x), std::move(args.baseArgs.y)),
m_z(std::move(args.classArgs.z)) {}
int m_z;
};
int main(int argc, char *argv[]) {
MyClass myClass(23, 42, "Hello");
myClass.foo();
} |
Beta Was this translation helpful? Give feedback.
We can expand this to fix a few issues:
Copy
on the arguments