[Qt-interest] How to "snapshot" a QObject if it can't be copied?
Hostile Fork
hostilefork at gmail.com
Fri Oct 16 08:49:46 CEST 2009
Hi all...
I have a QObject-derived class. It has signals and slots that modify
the object's data in a multithreaded environment. (Yeah, yeah, who
doesn't?) :)
I'd like to be able to snapshot that object. Because certain routines
would be harmed by getting different answers from method calls on this
QObject (if modifications happen on the other thread while they are
working). So routines have a QObject pointer, they call snapshot(),
then they make all their method calls on the snapshot. That way they
don't see the modifications.
Seems simple in theory. But here are the rules of my strange game:
1. I'd like to not pay for the copy unless a modification actually
happens
1a. I fixated on the idea that this was an application for
QSharedDataPointer
2. This is a property I want enforced on a whole inheritance hierarchy
First barrier is of course that you can't apply copy-on-write to
QObject, because they can't be copied! So to make a "copy-on-write"
QObject, you have to start by giving it a member variable for the
"state". You copy the state, not the QObject. AmIRight?
Second barrier is that I'd like the copy-on-write aspect to be
enforced by the inheritance hierarchy. e.g. I have a BaseHolder
class, and I want all objects derived from BaseHolder to have this
"you can snapshot me!" property returning a properly typed Base.
Qt and templates tend to not mix well, but for simplicity I'll pretend
they do. Here's a non-compiling pseudocode of what I was thinking:
template< class DerivedDataType >
class Base
{
// This is the base class and you make all your data
// access methods derive from it, they should all
// be const because if you modify it won't be reflected
// back to the BaseHolder!
protected:
// Note: DerivedDataType should inherit from QSharedData or obey
// the shared data protocol
QSharedDataPointer< DerivedDataType > d;
public:
Base (unique_ptr< DerivedDataType >&& init)
d (move(init))
{
}
Base (const Base &other)
: d (other.d)
{
}
~Base()
{
// NOT virtual, to reinforce that you are absolutely
// not supposed to be putting data members in
// derived classes. Put them in the DerivedDataType
// class! otherwise how will they make it into the
// snapshot?
}
};
template< class DerivedDataType >
class BaseHolder : public QObject
{
// This is the QObject class and its slots can modify
// the state. some methods const and some non-const
protected:
Base< DerivedDataType > state;
public:
BaseHolder (unique_ptr< DerivedDataType >&& init) :
Base(move(init))
{
}
BaseHolder (const Base &other)
: d (other.d)
{
}
Base< DerivedDataType > snapshot()
{
// returns an object that is semantically equivalent
// to making a copy of the current state, but which
// defers the actual act of copying until a write occurs
return state;
}
virtual ~BaseHolder()
{
// This is virtual to indicate that it is okay to clean up
// any events or signals in your derived destructor.
// There's only one QObject and if you have mutexes
// or HttpRequests or whatever then that's fine to
// clean up here. But remember that clients
// won't work w/pointers to BaseHolder... just instances
// of Base... so if something is part of the data interface
// make it a method on Base which is a function
// of DerivedDataType
}
};
Sort of?
Making matters worse, the code that calls snapshot() is generic...
e.g. it must be able to make a snapshot without knowing specifically
which template instance it is dealing with. This means I need more
steps in the class heirarchy, and a virtual copy constructor (e.g.
clone)
http://www.parashift.com/c%2B%2B-faq-lite/virtual-functions.html#faq-20.8
This is breaking my brain. :) Besides switching from Qt & C++ to a
language where this can be more naturally expressed, can any sharp
sticks on qt-interest point me in a good direction or give some
perspective, given my intention?
Best,
Brian
---
http://hostilefork.com
More information about the Qt-interest-old
mailing list