Qt4学习笔记 (7)

January 22nd, 2015 No comments

This is a repost from my previous Qt learning series, based on Qt 4.3.

    本篇说一下Qt对于脚本的支持, 即QtScript模块.

    Qt支持的脚本基于ECMAScript脚本语言, 这个东西又是javascript, jscript的基础. 所以, 一般只要学过javascript就基本会写Qt脚本了. 自此开始, Qt脚本现在就叫javascript.
    不过作为土人, javascript中有一个prototype的概念, 现在才知道. javascript本没有类的概念, 跟不用说是继承之类的了. 但是凭借prototype的特性, 我们可以实现类似C++中类, 以及类继承等一些特性.
    prototype是个什么概念? 因为这个单词实在表意不清, 导致我花了很多时间来理解这个. 每个javascript对象都有一个指向另一个对象的引用, 这就是它的prototype. 一个对象的prototype定义了这个对象可以进行的操作集. 用C++来类比的话, 这些操作集是一定是成员函数. 看下面的javascript代码:

function Shape(x, y) {
    this.x = x;
    this.y = y;
}
Shape.prototype.area = function() { return 0; }

function Circle(x, y, radius) {
    Shape.call(this, x, y);
    this.radius = radius;
}
Circle.prototype = new Shape;
Circle.prototype.area = function() {
    return Math.PI * this.radius * this.radius;
}

    我们把Circle对象的prototype设置成Shape对象, 实际上就是把Shape对象的prototype赋给了Circle对象, 让Circle对象的初始操作集跟Circle对象是一样的. 之后我们又重载了area()函数, 当然我们还可以加入新的函数. 它对应的C++代码如下:

class Shape
{
public:
    Shape(double x, double y) {
        this->x = x;
        this->y = y;
    }
    virtual double area() const { return 0; }
    double x;
    double y;
};

class Circle : public Shape
{
public:
    Circle(double x, double y, double radius) : Shape(x, y)
    {
        this->radius = radius;
    }
    double area() const { return M_PI * radius * radius; }
    double radius;
};

    所以, 我们看到了, 对于一个javascript对象来说, 它还包括了一个内部的prototype对象. 对于Qt要用C++来实现类似prototype的功能的话, 除了要写一个javascript中的对应类, 还要写这个类对应的prototype类. 这个东西很高级, 也很麻烦, 所以建议看官方文档: http://doc.trolltech.com/4.3/qtscript.html#making-use-of-prototype-based-inheritance

    下面我们来说一下一般怎样从Qt的C++代码中调用Qt的script代码. 假设我们要写一个dialog, 上面有一个QPushButton, 一个QLineEdit. 点击QPushButton的时候, 会弹出一个QMessageBox来显示消息.

a) 直接写Qt的C++代码的话, 只要用signal/slot就行了:

void HelloDialog::helloClicked()
{
    QString text = textEdit->text();
    text = "Hello " + text;
    QMessageBox::information(this, "info", text);
}

b) 现在我们要加入javascript 的支持. 要解决的大概有这么一些问题: javascript中怎么拿到QLineEdit里的字符串? javascript中怎么调用QMessage这个Qt的类? 我们还是先来看代码:

QScriptValue constructQMessageBox(QScriptContext *, QScriptEngine *engine) {
    return engine->newQObject(new QMessageBox());
}

void HelloDialog::helloClicked()
{
    QString helloScript = scriptsDir.filePath("xxx.js");
    QFile file(helloScript);
    if (!file.open(QIODevice::ReadOnly)) {
        return;
    }
    QTextStream in(&file);
    in.setCodec("UTF-8");
    QString script = in.readAll();
    file.close();
    // section 1
    QScriptEngine engine;
    QScriptValue helloDialog = engine.newQObject(this);
    engine.globalObject().setProperty("helloDialog", helloDialog);
    // section 2
    QScriptValue constructor = engine.newFunction(constructQMessageBox);
    QScriptValue qsMetaObject = engine.newQMetaObject(&(QMessageBox::staticMetaObject), constructor);
    engine.globalObject().setProperty("QMessageBox", qsMetaObject);

    QScriptValue result = engine.evaluate(script);
}

    我们先把整个javascript文件读进来, 加入一堆设置, 最后调用QScriptEngine::evaluate()函数来执行这段javascript. QScriptEngine这个类就相当于javascript的解释器.
    javascript里没有类这个概念, 所有的变量都是var类型. 如果要让Qt的C++类在javascript里运行, 那么先要将它包装(wrap)成一个javascript的类型. 代码的section 1部分把this(即当前的dialog)先做了包装, 然后把包装后的对象加入到javascript的运行环境的全局变量中.
    接着来解决QMessageBox的问题. 由于javascript中没有类, 继而也就是没构造函数这个概念, 但是当我们在javascript中new一个Qt C++对象的时候, 还是需要调用它的构造函数. 代码的section 2部分先把一个C++回调函数(之所以称为回调函数, 是因为要作为QScriptEngine::newFunction()的参数, signature是固定的)包装成一个QScriptValue, 然后把它和QMessageBox的meta-object信息一起包装成一个QScriptValue, 最后依样画葫芦地加入到javascript的运行环境的全局变量中. 这样我们就能在javascript中new出一个QMessageBox了.
    有一个很重要问题. 就是Qt的meta-object系统和javascript的调用系统是有对应关系的. 在javascript中, 一个var如果是QObject包装而来, 那么这个QObject的所有property(Q_PROPERTY声明), signal/slot都是可以在javascript中调用的. 还有就是这个QObject的所有child (指的是包含而不是继承关系), 也是可以直接访问的.
    看一下javascript代码. 其中greeting和text都是属性:

