最近通过的C++0x特性

如果说函数式程序设计语言的复兴还未成为主流的话,那么函数式程序设计的重要特征Lambda函数与闭包已经真正成为主流了。

据图灵出版的《Exceptional C++ Style中文版》作者Herb Sutter的报道,C++标准委员会已经投票通过,将Lambda函数与闭包加入C++0x。同时批准的新特性有:

  • N2535 Namespace associations (inline namespace)
  • N2540 Inheriting constructors
  • N2541 New function declarator syntax
  • N2543 STL singly linked lists (forward_list)
  • N2544 Unrestricted unions
  • N2546 Removal of auto as a storage-class specifier
  • N2551 Variadic template versions of std::minstd::max, and std::minmax 
  • N2554 Scoped allocator model
  • N2525 Allocator-specific swap and move behavior
  • N2547 Allow lock-free atomic<T> in signal handlers
  • N2555 Extended variadic template template parameters
  • N2559 Nesting exceptions (aka wrapped exceptions)

加上已经率先拥抱Lambda的C#和其他许多CLR语言(比如VB2008),以及Java 7,还有本就出身“正派”的一堆动态语言……函数式编程的春天已经来了。

当然,对于语言不断增加特性的这种赶集扎堆现象,批评之声也一直不绝于耳。图灵出版的《修改代码的艺术》一书作者Michael Feathers就发出疑问了:“” 一种新的特性,有其利,也必然有其弊。而且各种语言本来就各有擅长,何必勉强地改得越来越像?从另一角度来说,混合使用多语言,在虚拟机一层为多语言的集成更多地努力,也许是更正确的做法?

        

关于qt程序的内在使用情况的测试

最小的qt控制台程序,内存使用情况: 只链接QtCore, 不链接QtGui
VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9548 2148 1888 S 0.0 0.2 0:00.00 tq

  1.  
  2. #include <QtCore>
  3.  
  4. int main(int argc, char**argv)
  5. {
  6.     QCoreApplication a(argc, argv);
  7.  
  8.     return a.exec();
  9.     return 0;
  10. }

最小的qt GUI程序,链接QtCore和QtGui. 没有一个QMainWindow实例,只是使用QApplication
VIRT RES SHR S %CPU %MEM TIME+ COMMAND
29108 8892 7540 S 0.0 0.9 0:00.12 tq

  1. #include <QtCore>
  2. #include <QtGui>
  3.  
  4. int main(int argc, char**argv)
  5. {
  6.     QApplication a(argc, argv);
  7.  
  8.     return a.exec();
  9.     return 0;
  10. }

最小的qt GUI程序,链接QtCore和QtGui. 有一个QMainWindow实例
VIRT RES SHR S %CPU %MEM TIME+ COMMAND
29024 9824 8472 S 0.0 1.0 0:00.12 tq

  1. #include <QtCore>
  2. #include <QtGui>
  3.  
  4. int main(int argc, char**argv)
  5. {
  6.     QApplication a(argc, argv);
  7.     QMainWindow mw;
  8.    
  9.     mw.show();
  10.  
  11.     return a.exec();
  12.     return 0;
  13. }

看了下larbin

larbin是个高效的网络爬虫,可扩充性强,扩充的地方留置了接口。

这个项目虽然好,可已经很久没有更新了,现在的新编译器都编译不了,改了一些代码。

试运行了一下,抓取速度是快。

larbin还有些功能可能加上,它会变的更强大:

分布式抓取。

https支持。

在freebsd 7.0上安装上了kde 4.1.0

通过ports安装,不复杂,但费时间,硬盘空间也用了不少,总算正常启动起来了。

kde4 很好,很强,启动了kde4桌面,感觉不到在用什么系统,所能感受到的只有KDE4。

从安装kde4,到试用了一会,freebsd 做桌面还有很多问题,现在就没法正常使用flash插件看网上视频。

开发环境现在一般只用一个emacs全搞定,也就是开发环境肯定没问题。

