25.11.10

The Beauty of Qt 1: D-Pointer / Private Implementation

I believe many of you who just start reading the source code of Qt will have the question: why Qt uses those private classes? What's the benefit of such a design pattern? Well, the most significant reason is to keep the binary compatibility.

Then what is binary compatibility? The article from KDE TechBase defines it like this:
A library is binary compatible, if a program linked dynamically to a former version of the library continues running with newer versions of the library without the need to recompile.
With this definition in mind, the benefit is easy to see: without it, whenever a library is updated, all the applications rely on it should be recompiled to work. Definitely, it's totally unacceptable for any widely used libraries like Qt. More information on binary compatibility itself can be found on that article from KDE TechBase, and here I only want to share with you how it's used.

Without the usage of D-pointers, we may have a class defined as below:
class MyClass
{
public:
MyClass();
~MyClass();
private:
int myVar;
};


Obviously, the private member of myVar here is a big enemy of binary compatibility. There fore, we refactor the code into the following:
class MyClassPrivate;
class MyClass
{
public:
MyClass();
~MyClass();
private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
};


Here, we use a pointer d_ptr to refer to the private implementation, and the macro of Q_DECLARE_PRIVATE is employed to define some helper functions and friend class:
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
friend class Class##Private;


Then we define the private class as below:
class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent);
private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
int myVar;
};


Here, the q_ptr pointer points the public interface, and the Q_DECLARE_PUBLIC macro is used to define some helper functions:
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
friend class Class;


And we can further use the following two macros to ease the access:
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()


That's all about the usage of D-pointers and private implementations in Qt. Let me use a simple sample to end this post ;)
// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#include <QtCore/QObject>

class MyClassPrivate;
class MyClass: public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent = 0);
virtual ~MyClass();
void dummyFunc();

signal:
void dummySignal();

private:
MyClassPrivate * const d_ptr;
Q_DECLARE_PRIVATE(MyClass);
Q_DISABLE_COPY(MyClass);
};

#endif // MYCLASS_H

// myclass.cpp
#include "myclass.h"

class MyClassPrivate
{
public:
MyClassPrivate(MyClass *parent)
: q_ptr(parent)
{
}

void foobar()
{
Q_Q(MyClass);
emit q->dummySignal();
}

private:
MyClass * const q_ptr;
Q_DECLARE_PUBLIC(MyClass);
};

MyClass::MyClass(QObject *parent)
: QObject(parent)
, d_ptr(new MyClassPrivate(this))
{
}

MyClass::~MyClass()
{
Q_D(MyClass);
delete d;
}

void MyClass::dummyFunc()
{
Q_D(MyClass);
d->foobar();
}


The Chinese translation is available at: http://blog.csdn.net/zhu_xz/archive/2010/11/25/6035861.aspx

1.11.10

New release of Web Runtime for N900

Finally, I'm happy to push the latest Web Runtime (v1.1.0) for N900 to the extras-devel repository. Please note that this release is only tested against PR 1.3. There's no new features implemented compared to the previous release back in July, but only bug fixes. Personally, this is one of the last duties for me in the WRT project, and let's see what I would play with in the future ;)
What is Web Runtime?
It's a development framework with which you can write applications in standard Web technologies, like HTML, CSS, and JavaScript. Also, a bunch of Device APIs are provided to grant you the access to native resources, e.g. file system, sensors, etc., using JavaScript.
To install WRT on N900, you should first enable the extras-devel repository.

If you have the previous release installed, you should first uninstall:
sudo gainroot
apt-get purge libwrt-experimental1

Then install the new release:
sudo gainroot
apt-get install qtwrt

Have fun and feel free to go to the public forum for more questions ;)