function showMessage(parent, title, text)
{
    var messageBox = new QMessageBox;
    messageBox.windowTitle = title;
    messageBox.text = text;
    messageBox.icon = QMessageBox.Information;
    return messageBox.exec();
}

return showMessage(helloDialog, "info", helloDialog.greeting + " " + helloDialog.text);

c) 我们实现了用javascript来控制逻辑. GUI的话, Qt也提供了一种可以直接读取*.ui的方法: QUiLoader::load()函数. 于是我们连GUI也可以不用直接编译到binary里去了. 我们要做的就是用Qt的C++代码搭一个大概的框架, 加载需要的*.ui, *.js文件, 在适当的时候调用适当的javascript函数就行了. 而且*.ui文件对于每个控件都会有一个objectName的属性, 用uic生成代码的话, 这个值就是变量名, 如果用QUiLoader::load()的话, 这个就被赋给了QObjectobjectName这个property. 当我们要在一个QWidget的javascript对象里引用它的子控件的时候, 便能直接用这个objectName来引用. 于是*.ui 和*.js文件可以说简直配合的天衣无缝那.
    还是来看代码, Qt的C++代码没什么好说的, 就看javascript代码:

function showMessage(parent, title, text)
{
    var messageBox = new QMessageBox;
    messageBox.windowTitle = title;
    messageBox.text = text;
    messageBox.icon = QMessageBox.Information;
    return messageBox.exec();
}

function doClick()
{
    var text = dialog.textEdit.text;
    showMessage(dialog, "info", "Hello " + text);
}

dialog.show();
dialog.helloButton.clicked.connect(doClick);
return 0;

    直接访问子控件是不是清爽多了? 呵呵. 代码见这里. 其它请参考官方文档:
*) http://doc.trolltech.com/4.3/ecmascript.html
*) http://doc.trolltech.com/4.3/qtscript.html

Categories: C/C++ Tags:

Qt4学习笔记 (6)

January 22nd, 2015 No comments

This is a repost from my previous Qt learning series, based on Qt 4.3.

    本篇继续介绍Qt的高级特性:

1. L&F Customization (自定义观感)

    这个东西也就是传说中的skining, 换皮肤(画皮? – -). 如果你用过mfc或者java来实现控件的换肤功能的话, 那你一定觉得那是最恐怖的事情, 简直是一张张的图片往上贴啊… Qt也可以这么做, 但是提供了2种更聪明方便的方法:
a) qss (Qt Style Sheet): 这个东西灵感来源于html的css (书上也的是”inspired by”, 我就直译啦). 也就是说用qss来控制观感, 逻辑什么的还是用C++代码. 然后我就不介绍啦, 看看官方文档吧: http://doc.trolltech.com/4.3/stylesheet-syntax.html
b) 子类化QStyle类: Qt把所有观感相关的数据都抽象到了QStyle或者它的子类中, 我们要支持一个新的L&F的话, 只要继承这个类或者其子类, 重载一些函数就可以了. 这个我也不详细说啦, 书上也几乎都是抄的官方文档: http://doc.trolltech.com/4.3/style-reference.html

2. 插件框架:

    我们来想一下, 对于插件一般的实现方法. 能想到的大概是这样. 写一堆dll, 这些dll都提供统一的export接口函数.
    Qt的做法做法是差不多的, 只是在中间又提供了一层跨平台的抽象(shared dll在各个平台的实现都是不一样的嘛) 以及精简接口. 用PE Explorer可以看到所有的Qt plugin都只export了2个函数: qt_plugin_instance(), qt_plugin_query_verification_data(), 不过这两个函数具体是怎么调用的还没survey过.
    Qt的plugin可以分为两类: Qt本身的plugin和自己写的application的plugin.
    Qt的plugin可以implement 已有的接口. 比如可以implement QStylePlugin接口把style作为plugin, 可以implement QImageIOPlugin接口扩展Qt可以识别的图片格式, 等等. 这个就不说了.
    Qt的plugin还可以implement自定义的接口. 下面说一下大概的步骤:

a) 首先定义一个interface的类, 定义virtual functions, 最后调用Q_DECLARE_INTERFACE宏. 一般的interface都会提供这样的结构, 一个keys()接口说明当前plugin可以创建哪些object, 然后是一个创建object的函数(这里我写的doSomething()是同样的意思). 代码如下:

class QXxxInterface
{
public:
    virtual ~QXxxInterface() { }
    virtual QStringList keys() const = 0;
    virtual QXxx doSomething(QString key) = 0;
};
Q_DECLARE_INTERFACE(QXxxInterface, "com.ycool.gonwan.QXxxInterface/1.0")

b) Implement 这个interface, 以下分别是*.h文件和*.cpp文件:

// .h
class QMyXxxPlugin : public QObject, public QXxxInterface
{
    Q_OBJECT
    Q_INTERFACES(QXxxInterface)
public:
    QStringList keys() const;
    QXxx doSomething(QString key);
};
// .cpp
Q_EXPORT_PLUGIN2(myxxxplugin, QMyXxxPlugin)

    *.h中的Q_INTERFACES宏用来告诉Qt的meta system这个类implement了哪个接口. 而*.cpp中Q_EXPORT_PLUGIN2宏是用来export dll接口的, 放在所有类成员函数之外调用就行了.

c) 调用, Qt提供了一个QPluginLoader类专门用来load插件. 依据上面现有的代码, 我们可以如下调用:

QPluginLoader loader("./plugins/myxxxplugin.dll");
if (QXxxInterface *interface = qobject_cast<QXxxInterface *>(loader.instance()))
{
    foreach (QString key, interface.keys())
    {
        interface.doSomething(key);
    }
}

    以上代码并不涉及具体的plugin的操作, 否则就不能叫做plugin了呵呵.
    官方文档: http://doc.trolltech.com/4.3/plugins-howto.html

Categories: C/C++ Tags:

Qt4学习笔记 (5)

January 22nd, 2015 No comments

This is a repost from my previous Qt learning series, based on Qt 4.3.

    本篇说一下Qt所谓的高级特性: i18n 的支持 (Internationalization).
    首先明确一点, Qt对于unicode的支持是相当好的, 用来表示字符串的QString保存的是16-bit的QChar(官方文档上这么写哦), 跟java的char是一样的. 但是但是..实际上QString的内部数据一点也没有用到QChar, 大概是为了performance的考虑不吧, 附一段源码(/src/corelib/tools/qstring.h):

struct Data {
    QBasicAtomic ref;
    int alloc, size;
    ushort *data;
    ushort clean : 1;
    ushort simpletext : 1;
    ushort righttoleft : 1;
    ushort asciiCache : 1;
    ushort capacity : 1;
    ushort reserved : 11;
    ushort array[1];
};
Data *d;

    大概是这么个情况. 下面来说一下怎么样支持i18n.

a) 首先要做的就是把要翻译的string统统用tr()函数包起来(其实是QObject::tr()). 当然还有其它的方法, 不过这个最容易.

b) 编辑*.pro文件, 加入需要支持的语言信息. 比如我们要支持简体中文和日本语:

TRANSLATIONS  = find_zh.ts \
                find_ja.ts

c) 运行lupdate来生成上面两个*.ts文件. lupdate会自动搜索需要翻译的字符串(用tr()函数包起来的作用):

# lupdate -verbose find.pro

d) 用Qt linguist来编辑生成的两个文件, 当然如果你够nb.. 可以手动编辑… 截张图:
qt_5_1

e) 嗯.. 最好是全部都打勾了才好.. 工具栏上有检查选项, 通过了才打勾. 接着就是运行lrelease工具把*.ts文件转成binary的*.qm文件, 以便可以添加到Qt的resource文件(*.qrc) 中使用:

# lrelease -verbose find.pro

f) 新加一个*.qrc资源文件, 内容如下. 具体语法请查阅官方文档:

<RCC>
    <qresource prefix="/translations" >
        <file>find_ja.qm</file>
        <file>find_zh.qm</file>
    </qresource>
</RCC>

g) 然后就一切正常了:

# qmake find.pro
# nmake

h) 代码里大概可以这样写, 就是对QApplication对象设一个translation的属性类, 注释可以用来切换语言:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QTranslator appTranslator;
    appTranslator.load("find_" + QLocale::system().name(), ":/translations");
    //appTranslator.load("find_zh.qm", ":/translations");
    //appTranslator.load("find_ja.qm", ":/translations");
    app.installTranslator(&appTranslator);
    FindDialog *dialog = new FindDialog;
    dialog->show();
    return app.exec();
}

    还是截张图吧..三种语言…:
qt_5_2
    可以看到, Qt的layout又发挥作用了, 中文和日文的dialog相对长了一些.

    以上. 代码见这里.

Categories: C/C++ Tags:

Qt4学习笔记 (4)

January 22nd, 2015 No comments

This is a repost from my previous Qt learning series, based on Qt 4.3.

    今天来说一些琐碎的细节问题:

1. Item-View 类:

    所谓Item-View类就是类似excel的表格啊, windows资源管理器左边的目录树结构啊之类的控件.
    Qt的Item-View架构有点类似Struts的MVC架构, model表示的是数据, view表示的是数据显示. 没有control了, 一个控件还需要什么control么? 不过Qt还是在中间加了一个东西叫做delegate, 代理. delegate的作用就是: 控制当数据model要修改的时候, UI的view应该怎么显示, 是现实一个combobox呢? 还是一个lineeditor这样. 于是整个Qt的架构被称作model/view架构.
    然后举一个例子来说明一些类的关系. Qt对于比如一个list列表控件会提供这样几个类: QListWidget, QListItem, QListView, QXxxModel(比如QStringListModel). 这几个类的关系是: a) QListWidget = QListView + QXxxModel, QListWidget是Qt提供的一个方便类, 可以拿来直接用, 而如果要自定义一些行为的话, 那么要分别继承QXxxModelQListView类来达到目的(这个做法让我想到了java/eclipse里的jface的用法). b) QListWidget里的每一项叫做QListItem.
    最后还有一个index和role的概念. QXxxModel里的每一项其实都有一个index, 叫做QModelIndex, 实际上就是一个抽象. 注意它是针对一个model的, 跟QXxxItem没有直接的关系. 这个QModelIndex的作用就是, 可以跟据这个index来拿model中的数据. 这里又有一个role角色的概念–这里其实就类似于一个attribute的名字. 比如你要自己实现一个model, 那么你必须实现如下函数:

QVariant data(const QModelIndex &index, int role) const;

    这里role是一个枚举, 可以参看Qt的帮助文档.

2. Container 类:

    这个东西其实没什么好多说的, 作用跟C++ STL的容器类一样, Qt也提供比如QVector, QLinkedList, QMap之类的容器类.
    不过用Qt的容器类有这样一些好处:
a) 它们支持implicitly shared的feature. 好高级的名字是吧..那如果告诉你其实就是copy-on-write, 那么应该能理解了吧(而COW本身就是一个设计模式: flyweight pattern). 我们用STL容器类的时候, 当要把它作为参数传递的时候, 一般都会传引用. 而如果用Qt类的话则不用. Qt会自己帮我们搞定一切, 不会因为copy参数而降低performance. 说到实现的话, 稍微喵了一眼源码, 其实就是一个引用计数(reference count) 的问题, 呵呵.
b) Qt的容器类能更好的跟其它Qt类来进行merge和互操作.
c) 这里还有一个function object, 或者说functor的东西, 其实这是STL里的概念了. 所谓的functor就是一个class重写了operator()操作符, 一边这个类可以当函数来使用(比如qSort()的第三个参数). 它的好处是, 既然是一个类, 便可以绑定额外的数据.
d) 顺便提一句, QString这个类是支持unicode的.

3. Thread 类:

    Qt的thread类的用法跟linux下pthread类用法很像. 我就不列举用法了. 只说一些需要注意的地方, 多线程总是很麻烦的皑皑.
a) 在同一线程中, singal/slot的调用是同步的, 但是在不同线程中就是异步的. 调用线程只是简单的发送一个event, 然后接受线程在自己的event loop中来调用slot.
b) thread-safe的概念(简称ts吧).. ts函数: 不同线程调用这个函数, 而这个函数会操作全局的共享变量, 如果不会出现同步问题, 那么这个函数就是ts的. ts类: 其中所有的函数都是ts的, 那么这个类也是ts的.
c) reentrant, 可重入的概念. 这个东西很高级, 应该说, 一个函数如果是reentrant的话, 那么它就是ts的; 如果它不是ts的话, 那么它就一定不是reentrant的. reentrant的概念跟多线程无关, 它要求即使在同一线程内, 调用同一函数能正常工作.
    举个例子, 比如strtok()函数一定用过, 每次迭代返回下一个token的时候, 参数都一样, 但是返回不一样. 于是strtok()函数肯定是在内部维护了一个static的buffer以便操作. 由此来说, 包含static或者global变量操作的函数一定不是reentrant的, 我们要避免这种情况.
    还有一个问题, 什么情况下ts的函数不是reentrant的呢? 再举例子, 有两个线程分别用t1和t2表示. 它们同时调用一个ts的函数, 这个函数用mutex来互斥共享数据. 假设t1先进入那个mutex, 于是t2进不去了. 这个时候把t1喀嚓掉(不管你用pthread_cancel()还是别的什么). 然后, 问题就来了..t2这个线程永远卡死了=v=…
d) QObject类是reentrant的. 但是有一些注意事项:
i) 如果要创建一个新的QObject, 那么创建必须在它的parent object(指的是包含关系, 而不是继承)的创建线程中进行这个操作.
ii) 在某个线程中创建QObject必须在那个线程中释放. 如果要释放其它线程中的QObject, 那么可以进行deffered delete: 调用QObject::deleteLater()函数.
iii) Timer类, Network类的使用必须在单个线程中, 跨线程调用是不行的.
iv) 由于底层类库的限制, Qt的widget类都不是reentrant的, 而结果就是a线程不能直接调用b线程中某个widget的成员函数. 有2个妥协的方法: 1) 用singal/slot. 2) 用QMetaObject::invokeMethod() (其实就是singal/slot机制的底层实现函数).
    额额, 多线程就是麻烦呀.. 写了那么多…

4. Network 类:

    这个没什么好说的其实.
    我们知道网络一般都延时, Qt提供了2套函数: 同步和异步, 分别对应block和non-block的情况, 就这些.

5. XML 类:

    也没啥好多说的.
    Qt提供了3套XML的API(版本4.3为止; 4.4似乎又有新的API):
a) 是最容易理解的, 写什么就是什么, 读什么就是什么, 基于文本操作. 相关的类有QXmlStreamReader, QXmlStreamWriter.
b) 是SAX API, 基于event driven. 没啥好说的, 标准API啊…
c) 不用说也知道了, DOM API. 又是标准…..

6. 其它:

    以上介绍了一些Qt常用类. Qt实际上还包含database的支持(mysql, db2, oracle… 还整个包含sqlite), DnD (Drag& Drop), 2D graphics, I/O(这里有一个设计模式, Serializer Pattern, 呵呵) 等一些类, 由于比较简单, 就不说了. 最新的Qt 4.4还包括Webkit, Phonon等新的API. 4.5会加入ODF, XSLT等支持, 4.6则是USB, bluetooth等… 这个东西已经不是简单的GUI tookit了, 会越来越强大…..

Categories: C/C++ Tags:

Qt4学习笔记 (3)

January 22nd, 2015 No comments

This is a repost from my previous Qt learning series, based on Qt 4.3.

    今天说一下Qt的事件处理(event processing)机制.

    我们之前已经用过了, 当一个QPushButton被点击的时候, 它的clicked() signal就会被emit, 这还是一个signal/slot的关系. 那么Qt中的event到底是干嘛的呢?
    书上有这么一句话: As a rule, signals are useful when using a widget, whereas events are useful when implementing a widget. 也就是说一般的话, 调用signal就可以了, 如果要自定义控件, 那么还是需要用到event的. Qt的event相当于系统native的事件的抽象. 比如windows的话就是对应于message.
    还是根据源码来剖析event和signal的关系. 下图是点击了一个QPushButton之后的call stack:

qt_3

    从QApplication::notify()开始, 然后是QPushButton::event()函数, 其中分别调用父类QAbstractButton:event(), QWidget::event(). 最后这个event被dispatch到QAbstractButton::mouseReleaseEvent(), 在这个函数中, 被判定为click了这个QButton, 即调用QAbstractButtonPrivate::click()函数. 最后再在这个函数中调用QAbstractButtonPrivate::emitClicked()函数来emit QAbstractButton::clicked()这个signal.
    所以说, event实际上是singal/slot的底层实现.

Categories: C/C++ Tags:

Qt4学习笔记 (2)

January 22nd, 2015 No comments

This is a repost from my previous Qt learning series, based on Qt 4.3.

    之前的笔记: QT4学习笔记 (1). 可以复习一下, 修改了一些错误.
    今天主要说一下Qt的实现, 主要是内存方面. 书上没有写, 完全是自己看源码的, 版本4.3.3 commercial.

    先来还是那段最简单的Qt代码:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel *label = new QLabel("Hello Qt!");
    label->show();
    return app.exec();
}

    吐了没?= =b.. 还是那个new出来的QLabel的问题, Qt到底怎么样来回收的呢? debug的时候, 我直接把断点设在了QObjectQWidget类的析构函数(直接用debug模式, 打开源码设断点运行就行了). 事实证明还不够. 通过看call stack, 最终发现析构的过程从QApplication这个类开始的.
    /src/gui/kernel/qapplication.cpp, 析构函数中, 有这么一段:

    // delete widget mapper
    if (QWidgetPrivate::mapper) {
        QWidgetMapper * myMapper = QWidgetPrivate::mapper;
        QWidgetPrivate::mapper = 0;
        for (QWidgetMapper::Iterator it = myMapper->begin(); it != myMapper->end(); ++it) {
            register QWidget *w = *it;
            if (!w->parent())                        // window
                w->destroy(true, true);
        }
        delete myMapper;
    }

    可能有点难懂, 反正大写Q开头的都是类名. 这段意思大概就是QApplication类会维护一个所有运行中QWidget的mapper, 以便析构函数中作相应处理. 这里似乎只看到调用QWidgetdestory()函数, 没有释放内存. debug的结果也验证了, QLabel的析构函数没有被调用到. 难道Qt不具备防止memory leak的么?
    我们改一下代码, 把heap内存改成stack内存:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel label("Hello Qt!");
    label.show();
    return app.exec();
}

    这个显然是没有问题的. 那我们应该怎么用Qt, 才能利用Qt自带的memory管理功能, 只管new, 不用自己delete呢? 再看一段代码:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    {
        QLabel *label = new QLabel("Hello!");
        QHBoxLayout *layout = new QHBoxLayout;
        layout->addWidget(label);
        QDialog dialog;
        dialog.setLayout(layout);
        dialog.show();
    }
    return app.exec();
}

    (以上sb代码debug之用, 切勿模仿…)
    我们在QDialog上加了一个QLabel, QDialog是在stack上创建的. debug中, 当QDialog对象调用析构函数时, 在/src/gui/kernel/qwidget.cpp中, 会调用QWidget的析构函数. 在QWidget的析构函数中, 有这样一段:

    if (!d->children.isEmpty())
        d->deleteChildren();

    d是什么东西? 继续跟进, 发现d是一个QObjectPrivate类的对象, 其中的确有delete的操作(废话, 老子依据call stack反推的啊). 于是我们清楚了, Qt的类之间有父子关系(这里指的是包含, 而不是继承关系), 一旦父类对象被被delete的话, 子类也会被相应的delete. 所以, Qt中我们一般都不需要调用delete的. 但是有一个前提: 由于deleteChildren()函数是QObjectPrivate类的函数, 要使以上条件成立的话, 所有的包含关系的类必须都继承自QObject. 这里有一个design pattern, composite, 嗯.
    另外有一点, QObject类没有定义拷贝构造函数, 也就是说一般的赋值都是shadow copy. 其实是有道理的, 当我们copy一个QObject的时候, 新的object到底是继承原来的children还是不继承呢? 呵呵.
    最后我们再来花点时间讨论一下QObjectQObjectPrivate类的关系. 在/src/corelib/global/qglobal.h中, 有以下代码:

#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()

#define Q_DECLARE_PRIVATE(Class) \
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(d_ptr); } \
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(d_ptr); } \
    friend class Class##Private;

#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;

    额.. 也就是说, 一般QXxx类和QXxxPrivate类互为friend类, 一般来说QXxxPrivate类都是一些底层实现, 不属于对外的Qt API. 而QXxx类则是对外的Qt API. 有比如说有QProcessQProcessPrivate两个类, 在/src/corelib/io下, 对应4个文件, qprocess.h, qprocess.cpp, qprocess_p.h, qprocess_win.cpp. 前2个是对外的Qt API, *_p.hQProcessPrivate类头文件, *_win.cpp则是对应的平台相关的实现代码.

    好了, 下班了, 明天再写…

Categories: C/C++ Tags:

Qt4学习笔记 (1)

January 22nd, 2015 No comments

This is a repost from my previous Qt learning series, based on Qt 4.3.

    为什么要学这个? 主要是学习其中的design pattern.
    丸子买不起书, 所以看的是电子版: C++ GUI Programming with Qt4, 2nd Edition

    作为一个跨平台的GUI库, 首先Qt用的design pattern是facade模式: 不管subsystem如何, 对外提供简单统一接口.
    跟wxWidgets一样, 每次你new出来的widget其实都是不用自己来delete的, Qt会自己帮你delete掉. 可以参看源码: /src/gui/kernel/qwidgets.cpp 中的析构函数. 注意如果你用MFC的话, memory是要你自己搞定的.