2008-06-17
装上了在线歌曲播放器amarok 2.0 alpha 2 版,armarok是基于kde的在线播放器,上面上许多的歌曲源(现在都是英文的),支持直接搜索,支持自己添加歌曲源,支持播放本地音频、视频。
amarok 主页: http://amarok.kde.org/

2008-06-17
装上了 fcitx-3.5 中文输入法。

nullfxp同步模块

文件夹同步算法研究

1) 扫描本地同步目录,记录目录下所有文件.
2)扫描远程同步目录,记录目录下所有文件,并发送到本地端.
3) 不分大小写的排序本地/远程同步目录的文件列表。
4) 通过归并比较,得到四组文件:本地仅有的文件,远程仅有的文件,本地新的文件,远程新的文件。

OpenGL图象

       OpenGL是一种渲染2D和3D图象的标准API。Qt程序可以使用QtOpenGL模块绘制3D图象,这依赖于系统的OpenGL库。这一节假设你已经熟悉了OpenGL。如果OpenGL对你来说是新的,一个开始学习的好地方是http://www.opengl.org/。
       在Qt程序中使用OpenGL绘图是非常直接的:我们必须继承QGLWidget,重新实现几个虚函数,并把程序与QtOpenGL和OpenGL库链接。因为QGLWidget继承自QWidget,我们已经知道的大多数依然可用。主要的不同是我们使用标准的OpenGL函数执行绘图而不是用QPainter。
       为了展示这是如何工作的,我们预览一下图8.17中展示的四面体程序的代码。该程序展示了一个3D的四面体,或者叫四边骰子,它的每一面都使用不同的颜色绘制。用户可以通过按下鼠标并拖动来旋转该四面体。用户也可以通过双击它并从弹出的QcolorDialog选择一种颜色设置其中一面的颜色。
