I'm trying to test QML to understand how it works with C++. I have ClassA and ClassB - 2 similar C++ classes. Here is a ClassA. All methods are self explanatory with their names, so I won't place implementation here.
class ClassB;
class ClassA : public QObject
{
Q_OBJECT
public:
explicit ClassA(QObject *parent = 0);
~ClassA();
Q_PROPERTY(ClassB* classB READ getClassB WRITE setClassB NOTIFY classBChanged)
ClassB* getClassB() const;
void setClassB(ClassB *classB);
signals:
void classBChanged();
private:
ClassB *m_classB;
};
ClassB is the same, just change all *lassA* to *lassB* and all *lassB* to *lassA*.
Then I register both classed in QML with
qmlRegisterType<ClassA>("testmodule.test",1,0,"ClassA");
qmlRegisterType<ClassB>("testmodule.test",1,0,"ClassB");
And in QML code on mouse click I create both objects like this:
onClicked: {
var comp = Qt.createComponent("TClassA.qml"); //TClassA.qml is
//a component of type
//ClassA
var ca = comp.createObject();
comp = Qt.createComponent("TClassB.qml");
var cb = comp.createObject();
ca.classB = cb;
cb.classA = ca;
parent.blockFromGC = ca;
}
And after that I call garbage collector with gc(). I expected that ca is blocked from removal with parent.blockFromGC and cb is blocked from removal with reference from ca. But garbage collector destroyed cb and after that parent.blockFromGC.classB === null.
So I have second MouseArea with this code:
onClicked: {
console.log(mouse.button)
// if (mouse.button == Qt.RightButton) {
// console.log(parent.vasya.classB)
// }
gc();
console.log(parent.blockFromGC.classB) //I use cb here
}
So when I click the MouseArea I get in console:
qml: 1 //Left button
qml: null //value of parent.blockFromGC.classB
classB destroyed: TQMLClassB(0x34960d0) //I have qDebug() in destructor
So my object cb was destroyed.
So I have this questions:
1) Is there a way how I can register a C++ type as basic type, so I could
write var ca = new ClassA() instead of creating a *.qml file, creating a component and finally creating an object?
2) Why did garbage collector destroyed my cb object and what should I do
to keep this object from deleting?
Moreover! If I uncomment those commented lines
// if (mouse.button == Qt.RightButton) {
// console.log(parent.vasya.classB)
// }
regardless the button I press, the object is not destroyed anymore.
qml: 1 //left button
qml: TQMLClassB(0x3df8e90) //object is alive
.....
qml: 2 //right button
qml: TQMLClassB(0x3df8e90) //before gc() - alive
qml: TQMLClassB(0x3df8e90) //after gc() - alive
3) Where can I read about QML memory management in detailes? I find this behaviour really strange..
Addition 1: I played with this situation a bit more and the results were unpredictable. I updated from 5.3 to Qt 5.4 and this behavior with object deletion has gone. The problem is that the behavior was so unpredictable, that the fact I can't reproduce this behavior in Qt 5.4 doesn't mean that the problem is fixed. I'll try to look in bug reports and bug fixes. If I found something, I'll post it here. If not, I'll try to reproduce this situation in Qt 5.4 and post a report.
Like any QML type, you can define a component statically within another:
Component {
id: classAComponent
ClassA { }
}
onClicked {
var ca = classAComponent.createObject()
}
There is a subtlety here: assigning a QML object to a QML property will increase its JavaScript ref-count. But an instance stored only in the Q_PROPERTY of a C++ object won't be marked by the garbage collector.
QML has a dual ownership system. First it defines a hierarchy of QObject/QQuickItem used for display and ownership. Attached to this backbone is a garbage collection system where any QML object can own a tree of JavaScript objects through property var.
So to keep your ClassB object alive, you either have to keep it in a QML property, or provide a parent for it when calling component.createObject() (it's a hard ownership; it will be destroyed regardless of any JS reference to it when the parent is destroyed)
Example with the QML property:
Component {
id: classAComponent
ClassA {
property Item refClassB
}
}
onClicked {
var ca = classAComponent.createObject()
ca.refClassB = classBComponent.createObject()
}
Ideally you should avoid dynamically creating object as much as possible and use your C++ objects statically like normal QML components and let the declarative structure maintain the QObject backbone automatically, like this:
ClassA {
classB: ClassB { }
}
Sadly not so much, the best I know of, more for QML than C++ is Dynamic QML Object Creation from JavaScript.
Related
I'm in the early stages of learning Dart & Flutter. I'm looking at how to implement an eventbus, which works fine, but I've noticed that Widgets (and/or their associated state) hold a strong reference to the (global) eventbus, causing a memory leak. The solution is to cancel the subscription in the widget-state's dispose method, but I'd like to know if there's a better approach (I'm coming from Swift which allows variables to be declared as 'weak').
EDIT
I ended up subclassing the state as follows... any better suggestions?
abstract class CustomState<T extends StatefulWidget> extends State {
List<StreamSubscription> eventSubscriptions = [];
void subscribeToEvent(Object eventClass, Function callback) {
StreamSubscription subscription = eventBus.on(eventClass).listen(callback);
eventSubscriptions.add(subscription);
}
void dispose() {
super.dispose();
eventSubscriptions.forEach((subscription) => subscription.cancel());
eventSubscriptions = null;
}
}
class MyEvent {
String text;
MyEvent(this.text);
}
class _MyHomePageState extends CustomState<MyHomePage> {
#override
void initState() {
super.initState();
subscribeToEvent(MyEvent, onEventFired);
}
void onEventFired(event) {
print('event fired: ${event.runtimeType} ${event.text}');
}
}
Dart doesn't provide weak reference feature.
An Expando has a weak reference behavior though.
Not sure if this is of use in your use case.
https://api.dartlang.org/stable/1.24.3/dart-core/Expando-class.html
https://groups.google.com/a/dartlang.org/forum/m/#!topic/misc/S7GGxegtJe4
What is the Dart "Expando" feature about, what does it do?
https://github.com/dart-lang/sdk/issues/16172
I sometimes use a Mixin that provides a list where I can add subscriptions and a dispose methode that cancels all subscriptions and add it to widgets and other classes where I need it.
As of 2020, I'd like to add to Günter's answer that I've just published a package that goes as close as possible to a weak-reference by implementing a weak-map and a weak-container, as well as cache functions that take advantage of weak references.
https://pub.dev/packages/weak_map
It's much easier to use than an Expando (it uses Expando internally).
Since dart 2.17 you can use WeakReference.
Any object wrapped in WeakReference(obj) is not kept from being garbage collected.
You access the object via the target property which becomes null when the object got garbage collected.
final myWeakRef = WeakReference(ExampleObj());
// access obj, may be null
print(myWeakRef.target);
Using forms in a C++ DLL just to note. I'd thought this would be important as I need it to have the same variables as the Application has (which may or may not be different)
Starting my form I have to do this:
Form1 ^ ThisForm = gcnew Form1;
Application::Run(ThisForm);
Which is basic nothing to difficult. I get my form working fine upon use. Now I want to create a thread through Form1 (ThisForm). They are defined here:
public:
VOID WINAPI MainThread2();
And all it does is set the label within this to the current time:
VOID Form1::MainThread2()
{
while (true)
{
Beep(400, 100);
time_t CurrentTime = time(0);
struct tm* TimeStruct = localtime(&CurrentTime);
string str = to_string(TimeStruct->tm_hour) + ":" + to_string(TimeStruct->tm_min) + ":" + to_string(TimeStruct->tm_sec);
String^ timestring = gcnew String(str.c_str());
this->label1->Text = "hello";
}
}
But obviously I can't create a thread with this:
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ThisForm->MainThread2, NULL, NULL, NULL);
Due to this error:
error C2440: 'type cast': cannot convert from 'overloaded-function' to 'LPTHREAD_START_ROUTINE'
How would I manage to start a thread through ThisForm
I recommend that you avoid the unmanaged thread APIs, and use the managed ones. This will let you use instance methods of managed classes, rather than just static C++ methods.
MSDN References:
Thread class
Thread constructor
If you really do want to use the unmanaged APIs, you need to make your thread method a static or global method (not instance, as you have it now). I don't remember off the top of my head if it's allowed to be a static method of a managed class; you may need to have it be a global method to make this work. (A static method of an unmanaged class would also work, but that doesn't buy you much.)
I'm working on a student project. It's a network card game. The solution contains 3 projects. Client's GUI using Windows Forms so it has managed classes. Static client's library in native C++. GUI's project has reference to it thus uses 'Mixed Rules'. Server is in native C++ as well. I use RPC middleware for communication. It works only with native C++. That is why I need the static library to hide there all the details of communication on client's side.
Since the server can at any moment change its state and that should be shown in client's GUI, I use callback approach to change Windows Forms' components. And here I found a problem because I need to change private members of managed class with the help of a native object.
There are probably different ways to do that. My idea is sending a pointer to instance of managed class into instance of native class and saving it there. So later I can call from that native object public member functions of that managed class to change components.
This is from my 'Mixed Rules' GUI project:
//Native class for changing window 'Lobby'
class LobbyI : public ClientLib::Lobby {
public:
LobbyI();
~LobbyI();
//Should change window due to current Server's state
void reDraw(const CommonLogic::ServerState&);
};
// Managed class implements GUI for window 'Lobby'
// generated by Visual Studio designer
public ref class LobbyGUI : public System::Windows::Forms::Form {
//My members
ClientLib::Mediator* mediatorPtr; // Is it correct?
LobbyI* lobbyPtr; // ?
public:
LobbyGUI(void) {
InitializeComponent();
mediatorPtr = new ClientLib::Mediator(); // Is it correct?
lobbyPtr = new LobbyI(); // ?
mediatorPtr->setCallback(lobbyPtr);
}
protected:
~LobbyGUI() {
if (components) { delete components; }
delete lobbyPtr; // Is it correct?
lobbyPtr = nullptr; // ?
delete mediatorPtr; // ?
mediatorPtr = nullptr; // ?
}
private: System::Windows::Forms::Button^ buttonLogIn;
//...
This is from native static library ClientLib:
class Lobby {
public:
virtual ~Lobby();
virtual void reDraw(const CommonLogic::ServerState&) = 0;
};
class Mediator {
CommonLogic::ServerState serverState;
Lobby* lobbyPtr;
public:
Mediator();
~Mediator();
void setCallback(Lobby* ptr) { lobbyPtr = ptr; }
void reDrawLobby() { lobbyPtr->reDraw(serverState); }
};
This code builds ok. The only thing I need now is that the member function reDraw() of native derived class LobbyI is able to change the window implemented by managed class LobbyGUI. Thus getting and keeping and using pointer to it. And then I think it all will work. How to do that?
Maybe it's not the nicest implementation in general. I would be happy to read other suggestion.
I'm also doubtful about the way I used pointers to native classes inside managed class. Is it correct? It didn't work correct until I inserted ptr=nullptr; after delete ptr; in destructor.
UPDATE: Now I see redundancy in my code. Abstract class Lobby is useless. I need only to implement reDraw() function in managed class which will have obviously access to components of the window. And then pass safe pointer to native class function which expects pointer to a function as a parameter.
Finally I've solved it!! Using this article. In the following code a native object stores provided pointer to a function of managed object. So this callback function can be invoked at any time. A delegate is used as a form of type-safe function pointer. Instance of GCHandle is used to prevent the delegate from being relocated by garbage collector.
Here is simple CLR Console Application which increments and prints some integer using callback function invoked from native object. Thus we can "change private members of managed object using a native one".
using namespace System;
using namespace System::Runtime::InteropServices;
typedef void(__stdcall *ANSWERCB)(); // define type of callback function
#pragma unmanaged
class NativeClass {
ANSWERCB cbFuncPtr = 0; // pointer to callback function
public:
void setCallback(ANSWERCB fptr) {
cbFuncPtr = fptr;
incAndPrint();
}
void incAndPrint() { cbFuncPtr(); } // invokes callback which increments and prints
};
#pragma managed
ref class ManagedClass {
public: delegate void Del();
private:
Int32 i;
NativeClass* nativePtr;
Del^ delHandle;
GCHandle gch;
public:
ManagedClass(Int32 ii) : i(ii) {
nativePtr = new NativeClass;
delHandle = gcnew Del(this, &ManagedClass::changeAndPrintInt);
gch = GCHandle::Alloc(delHandle);
IntPtr ip = Marshal::GetFunctionPointerForDelegate(delHandle);
ANSWERCB callbackPtr = static_cast<ANSWERCB>(ip.ToPointer());
nativePtr->setCallback(callbackPtr);
}
~ManagedClass() {
delete nativePtr;
nativePtr = __nullptr;
gch.Free();
}
private:
void changeAndPrintInt() // callback function
{
Console::WriteLine(++i);
}
};
int main(array<System::String ^> ^args)
{
ManagedClass mc(1);
return 0;
}
class MyClass
{
private static MyClass obj;
public static MyClass getInstance()
{
if(obj==null)
{
obj = new MyClass();
}
return obj;
}
In the above java code sample, because obj is a static variable inside the class,
will getInstance still be non-thread safe? Because static variables are shared by all threads, 2 simultaneous threads shall be using the same object. Isnt it?
Vipul Shah
Because static variables are so widely shared they are extremely un-thread safe.
Consider what happens if two threads call your getInstance at the same time. Both threads will be looking at the shared static obj and both threads will see that obj is null in the if check. Both threads will then create a new obj.
You may think: "hey, it is thread safe since obj will only ever have one value, even if it is initialized multiple times." There are several problems with that statement. In our previous example, the callers of getInstance will both get their own obj back. If both callers keep their references to obj then you will have multiple instances of your singleton being used.
Even if the callers in our previous example just did: MyClass.getInstance(); and didn't save a reference to what MyClass.getInstance(); returned, you can still end up getting different instances back from getInstance on those threads. You can even get into the condition where new instances of obj are created even when the calls to getInstance do not happen concurrently!
I know my last claim seems counter-intuitive since the last assignment to obj would seem to be the only value that could be returned from future calls to MyClass.getInstance(). You need to remember, however, that each thread in the JVM has its own local cache of main memory. If two threads call getInstance, their local caches could have different values assigned to obj and future calls to getInstance from those threads will return what is in their caches.
The simplest way to make sure that getInstance thread safe would be to make the method synchronized. This will ensure that
Two threads can not enter getInstance at the same time
Threads trying to use obj will never get a stale value of obj from their cache
Don't try to get clever and use double checked locking:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Good explanation can be found here:
http://en.wikipedia.org/wiki/Singleton_pattern
The wiki article highlights various thread-safe approaches along with some of their pros and cons.
in this case getInstance() is not thread-safe, even if you use static variable. only synchronization makes this thread-safe.
The following example shows a weird thread save modified single ton pattern which supports generics as well.
To have it just thread save and synchronization save just take the synchronized block and the transient and volatile keywords.
Notice, that there is a double check, the synchronized block is inside an if. This brings more performance, because synchronized is expensive.
Of course for a real singleton do not use maps, I said it is a modified one.
public class Edge<T> {
#SuppressWarnings({"unchecked"})
private static transient volatile HashMap<Object,HashMap<Object, Edge>> instances = new HashMap<Object, HashMap<Object,Edge>>();
/**
* This function is used to get an Edge instance
* #param <T> Datatype of the nodes.
* #param node1, the source node
* #param node2, the destination node
* #return the edge of the two nodes.
*/
#SuppressWarnings({"unchecked"})
public static <T> Edge<T> getInstance(T node1, T node2){
if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){
synchronized (Edge.class) {
if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){
Edge<T> edge = new Edge<T>(node1, node2);
if(!instances.containsKey(node1)){
instances.put(node1, new HashMap<Object, Edge>());
}
instances.get(node1).put(node2, edge);
}
}
}
return (Edge<T>)instances.get(node1).get(node2);
}
public class Singleton{
private static transient volatile Singleton instance;
public static Singleton getInstance(){
if(instance==null)synchronized(Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
return instance;
}
private Singleton(){
/*....*/
}
}
Page 182:
http://books.google.com/books?id=GGpXN9SMELMC&printsec=frontcover&dq=design+patterns&hl=de&ei=EFGCTbyaIozKswbHyaiCAw&sa=X&oi=book_result&ct=result&resnum=2&ved=0CDMQ6AEwAQ#v=onepage&q&f=false
Think this can be tagged as answered now.
class MyClass
{
private static MyClass obj;
private MyClass(){
// your initialization code
}
public static synchronized MyClass getInstance()
{
if(obj==null)
{
obj = new MyClass();
}
return obj;
}
I'll agree with #Manoj.
I believe the above will be one of the best methods to achieve singleton object.
And synchronization makes the object thread safe.
Even, it's static :)
When I build the following C++/CLI code in VS2008, a code analysis warning CA1001 is displayed.
ref class A
{
public:
A() { m_hwnd = new HWND; }
~A() { this->!A(); }
protected:
!A() { delete m_hwnd; }
HWND* m_hwnd;
};
ref class B
{
public:
B() { m_a = gcnew A(); }
protected:
A^ m_a;
};
warning: CA1001 : Microsoft.Design :
Implement IDisposable on 'B' because
it creates members of the following
IDisposable types: 'A'.
To resolve this warning, I would have to add this code to class B:
~B() { delete m_a; }
But I don't understand why. Class A implements IDisposable via its destructor (and finalizer).
So surely whenever A gets garbage-collected, then A's finalizer or destructor will get called, freeing its unmanaged resources.
Why does B have to add a destructor to call 'delete' on its A member?
Will the GC only call A's destructor if B explicitly calls "delete m_a"?
Edit: it seems this works automatically if you use the "syntax sugar" method of declaring the A member, like this:
ref class B
{
public:
B() { }
protected:
A m_a;
};
but this is not always possible.
Why isn't the GC clever enough to automatically dispose of the managed reference pointer of A^, once no one else has a pointer to it?
You should use stack semantics for the member and add a destructor to the containing class.
Then the member will be disposed.
See http://msdn.microsoft.com/en-us/library/ms177197.aspx
ref class B
{
public:
B() {}
~B() {}
protected:
A m_a;
};
The member is still a ref. type and is still created on the heap.
Edit:
Dispose in .net is at best unfortunate, in C# the whole deterministic behaviour is broken and you have to be really rigerous with Dispose calls to get the behaviour most c++ developers expect.
In c++/cli stack semantics make it better. If you can't use them you are back to having to explicitly call dispose which in c++/cli is represented by the destructor.
The only way to automatically chain dispose calls to members is through stack semantics if the members are normal managed pointers just like c# you'll have to chain the calls manually.
Many classes could hold the same A^ pointer, there is no way to know which one should call the destructor.
You get the warning because you have implemented the destructor which causes your class to implement IDispose. This gives you a chance to clean up in a deterministic manner.
The GC alone can only collect an object with no references and call the finalizer. This is far from deterministic. Note that relying on the finalizer to do the clean up should be a safety net only as it may be called a long time in the future if at all.
I would recommend trying to design your code to allow the above pattern.