先来看一段最简单的Qt代码:

#include <QApplication>
#include <QLabel>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QLabel *label = new QLabel("Hello Qt!");
    label->show();
    return app.exec();
}

    诶.. 你发现既然是GUI的程序, 为什么用的入口函数是main(), 而不是WinMain()? 能看出这个问题你已经有相当的水平了. 其实Qt自己写了一个WinMain(), 在里面调用你的main(). 源码在这里可以找到: /src/winmain/qtmain_win.cpp (windows版本).
接下来再看一个稍微复杂一点的:

#include <QApplication>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QPushButton *button = new QPushButton("Quit");
    QObject::connect(button, SIGNAL(clicked()), &app, SLOT(quit()));
    button->show();
    return app.exec();
}

    于是你看到了Qt的事件处理机制signal/slot (信号? 插槽? 好x啊–b). 一个signal相当于一个event, slot相当于一个handler. signal和slot可以是多对多的关系. 其实这里包含了两个dessign pattern. observer模式显而易见, 还有一个就是mediator模式. 这样说应该懂了吧, 不懂的话请翻阅我之前写的design pattern系列的文章.
    另外, 可以参考Qt的官方文档: Signals and Slots. 上面大概说了这么几点重要的:
a) signals/slots/emit实际上都是macro(可以看源码). 对于c++ compiler来说, signals–>protected, slots–>nothing, emit–>nothing, 对于moc(这个工具下面会说)来说, 则被另外不同的preprosessor机制预处理.
b) 通过signal/slot可以调用private的slot(这个我没试过..不太好吧).
c) SIGNAL/SLOT宏, 这2个东西很搞, 实际上就是在函数名前加上”1″或者”2″结成新的字符串–b. 所以, 丸子觉得, 这里可能是不安全的, 如果拼错了的话编译器也是检查不出来的.
d) Q_SIGNALS/Q_SLOTS/Q_EMIT宏有特殊意义, 用于第三方的signal/slot机制避免冲突.
e) signal/slot的函数signature必须: *) 相同. *) 或者slot的参数比signal要少.
    最后, 你看到一个QButton自己show()就可以了. 没错, 因为QButton继承QWidget, QWidget都是可以直接show()的, 这点跟其他的GUI Kit不一样.
    这里有2个工具: moc(Meta-Object Compiler), uic(User Interface Compiler).
    moc用来维护Qt中类的运行时信息, 把它想象成Java中的reflection就可以了. 那么麻烦主要是由于C++本身不支持这个. 所以, 实际上signal/slot机制的实现也用到了这些信息, 因此一个Qt类一定要实现某些特定的接口函数, 这就是moc所做的工作了.
    uic用来把*.ui文件转化成C++代码, 以便能编译到一个binary中去. 用xml做ui真的是一个很不错的想法, 不过这里有一个问题啊. 那就是用uic生成C++代码之前, 其它C++类如果要引用ui中的成员变量似乎是不可能的. 诶.. 这个不知道怎么解决.

    つつく.

Categories: C/C++ Tags:

Feature Matrix of NoSQL Databases

January 21st, 2015 No comments

Feature matrix of NoSQL databases, listed in Appendix of Seven Databases in Seven Weeks:

  MongoDB CouchDB Riak Redis PostgreSQL Neo4j HBase
Genre Document Document Key-value Key-value Relational Graph Columnar
Version 2.0 1.1 1.0 2.4 9.1 1.7 0.90.3
Datatypes Typed Typed Blob Semi-typed Predefined and typed Untyped Predefined and typed
Data Relations None None Ad hoc (Links) None Predefined Ad hoc (Edges) None
Standard Object JSON JSON Text String Table Hash Columns
Written in Language C++ Erlang Erlang C/C++ C Java Java
Interface Protocol Custom over TCP HTTP HTTP, protobuf Simple text over TCP Custom over TCP HTTP Thrift, HTTP
HTTP/REST Simple Yes Yes No No Yes Yes
Ad Hoc Query Commands, mapreduce Temporary views Weak support, Lucene Commands SQL Graph walking, Cypher, search Weak
Mapreduce JavaScript JavaScript JavaScript, Erlang No No No (in the distributed sense) Hadoop
Scalable Datacenter Datacenter (via BigCouch) Datacenter Cluster (via master-slave) Cluster (via add-ons) Cluster (via HA) Datacenter
Durability Write-ahead journaling, Safe mode Crash-only Durable write quorum Append-only log ACID ACID Write-ahead logging
  MongoDB CouchDB Riak Redis PostgreSQL Neo4j HBase
Secondary Indexes Yes Yes Yes No Yes Yes (via Lucene) No
Versioning No Yes Yes No No No Yes
Bulk Load mongoimport Bulk Doc API No No COPY command No No
Very Large Files GridFS Attachments Lewak (deprecated) No BLOBs No No
Requires Compaction No File rewrite No Snapshot No No No
Replication Master-slave (via replica sets) Master-master Peer-based, master-master Master-slave Master-slave Master-slave (in Enterprise Edition) Master-slave
Sharding Yes Yes (with filters in BigCouch) Yes Add-ons (e.g., client) Add-ons (e.g., PL/Proxy) No Yes via HDFS
Concurrency Write lock Lock-free MVCC Vector-clocks None Table/row writer lock Write lock Consistent per row
Transactions No No No Multi operation queues ACID ACID Yes (when enabled)
Triggers No Update validation or Changes API Pre/post-commits No Yes Transaction event handlers No
Security Users Users None Passwords Users/groups None Kerberos via Hadoop security
Multitenancy Yes Yes No No Yes No No
Main Differentiator Easily query Big Data Durable and embeddable clusters Highly available Very, very fast Best of OSS RDBMS model Flexible graph Very large-scale, Hadoop infrasturcture
Weaknesses Embed-ability Query-ability Query-ability Complex data Distributed availability BLOBs or terabyte scale Flexible growth, query-ability
Categories: Database Tags: , , , ,