图8.17 四面体程序

  1. class Tetrahedron : public QGLWidget
  2. {
  3.     Q_OBJECT
  4. public:
  5.     Tetrahedron(QWidget *parent = 0);
  6. protected:
  7.     void initializeGL();
  8.     void resizeGL(int width, int height);
  9.     void paintGL();
  10.     void mousePressEvent(QMouseEvent *event);
  11.     void mouseMoveEvent(QMouseEvent *event);
  12.     void mouseDoubleClickEvent(QMouseEvent *event);
  13. private:
  14.     void draw();
  15.     int faceAtPosition(const QPoint &pos);
  16.     GLfloat rotationX;
  17.     GLfloat rotationY;
  18.     GLfloat rotationZ;
  19.     QColor faceColors[4];
  20.     QPoint lastPos;
  21. };

       该Tetrahedron类继承自QGLWidget。initializeGL(), resizeGL(), 和 paintGL()函数都是重新实现了QGLWidget中的。鼠标事件处理器像通过一样是重新实现了QWidget中的虚函数。

  1. Tetrahedron::Tetrahedron(QWidget *parent)
  2.     : QGLWidget(parent)
  3. {
  4.     setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
  5.     rotationX = -21.0;
  6.     rotationY = -57.0;
  7.     rotationZ = 0.0;
  8.     faceColors[0] = Qt::red;
  9.     faceColors[1] = Qt::green;
  10.     faceColors[2] = Qt::blue;
  11.     faceColors[3] = Qt::yellow;
  12. }

       在构造函数中,我们调用QGLWidget::setFormat()来指定OpenGL显示上下文,并初始化该类的私有变量。

  1. void Tetrahedron::initializeGL()
  2. {
  3.     qglClearColor(Qt::black);
  4.     glShadeModel(GL_FLAT);
  5.     glEnable(GL_DEPTH_TEST);
  6.     glEnable(GL_CULL_FACE);
  7. }

       initializeGL()函数仅在paintGL()调用前调用了一次。这是我们可以设置渲染上下文,定义显示列表,和执行其他初始化的地方。
       所有代码都是标准的OpenGL,除了QGLWidget的qglClearColor()函数之外。如果我们希望坚持使用OpenGL,我们可以在RGBA模式下调用glClearColor(),在颜色索引模式调用glClearIndex()。

  1. void Tetrahedron::resizeGL(int width, int height)
  2. {
  3.     glViewport(0, 0, width, height);
  4.     glMatrixMode(GL_PROJECTION);
  5.     glLoadIdentity();
  6.     GLfloat x = GLfloat(width) / height;
  7.     glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
  8.     glMatrixMode(GL_MODELVIEW);
  9. }

       resizeGL()函数在paintGL()首次调用前,在initializeGL()被调用之后被调用。它还在物件在任何调整尺寸的时候被调用。这是我们可以设置OpenGL视口、投影以及任何依赖于物件尺寸的其他设置的地方。

  1. void Tetrahedron::paintGL()
  2. {
  3.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  4.     draw();
  5. }

       paintGL()函数在任何物件需要重绘的时候调用。这与QWidget::paintEvent()很相似,但与QPainter函数相反,我们使用OpenGL函数。实现的绘图是在私有函数draw()中执行的。

  1. void Tetrahedron::draw()
  2. {
  3.     static const GLfloat P1[3] = { 0.0, -1.0, +2.0 };
  4.     static const GLfloat P2[3] = { +1.73205081, -1.0, -1.0 };
  5.     static const GLfloat P3[3] = { -1.73205081, -1.0, -1.0 };
  6.     static const GLfloat P4[3] = { 0.0, +2.0, 0.0 };
  7.     static const GLfloat * const coords[4][3] = {
  8.         { P1, P2, P3 }, { P1, P3, P4 }, { P1, P4, P2 }, { P2, P4, P3 }
  9.     };
  10.     glMatrixMode(GL_MODELVIEW);
  11.     glLoadIdentity();
  12.     glTranslatef(0.0, 0.0, -10.0);
  13.     glRotatef(rotationX, 1.0, 0.0, 0.0);
  14.     glRotatef(rotationY, 0.0, 1.0, 0.0);
  15.     glRotatef(rotationZ, 0.0, 0.0, 1.0);
  16.     for (int i = 0; i < 4; ++i) {
  17.         glLoadName(i);
  18.         glBegin(GL_TRIANGLES);
  19.         qglColor(faceColors[i]);
  20.         for (int j = 0; j < 3; ++j) {
  21.             glVertex3f(coords[i][j][0], coords[i][j][1],
  22.                        coords[i][j][2]);
  23.         }
  24.         glEnd();
  25.     }
  26. }

       在draw()中,我们绘制该四面体,利用x, y, 和 z旋转和存储在faceColors数组中的颜色。所有都是标准OpenGL,除了qglColor()调用之外。我们本应该使用glColor3d() 或者 glIndex()中的一个,具体用哪个依赖于当前的模式。

  1. void Tetrahedron::mousePressEvent(QMouseEvent *event)
  2. {
  3.     lastPos = event->pos();
  4. }
  5. void Tetrahedron::mouseMoveEvent(QMouseEvent *event)
  6. {
  7.     GLfloat dx = GLfloat(event->x() - lastPos.x()) / width();
  8.     GLfloat dy = GLfloat(event->y() - lastPos.y()) / height();
  9.     if (event->buttons() & Qt::LeftButton) {
  10.         rotationX += 180 * dy;
  11.         rotationY += 180 * dx;
  12.         updateGL();
  13.     } else if (event->buttons() & Qt::RightButton) {
  14.         rotationX += 180 * dy;
  15.         rotationZ += 180 * dx;
  16.         updateGL();
  17.     }
  18.     lastPos = event->pos();
  19. }

       mousePressEvent() 和 mouseMoveEvent()函数也是重新实现自QWidget以允许用户通过点击和拖动旋转该视图。鼠标左键允许用户沿x和y轴旋转,鼠标右键允许用户沿x和z轴旋转。
       在修改了rotationX、rotationY 或者rotationZ变量后,我们调用updateGL()重绘该场景。

  1. void Tetrahedron::mouseDoubleClickEvent(QMouseEvent *event)
  2. {
  3.     int face = faceAtPosition(event->pos());
  4.     if (face != -1) {
  5.         QColor color = QColorDialog::getColor(faceColors[face], this);
  6.         if (color.isValid()) {
  7.             faceColors[face] = color;
  8.             updateGL();
  9.         }
  10.     }
  11. }

       mouseDoubleClickEvent()重新实现自QWidget以允许用户通过双击它设置四面体各面的颜色。我们调用私有函数faceAtPosition()来决定哪一面,如果有的话,位于鼠标的下面。如果一个面被双击,我们调用QColorDialog::getColor()为该面获取一个新的颜色。然后我们用新的颜色值更新faceColors数组,再调用updateGL()来重绘该场景。

  1. int Tetrahedron::faceAtPosition(const QPoint &pos)
  2. {
  3.     const int MaxSize = 512;
  4.     GLuint buffer[MaxSize];
  5.     GLint viewport[4];
  6.     glGetIntegerv(GL_VIEWPORT, viewport);
  7.     glSelectBuffer(MaxSize, buffer);
  8.     glRenderMode(GL_SELECT);
  9.     glInitNames();
  10.     glPushName(0);
  11.     glMatrixMode(GL_PROJECTION);
  12.     glPushMatrix();
  13.     glLoadIdentity();
  14.     gluPickMatrix(GLdouble(pos.x()), GLdouble(viewport[3] - pos.y()),
  15.                   5.0, 5.0, viewport);
  16.     GLfloat x = GLfloat(width()) / height();
  17.     glFrustum(-x, x, -1.0, 1.0, 4.0, 15.0);
  18.     draw();
  19.     glMatrixMode(GL_PROJECTION);
  20.     glPopMatrix();
  21.     if (!glRenderMode(GL_RENDER))
  22.         return -1;
  23.     return buffer[3];
  24. }

       faceAtPosition()函数物件上特定点所在面的序号,或者如果该点没有面则返回-1。决定这个值的代码在OpenGL中有点小复杂。本质上来说,我们所做的只是在GL_SELECT模式下渲染该场景以充分利用OpenGL的挑选功能,然后就可从OpenGL的命中记录中获取面的序号(它的“名字”)。

       下面是main.cpp:

  1. #include <QApplication>
  2. #include <iostream>
  3. #include "tetrahedron.h"
  4. using namespace std;
  5. int main(int argc, char *argv[])
  6. {
  7.     QApplication app(argc, argv);
  8.     if (!QGLFormat::hasOpenGL()) {
  9.         cerr << "This system has no OpenGL support" << endl;
  10.         return 1;
  11.     }
  12.     Tetrahedron tetrahedron;
  13.     tetrahedron.setWindowTitle(QObject::tr("Tetrahedron"));
  14.     tetrahedron.resize(300, 300);
  15.     tetrahedron.show();
  16.     return app.exec();
  17. }

       如果用户的系统不支持OpenGL,我们向控制台打印一个错误消息并立即返回。
       要把该程序与QtOpenGL模块和系统的OpenGL库链接,.pro文件中需要下面的一条:
QT += opengl

       该四面体程序就完成了。为了更多关于QtOpenGL模块的信息,请看手册中QGLWidget, QGLFormat, QGLContext, QGLColormap, 和 QGLPixelBuffer的文档。

打印

       在Qt中打印与在QWidget, QPixmap, 或者 QImage上绘图相似。它包含下面几步:
1.创建一个QPainter当作一个绘图设置。
2.弹出一个QprintDialog,让用户选择一个打印机并设置几个参数。
3.创建一个在Qprinter上执行操作的QPainter。
4.使用该QPainter绘制一页。
5.调用QPrinter::newPage()前进到下一页。
6.重复第4和5步直到所有页都绘制完毕。

       在Windows和Mac OS X 上,Qprinter使用所在系统的打印机驱动程序。在Unix上,它生成PostScript并把它发送到lp或者lpr(或者到QPrinter::setPrintProgram()所设置的程序)。Qprinter也可通过调用setOutputFormat(QPrinter::PdfFormat)用于生成PDF文件。

图8.12 打印一个QImage

       让我们以一个仅在打印单页的简单例子开始。第一个例子打印一个QImage:

  1. void PrintWindow::printImage(const QImage &image)
  2. {
  3.     QPrintDialog printDialog(&printer, this);
  4.     if (printDialog.exec()) {
  5.         QPainter painter(&printer);
  6.         QRect rect = painter.viewport();
  7.         QSize size = image.size();
  8.         size.scale(rect.size(), Qt::KeepAspectRatio);
  9.         painter.setViewport(rect.x(), rect.y(),
  10.                             size.width(), size.height());
  11.         painter.setWindow(image.rect());
  12.         painter.drawImage(0, 0, image);
  13.     }
  14. }

       我们假设PrintWindow类有一个Qprinter类型的叫printer的成员变量。我们应该在printImage()函数中从栈上创建该Qprinter,但它可能在执行到另一个打印时无法记住用户的对上一次打印设置。
       我们创建了一个QprintDialog并调用了exec()来显示它。如果用户点击了它的OK按钮它返回true,否则返回false。在调用exec()之后,该Qprinter对象就准备好使用了。(也可以不用QprintDialog就能打印,通过直接调用Qprinter的成员函数设置参数)。
       下一步,我们创建一个在Qprinter上绘图的QPainter。我们把窗口设为该图象的矩形并以相同的比率把视口设置为一个矩形,我们在(0,0)位置绘制该图象。
       默认情况下,QPainter窗口是初始化过的,因此打印机看上去与屏幕的处理方法相似(一般为72和100点每英寸),这使得打印工作可以容易的重用物件绘图代码。这里并没有什么关系,因为我们设置了我们自己的容器。
       打印只有一页非常容易,但是许多程序都需要打印多页。为此,我们需要一次绘制一页,并调用newPage()进行到下一页。这产生了我们要决定每页打印多少信息的问题。在Qt中有两种主要方法能处理多页文档:
我们可以把数据转换为HTML并使用Qtextdocument渲染它,这是Qt的丰富文件引擎。
       我们可以手工绘图和分页。

       我们将依次预览这两种方法。作为一个例子,我们将打印一本花草手册:一系列花的名字,每个都有一个文本描述。手册中的每个入口都以"name:description"格式存储为一个字符串,例如:
Miltonopsis santanae: A most dangerous orchid species.

       由于每种花的数据被表示为一个单独的字符串,我们可以使用一个QStringList表示手册中的所有花。下面是使用Qt的丰富文本引擎打印花草手册的函数:

  1. void PrintWindow::printFlowerGuide(const QStringList &entries)
  2. {
  3.     QString html;
  4.     foreach (QString entry, entries) {
  5.         QStringList fields = entry.split(": ");
  6.         QString title = Qt::escape(fields[0]);
  7.         QString body = Qt::escape(fields[1]);
  8.         html += "<table width=\"100%\" border=1 cellspacing=0>\n"
  9.                 "<tr><td bgcolor=\"lightgray\"><font size=\"+1\">"
  10.                 "<b><i>" + title + "</i></b></font>\n<tr><td>" + body
  11.                 + "\n</table>\n<br>\n";
  12.     }
  13.     printHtml(html);
  14. }

       第一步是把该QStringList转换为HTML。每种花变成一个有两个单元格的表格。我们使用Qt::escape()把特殊字符'&', '<', '>'替换为相应的HTML实体("&", "<", ">")。然后再调用printHtml()打印这些文本:

  1. void PrintWindow::printHtml(const QString &html)
  2. {
  3.     QPrintDialog printDialog(&printer, this);
  4.     if (printDialog.exec()) {
  5.         QPainter painter(&printer);
  6.         QTextDocument textDocument;
  7.         textDocument.setHtml(html);
  8.         textDocument.print(&printer);
  9.     }
  10. }

       printHtml()函数弹出一个QprintDialog并小心地打印一个HTML文档。它可被重用于任何Qt程序以打印任意HTML页。