std::thread and std::future in C++11

December 12th, 2014 No comments

This is a quick note to chapter 4 of C++ Concurrency in Action.

1. std::thread

In C++11, It’s quite simple to create a separate thread using std::thread. Following code will simply output “hello world” or “world hello”:

#include <iostream>
#include <thread>
using namespace std;

void foo(const char *s) {
    cout << s << endl;
}

int main() {
    thread t(foo, "hello");
    cout << "world" << endl;
    /* destructor of std::thread calls std::terminate(), so we should call join() manually. */
    t.join();
    return 0;
}

2. std::mutex and std::condition_variable

If you need synchronization between threads, there are std::mutex and std::condition_variable. The semantics are the same with that in pthread library. Here’s a simple producer/consumer demo:

#include <iostream>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
using namespace std;

queue<int> q;
mutex m;
condition_variable c;
const chrono::milliseconds ms(1000);

void producer() {
    static int i = 0;
    while (true) {
        m.lock();
        cout << "pushing " << i << endl;
        q.push(i++);
        c.notify_one();
        m.unlock();
        this_thread::sleep_for(ms);
    }
}

void consumer() {
    while (true) {
        unique_lock<mutex> lk(m);
        c.wait(lk, [](){ return !q.empty(); });
        int i = q.front();
        cout << "popping " << i << endl;
        q.pop();
        lk.unlock();
        this_thread::sleep_for(ms);
    }
}

int main() {
    thread t(consumer);
    thread t2(producer);
    t.join();
    t2.join();
    return 0;
}

3. std::future with std::async()

C++11 also simplifies our work with one-off events with std::future. std::future provides a mechanism to access the result of asynchronous operations. It can be used with std::async(), std::packaged_task and std::promise. Starting with std::async():

#include <iostream>
#include <future>
using namespace std;

void foo(const char *s) {
    cout << s << endl;
}

int bar(int a, int b) {
    return a + b;
}

int main() {
    /* auto will be simpler */
    future<void> f = std::async(foo, "hello");
    future<int> f2 = std::async(launch::async, bar, 1, 2);
    /* f.get() is required if f is deferred by the library */
    //f.get();
    /* std::async() can return a value */
    cout << "1 + 2 = " << f2.get() << endl;
    /* threads created by std::async() are joined automatically */
    return 0;
}

std::async() gives two advantages over the direct usage of std::thread. Threads created by it are automatically joined. And we can now have a return value. std::async() decides whether to run the callback function in a separate thread or just in the current thread. But there’s a chance to specify a control flag(launch::async or launch::deferred) to tell the library, what approach we want it to run the callback.

When testing With gcc-4.8, foo() is not called. But with VC++2013, it does output “hello”.

4. std::future with std::packaged_task

With std::async(), we cannot control when our callback function is invoked. That’s what std::packaged_task is designed to deal with. It’s just a wrapper to callables. We can request an associated std::future from it. And when a std::packaged_task is invoked and finished, the associated future will be ready:

#include <iostream>
#include <future>
using namespace std;

void foo() {
    cout << "in pt.." << endl;
}

int bar(int a, int b) {
    cout << "in pt2.." << endl;
    return a + b;
}

/* associate with tasks */
packaged_task<void()> pt(foo);
packaged_task<int(int,int)> pt2(bar);

void waiter() {
    /* get associated future */
    auto f = pt.get_future();
    /* wait here */
    f.get();
    cout << "after f.get().." << endl;
}

void waiter2() {
    auto f2 = pt2.get_future();
    f2.get();
    cout << "after f2.get().." << endl;
}

int main() {
    auto t = std::async(launch::async, waiter);
    auto t2 = std::async(launch::async, waiter2);
    /* associated futures will be ready when the packaged tasks complete */
    pt();
    pt2(1, 2);
    return 0;
}

In waiter() and waiter2(), future::get() blocks until the associating std::packaged_task completes. You will always get “in pt” before “after f.get()” and “in pt2” before “after f2.get()”. They are synchronized.

5. std::future with std::promise

You may also need to get notified in the middle of a task. std::promise can help you. It works like a lightweight event.

Future and Promise are the two separate sides of an asynchronous operation. std::promise is used by the “producer/writer”, while std::future is used by the “consumer/reader”. The reason it is separated into these two interfaces is to hide the “write/set” functionality from the “consumer/reader”:

#include <iostream>
#include <future>
using namespace std;

promise<bool> p;
promise<int> p2;

void waiter() {
    /* get associated future */
    auto f = p.get_future();
    /* wait here */
    f.get();
    cout << "after f.get().." << endl;
}

void waiter2() {
    auto f2 = p2.get_future();
    try {
        f2.get();
    } catch (...) {
        /* caught exception */
        cout << "caught exception in f2.get().." << endl;
    }
    cout << "after f2.get().." << endl;
}