图8.13 使用QTextdocument打印花草手册

       把文档转换为HTML并用Qtextdocument来打印它是到现在为止打印报表和其他复杂文档的常用替代选择。在我们需要更多控制的时候,我们可以做出该页的布局并手工绘图。现在让我们看一下如何使用这种方法打印一本花草手册。下面是新的printFlowerGuide()函数:

  1. void PrintWindow::printFlowerGuide(const QStringList &entries)
  2. {
  3.     QPrintDialog printDialog(&printer, this);
  4.     if (printDialog.exec()) {
  5.  
  6.         QPainter painter(&printer);
  7.         QList<QStringList> pages;
  8.         paginate(&painter, &pages, entries);
  9.         printPages(&painter, pages);
  10.     }
  11. }

       在设置完打印机并构造了绘图器后,我们就调用paginate()帮助函数来决定哪一条应该出现在哪一页中。这产生的结果是一系列的QStringList,每个QStringList持有每页的条目。我们把此结果传递给printPages()。
       例如,假设花草手册包含6条,我们把它叫做A, B, C, D, E, and F。现在假设在第一页有放置A和B的空间,C、D和E放在第二页,F在第三页。Pages列表现在有索引位置为0的列表[A, B],索引位置为1的列表[C, D, E],索引位置为2的列表[F ]。

  1. void PrintWindow::paginate(QPainter *painter, QList<QStringList> *pages,
  2.                            const QStringList &entries)
  3. {
  4.     QStringList currentPage;
  5.     int pageHeight = painter->window().height() - 2 * LargeGap;
  6.     int y = 0;
  7.     foreach (QString entry, entries) {
  8.         int height = entryHeight(painter, entry);
  9.         if (y + height > pageHeight && !currentPage.empty()) {
  10.             pages->append(currentPage);
  11.             currentPage.clear();
  12.             y = 0;
  13.         }
  14.         currentPage.append(entry);
  15.         y += height + MediumGap;
  16.     }
  17.     if (!currentPage.empty())
  18.         pages->append(currentPage);
  19. }

       我们遍历这些条目并把它们追加到当前页,直到我们遇到一个放不下的条目,然后我们把当前页追加到pages列表并开始一个新页。

  1. int PrintWindow::entryHeight(QPainter *painter, const QString &entry)
  2. {
  3.     QStringList fields = entry.split(": ");
  4.     QString title = fields[0];
  5.     QString body = fields[1];
  6.  
  7.     int textWidth = painter->window().width() - 2 * SmallGap;
  8.     int maxHeight = painter->window().height();
  9.     painter->setFont(titleFont);
  10.     QRect titleRect = painter->boundingRect(0, 0, textWidth, maxHeight,
  11.                                             Qt::TextWordWrap, title);
  12.     painter->setFont(bodyFont);
  13.     QRect bodyRect = painter->boundingRect(0, 0, textWidth, maxHeight,
  14.                                            Qt::TextWordWrap, body);
  15.     return titleRect.height() + bodyRect.height() + 4 * SmallGap;
  16. }

       enTRyHeight()函数使用QPainter::boundingRect()来计算每个条目所需的垂直空间。图8.14展示一个条目的布局以及SmallGap 和 MediumGap常量的意义。
图8.14 一条花目的布局

  1. void PrintWindow::printPages(QPainter *painter,
  2.                              const QList<QStringList> &pages)
  3. {
  4.     int firstPage = printer.fromPage() - 1;
  5.     if (firstPage >= pages.size())
  6.         return;
  7.     if (firstPage == -1)
  8.         firstPage = 0;
  9.     int lastPage = printer.toPage() - 1;
  10.     if (lastPage == -1 || lastPage >= pages.size())
  11.         lastPage = pages.size() - 1;
  12.     int numPages = lastPage - firstPage +