int main() {
    auto t = std::async(launch::async, waiter);
    auto t2 = std::async(launch::async, waiter2);
    /* associated futures will be ready after a value is set */
    cout << "setting p.." << endl;
    p.set_value(true);
    /* exceptions can also be set */
    cout << "setting p2.." << endl;
    p2.set_exception(std::exception_ptr(nullptr));
    return 0;
}

Again in waiter() and waiter2(), future::get() blocks until a value or an exception is set into the associating std::promise. So “setting p” is always before “f.get()” and “setting p2” is always before “f2.get()”. They are synchronized.

NOTE: std::future seems to be not correctly implemented in VC++2013. So the last two code snippet do not work with it. But you can try the online VC++2015 compiler(still in preview as this writing), it works.

Categories: C/C++ Tags: ,

Two-phase Lookup in C++ Templates

December 12th, 2014 No comments

This is a quick note to C++ Templates: The Complete Guide. Name Taxonomy comes first in chapter 9:

Qualified name: This term is not defined in the standard, but we use it to refer to names that undergo so-called qualified lookup. Specifically, this is a qualified-id or an unqualified-id that is used after an explicit member access operator (. or ->). Examples are S::x, this->f, and p->A::m. However, just class_mem in a context that is implicitly equivalent to this->class_mem is not a qualified name: The member access must be explicit.

Unqualified name: An unqualified-id that is not a qualified name. This is not a standard term but corresponds to names that undergo what the standard calls unqualified lookup.

Dependent name: A name that depends in some way on a template parameter. Certainly any qualified or unqualified name that explicitly contains a template parameter is dependent. Furthermore, a qualified name that is qualified by a member access operator (. or ->) is dependent if the type of the expression on the left of the access operator depends on a template parameter. In particular, b in this->b is a dependent name when it appears in a template. Finally, the identifier ident in a call of the form ident(x, y, z) is a dependent name if and only if any of the argument expressions has a type that depends on a template parameter.

Nondependent name: A name that is not a dependent name by the above description.

And the definition from Chapter 10:

This leads to the concept of two-phase lookup: The first phase is the parsing of a template, and the second phase is its instantiation.

During the first phase, nondependent names are looked up while the template is being parsed using both the ordinary lookup rules and, if applicable, the rules for argument-dependent lookup (ADL). Unqualified dependent names (which are dependent because they look like the name of a function in a function call with dependent arguments) are also looked up that way, but the result of the lookup is not considered complete until an additional lookup is performed when the template is instantiated.

During the second phase, which occurs when templates are instantiated at a point called the point of instantiation(POI), dependent qualified names are looked up (with the template parameters replaced with the template arguments for that specific instantiation), and an additional ADL is performed for the unqualified dependent names.

To summarize: nondependent names are looked up in first phase, qualified dependent names are looked up in second phase, and unqualified dependent names are looked up in both phases. Some code to illustrate how this works:

#include <iostream>

template <typename T>
struct Base {
    typedef int I;
};

template <typename T>
struct Derived : Base<T> {
    void foo() {
        //typename Base<T>::I i = 1.024;
        I i = 1.024;
        std::cout << i << std::endl;
    }
};

template <>
struct Base<void> {
    //const static int I = 0;
    typedef double I;
};

int main() {
    Derived<bool> d1;
    d1.foo();
    Derived<void> d2;
    d2.foo();
    return 0;
}

Now look into Derived::foo(). I is a nondependent name, it should be looked up only in first phase. But at that point, the compiler cannot decide the type of it. When instantiated with Derived<bool>, I is type int. When instantiated with Derived<void>, I is type double. So it’s better to look up I in the second phase. We can use typename Base<T>::I i = 1.024; to delay the look up, for I is a qualified dependent name now.

Unfortunately, two-phase lookup(C++03 standard) is not fully supported in VC++ even in VC++2013. It compiles well and gives your most expecting result(output 1 and 1.024). With gcc-4.6, it gives errors like:

temp1.cpp: In member function ‘void Derived<T>::foo()’:
temp1.cpp:12:9: error: ‘I’ was not declared in this scope
temp1.cpp:12:11: error: expected ‘;’ before ‘i’
temp1.cpp:13:22: error: ‘i’ was not declared in this scope

Another code snippet:

#ifdef _USE_STRUCT
/* ADL of nondependent names in two-phase lookup should
 * only works for types that have an associated namespace. */
struct Int { 
    Int(int) { };
};
#else
typedef int Int;
#endif

template <typename T>
void f(T i) {
    g(i);
};

void g(Int i) {
}

int main() {
    f(Int(1024));
    return 0;
}

When the compiler sees f(), g() has not been declared. This code should not compile, if f() is a nontemplate function. Since f() is a template function and g() is a nondependent name, the compiler can use ADL in first phase to find the declaration of g(). Note, a user-defined type like Int is required here. Since int is a primitive type, it has no associated namespace, and no ADL is performed.

VC++2013 still compiles well with this code. You can find some clue that they will not support it in the next VC++2015 release. With gcc, they declared to fully support two-phase lookup in gcc-4.7. I used gcc-4.8, error output looks like:

temp2.cpp: In instantiation of ‘void f(T) [with T = int]’:
temp2.cpp:20:16:   required from here
temp2.cpp:13:8: error: ‘g’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
     g(i);
        ^
temp2.cpp:16:6: note: ‘void g(Int)’ declared here, later in the translation unit
 void g(Int i) {
      ^

And the code compiles well with self-defined type Int(using -D_USE_STRUCT switch).

Categories: C/C++ Tags: ,