Enterprise Just Builder

Solution for Enterprise Software Architecture

C/C++类库

将 Win32 枚举型 APIs 转换为 STL 迭代器

原文1

操作系统都有许多 API 用于存取实体集合的,比方说 Unix 的 opendir()/readdir() 函数。Win32 API 也提供了许多函数用来枚举集合内的元素,通常采用的模型无外乎下面几种:回调函数 (如 EnumChildWindows), get-first/get-next (取第一个/取下一个 比如FindFirst/NextFile), 或 get-nth (取第n个 如 RegEnumValue) — 其语义和前二者完全不同。

现在 C++ 社区正在逐步朝着符合 STL 的通用编码格式迈进,如使用容器 (序列和关联)、迭代器、泛函 (或函子或函数对象)、算法和适配器等等。这一做法的最大好处是可以采用一种通用的方法操纵不同的实体, 大大减少开发者的工作量, 同时提高健壮性、可维护性和重用。

STL 技术未被广泛运用的原因之一是除了标准库所提供的类和函数 (list, vector, for_each 等) 之外, 缺少可用的 STL 兼容库, 特别对于操作系统 API。原因之一是这涉及编程模型之间迁移转换的复杂性, 特别是对于集合和序列。本文通过将两种 Win32 枚举模型实际转换 STL 序列, 创建封装了 Win32 API 的轻量级序列类, 并提供了可按 STL 标准算法来操纵枚举实体的迭代器。

本文所演示的类是 WinSTL 库的一部分,WinSTL 则是 STLSoft 的子项目。STLSoft 是个开源组织,一直致力于将 STL 概念运用到多种技术和操作系统中去。

>>> 阅读全文

 

, , , ,

Microsoft Bond C++ 使用手册

文章评价:

Bond 是微软开发的数据结构化(schema)处理框架,它支持跨语言的序列化与反序列化,支持强大的泛型机制,因此能够对数据进行有效地处理。该框架在微软公司内部的高扩展服务中得到了广泛的应用。Bond 可适用于服务间通信、大数据存储和处理等诸多应用场景。

Bond 定义了一个富类型系统和一套 schema 版本控制规则以提供前后向的兼容性。Bond 的核心功能包括高性能的数据序列化/反序列化和一个强大的通用数据转化机制。通过可插拔的序列化协议、数据流和用户自定义类型别名(user defined type aliases)等机制,由此 Bond 实现了高可扩展性。

目前该项目已经基于宽松的 MIT 许可开源在了 GitHub 上,当前版本支持 C++、C# 和 Python,可运行 在Linux、OS-X 和 Windows 平台上。Bond 的编译器完全是使用 Haskell 编写的。

Bond 与其他序列化系统具有很多相似性,例如 Google Protocol Buffers (ProtoBuf)、Thrift 以及 Avro等等。和它们相比,Bond 有以下特点:

>>> 阅读全文

 

, , ,

C++ 模板编程入门 (一)

于天生的畏惧,大多数 c++ 程序员都尽量远离模板技术,反对的声音有:

  • 模板难以学习和运用
  • 编译时的错误信息模糊又冗长
  • 不值得花费精力

我承认模板都有点难以学习、理解和运用。但是从模板中获得的益处会多于其负面影响。许多泛型函数或类都可以被模板所包装。我后面会加以解释。

虽然从技术上来看 c++ 模板与 STL (标准模板库) 是同门兄弟。不过在本文中,我将只涵盖模板的核心部分。在下一篇文章中,我会阐述更高级且有趣的模板技术以及使用 STL 的一些诀窍。

语法
你可能已经知道,大部分模板都使用尖括号:小于号(<)和大于号(>),其使用形式总是如下所示:

< Content >

这里的 Content 可以是:

>>> 阅读全文

 

, ,

用 libclang 实现代码生成器

文章评价:
代码生成器对大型 C++ 项目而言是非常有用的工具。由于语言层面上缺乏自省(introspection)机制,要实现反射、脚本绑定(script binding)及序列化都需要编写代码样板(boilerplate) 以保留相关信息,否则在编译时这些数据会全部被丢弃。大部分解决方案要嘛是侵入式的(严重依赖于宏 — Macro,结果难以调试且要求怪异的语法声明)要嘛非常脆弱(fragile)(要根据实际代码不断更新样板,常常没有任何警告就中止运行)。提高鲁棒性的方法之一是自动生成这类样板。要实现这一目标,需要用某种方式解析代码,换句话说,需要了解什么信息该被保留。然而,解析 C++ 本身就是极其复杂的工作,要考虑各种怪异情况,如果真想这样做,消费的时间可能相当漫长。 1

即使只针对 C++ 某个“差堪可用”的子集进行解析,这类尝试要嘛失败了要嘛对编码规则有严格要求。也就是说,要求避免使用解析器无法理解的语法 — 一旦有人不按规矩提交代码解析器就会中止工作。要解决这个问题,我发现 LLVM 项目中有一个很好的工具:libclang2。由于 CLANG C++ 前端 和 libclang 调用的代码是相同的,所以它了解 C++ 的一切。该类库的最新发布可以支持 C++1x(如不出意外将是 C++14)的功能。唯一略有欠缺的是文档方面:其官方文档不过是 Doxygen 生成的参考,虽然有用,但不足以介绍如何使用该类库;加上问题本身的复杂性,学习曲线将十分陡峭。

本文要讲述的是如何用 libclang 及其 Python 绑定来实现一个实用的 C++ 代码生成器。在某种意义上,“实用” 意味着这不是一个通用的解决方案。这里所演示的不是某种具体的实现,而是一种解决问题的思路、一个详细的范例。借助这些想法,你可以自己创建通用的反射方案。本文的另一设计原则是尽力减少代码侵入以便能按自然 C++ 语法编写代码。同时我也鼓励读者自己动手,从头开始尝试实现自己的代码生成器。

我必须感谢 Eli Bendersky 的有关帖子3,它是我收集的资料中最好的参考之一。

准备工作
要读懂本文,读者可能需要具备以下知识点:

>>> 阅读全文

 

, , , , ,

GPerf 和完美哈希函数

GNU Gperf 工具用来生成一种 “完美的” 散列函数(perfect hash function),可以将用户提供的一组特定字符串生成散列表、散列函数和查找函数的 C/C++ 代码。 所谓完美散列函数或完美 Hash 函数,指存在这样一个 hash 函数 F,可以将一个含有 n 元素的用户特定关键字集合 N 到集合 W, N 的关键字被唯一映射到 W 的 0..k-1 范围,其中 k>=n。如果 k=n 那么 F 就是“最小化完美 hash 函数”。

最小化完美 hash 函数具有两个特性:

  • 只需要执行一次查找就能完成静态搜索结构中的关键字识别,即所谓的“完美”。
  • 为保存关键字所分配的内存刚刚可以容纳关键字集合,不会多余。即“最小化”。

Gperf 被普遍地用在多种商业编译器、研究型编译器、语言处理工具的词法分析器上,用来生成关键字识别器,包括:GNU C, GNU C++, GNU Pascal, GNU Modula 3 和 GNU indent 等等。Gperf 将从用户提供的文件中(即关键字文件,通常使用 .gperf 作为扩展名,但不做强制要求)生成 C/C++ 源代码。所有代码被定向到标准输出,然后必须重定向到类似下面的文件:

gperf  -L C++ keyfile.gperf > perfecthash.hpp

注意: -L 选项将指示 gperf 生成 C++ 代码。

Gperf 会生成一个 0..K 元素的静态查找结构和一对 C 函数的源代码。利用这些函数只需要一次查找,就可以判断给定的字符串 S 是否在集合 W 中。这两个函数是:

>>> 阅读全文

 

, , ,

OpenCV 使用手册 (一) 介绍OpenCV

OpenCV简介
OpenCV 是 Open Source Computer Vision Library 的缩写, 它于 1999 年由 Intel 建立,现在由 Willow Garage 提供支持。OpenCV 是一个基于 BSD 许可证授权(开源)发行的跨平台计算机视觉库,可以运行在 Linux、Windows 和 Mac OS 操作系统上,是个轻量级而且高效的类库 ,由一系列 C 函数和少量 C++ 类构成,同时提供了 Python、Ruby、MATLAB 等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。最新版本是2.3。你可以访问官方站点以获得更多帮助。

编译
OpenCV 的官方站点提供了二进制代码的下载,但在某些场合,比如需要给源代码打补丁,这时候你可能需要从头开始编译 OpenCV 以便能得到修正的二进制代码。 现在 OpenCV 发布版本都提供了跨平台的自动化构建工具 Cmake 的方式,可以很方便地生成各种平台的 makefile。这里以 64 位 Windows 平台的的 VC++ 1.0 为例说明。

准备工作
为了编译 64 位的 OpenCV,我们需要安装的工具有 MinGW64 (GCC 4.7.2), CMake 和 Python 2.7.3(64位),这些工具主要是用来构建 Numpy 和 FFMPEG 的,后续阶段的 OpenCV 的编译依赖于这些类库。

Numpy 是 Python 下的一个基础科学的计算包,提供了矩阵、线性代数、傅立叶变换函数等等,Numpy 的编译需要用到线性代数工具包,它可以兼容于 BLAS/LAPACK,或者 ATLAS,MKS 等等实现,这里我们选用 netlib 提供的基本线性代数子程序 BLAS 和线性代数程序包 LAPACK,版本是3.3.1。编译 BLAS/LAPACK 只需要在 MSYS 中运行:

cmake -G "MSYS Makefiles"
make

编译后可以得到两个文件: libbas.a 和 liblapack.a,两个库文件拷贝到 MSYS 中的 /usr/local/lib 目录中即可。

>>> 阅读全文

 

, , , , ,

用OpenMP开发并行程序

OpenMP 是一套可于共享内存并行系统的多线程程序设计的指导语句(Compiler Directive),它最早是由 OpenMP Architecture Review Board 牵头提出的,现在OpenMP为许多厂商所支持,因此具有较高的可移植性。支持 OpenMP 的编程语言包括 C 语言、C++ 和 Fortran 等等,支持 OpenMP 的编译器包括 Sun Compiler,GNU Compiler 和 Intel Compiler,MS VC++ 等等。

OpenMP 提供了对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的 pragma 来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。如果选择忽略这些 pragma,或者编译器不支持 OpenMP 时,程序又可退化为通常的程序(一般为串行),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。

OpenMP 提供的这种对于并行描述的高层抽象降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现细节。对基于数据分集的多线程程序设计,OpenMP 是一个很好的选择。同时,使用 OpenMP 也提供了更强的灵活性,可以较容易的适应不同的并行系统配置。线程粒度和负载平衡等是传统多线程程序设计中的难题,但在 OpenMP 中,库函数从程序员手中接管了这两方面的部分工作。

但是,作为高层抽象,OpenMP 并不适合需要复杂的线程间同步和互斥的场合。OpenMP 的另一个缺点是不能在非共享内存系统(如计算机集群)上使用。在这样的系统上,MPI 使用较多。

OpenMP 的架构
OpenMP 是作为共享内存系统的标准问世的。它是为在多处理器机上编写并行程序而设计的一个应用编程接口。它包括一套编译指导语句和一个用来支持它的函数库。

>>> 阅读全文

 

, , ,

VC++中多字节字符集和Unicode之间的互换

在Visual C++.NET中,默认的字符集是Unicode,这和Windows默认的字符集是一致的,不过在老的VC6.0等工程中,默认的字符集形式是多字节字符集(MBCS:Multi-Byte Character Set),这样导致在VC6.0中非常简单实用的各类字符操作和函数在VS2005环境下运行时会报各种各样的错误,这里总结了在Visual C++.NET2005环境中Unicode字符集下CString和char *之间相互转换的几种方法,其实也就是Unicode字符集与MBCS字符集转换。

1.Unicode下CString转换为char *
方法一: 使用API:WideCharToMultiByte进行转换

CString str = _T("你好,世界!Hello,World");
//注意:以下n和len的值大小不同,n是按字符计算的,len是按字节计算的
int n = str.GetLength();  
//获取宽字节字符的大小,大小是按字节计算的
int len = WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),NULL,0,NULL,NULL);
//为多字节字符数组申请空间,数组大小为按字节计算的宽字节字节大小
char * pFileName = new char[len+1];   //以字节为单位
//宽字节编码转换成多字节编码
WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),pFileName,len,NULL,NULL);
pFileName[len+1] = \0;   //多字节字符以’\0’结束

方法二:使用函数:T2A、W2A

CString str = _T("你好,世界!Hello,World");
//声明标识符
USES_CONVERSION;
//调用函数,T2A和W2A均支持ATL和MFC中的字符转换
char * pFileName = T2A(str);  
//char * pFileName = W2A(str); //也可实现转换

注意:有时候可能还需要添加引用#include
2、Unicode下char *转换为CString

方法一:使用API:MultiByteToWideChar进行转换

>>> 阅读全文

 

,

在C/C++中使用LIBXML2

文章评价:
C/C++ 标准库中并没有提供任何操作 XML 的方法,因此必须借助第三方函数库,您可以使用免费的诸如 Microsoft MSXML SDK 的商业版软件,也可以使用开源实现 expat 和 libxml2,在xmlbench上罗列了一些常见的 XML 类库及其性能比较,可以供您参考选择。这里我们主要介绍 libxml2,libxml2 是一个 C 语言的 XML 程序库,它可以包括 Windows, Linux, Mac 等等多种操作系统上使用,不但支持 DOM 和 SAX2 等等标准 XML 的解析方法,还从 C# 中借鉴了 XmlTextReader 这种简洁易懂的 pull 解析方式,此外 libxml2 支持 XPATH 查询,并且部分支持 XSLT 转换,在许多著名函数库中都有 libxml2 的身影,比如 Glib 等等。

libxml 的官方地址是XmlSoft,您可以在上面下载最新的源代码。接下来,我们先将源代码编译成平台所需的二进制文件。

编译 libxml2
在 Linux 或者 Windows 上使用 GCC 的读者可以参考用MinGW64编译Readline GetText等类库一文。这儿我们介绍一下 VC++ 的编译方法。Libxml2 依赖于 iconv 和 zlib 库。libiconv 库是一个字符编码转换工具,它提供了一个iconv()的函数,以实现一个字符编码到另一个字符编码的转换。分别下栽这些源代码,包括Zlib 1.2.5, libiconv 1.11.1 和 libxml2 2.7.8。

首先编译 libiconv,需要用 admin 权限打开 VC++ 的命令提示窗口,然后运行:

nmake -f Makefile.msvc NO_NLS=1 MFLAGS=-MT

如果需要成共享库的话,可以在参数中的添加上”DLL=1”。编译结束之后,运行 nmake -f Makefile.msvc install,默认会在 C 盘创建 usr 目录,保存得到的头文件、库文件和二进制文件。

>>> 阅读全文

 

, , , , , ,

Libffi的使用及其范例

文章评分:

LibFF I与调用约定

“FFI” 的全名是 Foreign Function Interface,通常指的是允许以一种语言编写的代码调用另一种语言的代码。而 “Libffi” 库只提供了最底层的、与架构相关的、完整的”FFI”,因此在它之上必须有一层来负责管理两种语言之间参数的格式转换。

高级语言编译器产生代码时都会依据一系列的规则,这些规则十分必要,特别是对独立编译来说。其中之一是“调用约定” (Calling Convention),它包含了编译器关于函数入口处的函数参数、函数返回值的一系列假设。它有时也被称作“ABI”(Application Binary Interface)。调用约定(Calling Conventions)定义了程序中调用函数的方式,它决定了在函数调用的时候数据(比如说参数)在堆栈中的组织方式。

通常来说函数调用要用到的两条基本的指令:”CALL”指令和”RET”指令。”CALL”指令将当前的指令指针(这个指针指向紧接在CALL指令后面的那条指令)压入堆栈,然后执行一条无条件转移指令转移到新的代码地址。”RET”是与”CALL”指令配合使用的指令,在绝大多数函数中它是最后一条指令。”RET”指令弹出返回地址(就是早些时候”CALL”指令压入堆栈的地址)并将其加载到”EIP”寄存器中,然后从这个地址开始继续执行。

>>> 阅读全文

 

, , , , ,

用MinGW64编译GTK-DOC

Linux 下有太多重复的组织文档的方式(比如 man,doxygen),对大部分用户而言,GTK-DOC 基本就是鸡肋,我们可以把它给忽略。但是如果用 GIT 下载过 Gnome 一些源代码包,很大机会你会遇到需要运行 gnome-autogen.sh 这个命令(gnome-autogen.sh在 gnome-common 这个包中)来生成 configure 文件,这时候系统就会提示 Gnome-autogen.sh 依赖于 GTK-DOC。因此你不得不编译 GTK-DOC 来解决这个依赖性问题。

GTK-DOC 的编译秉承了 Unix/Linux 的许多命令行工具的缺点– 繁琐、难用、而且基本就没啥用,只是为了折腾人。所以在这里详述 GTK-DOC 的编译过程,以解决这个让人厌恶的依赖问题。(这种依赖性在 Gnome 的包里比比皆是,比较神经质的设计)。

首先 GTK-DOC 需要用到 XMLCATALOG 这个命令,xmlcatalog 存在于 libxml2 中,所以你首先需要编译 libxml2,详细过程你可以在用MinGW64编译Readline GetText等类库一文找到。

GTK-DOC 还需要 JADE 等等依赖包,但由于我们基本不需要 GTK-DOC 的功能,这里只需要下载两个非要不可的 DTD 就可以了:就是 Docbook 4.3 DTDDocbook-XSL-DOC-1.171.1

接下来需要把这两个包安装到 xml catalog 里,这也是个庞杂混乱的过程,为此我写了两段脚本,将其拷贝到相应的源代码目录中,在 MSYS 中运行这两段脚本就简单完成: 和 。

>>> 阅读全文

 

,

将嵌入汇编迁移到Windows 64位平台

文章评价:
在 X86 平台上的 Visual C++ 有一个很有用的特性就是,你可以在 C++ 代码中,直接编写汇编语句,而无需外部的汇编工具(如 MASM )。不幸的是,在 X64 和 IA64 平台上,这项特性为微软所抛弃。如果你需要将手上的 32位代码迁移到 X64 或者 IA64 上来,你不得不寻找一些替代的方法,这些方法包括:

  • 将既有的汇编代码修改并保存到独立的文件中,然后用 MASM64 来编译
  • 使用微软的 Compiler intrinsics,它提供了一些类似汇编的功能。
  • 用 C++ 和 Windows API 重写

将汇编代码提出到独立的文件中。
这里有个很简单的嵌入汇编的例子,我们在 C 函数中调用 FPU 指令来完成开方函数的功能。在 C 函数出口处,VC++ 编译器会自动添加复栈指令,所以不必自己编写。那反而会使系统混乱。

int quick_sqrt(__int64 params)
{
        int result=-1;
        __asm
        {
                fild params
                fsqrt
                fistp result
        }
        return result;
}

这个函数等同于下面这个 Naked 函数

__declspec(naked) int quick_sqrt(__int64 params)
{
        int result;
        __asm
        {
                push ebp
                mov ebp, esp
                sub esp, __LOCAL_SIZE

                fild params
                fsqrt
                fistp result
                mov eax, result

>>> 阅读全文

 

, , ,

GTK+ 3.0编程 (一) 编译GTK+3.1.6

文章评分:

GTK 最早是作为另一个著名的开放源码项目 —— GNU Image Manipulation Program (GIMP) —— 的副产品而创建的,这也是 GTK 名称的由来,即 GIMP Toolkit。当这个工具包获得了面向对象特性和可扩展性之后,在名称后面加上了一个加号。全世界各地的程序员已经用 GTK+ 编写了成百上千的应用程序,包括 Linux 下两个主要的桌面环境(Xfce 和 GNOME)。

GTK+ 据说具有简单易用,具有设计良好和可扩展性强等特点(这些特点相对 QT 真的是很值得怀疑),而且 GTK+ 是可移植的。这意味着用户编写出既可以在 Windows 上,又可以在 Linux 或 Unix 上运行的程序。在本文中,我们将利用 MinGW64 在 Windows 平台上编译出最新推出的 GTK+ 3.1.6,并用其开发出漂亮的 GUI 应用。其源代码在Linux 平台表现也是一致的。

GTK+ 架构
在编译 GTK+ 庞杂的软件包之前,我们需要对 GTK+ 有所了解, 比如这些软件包是如何组合成 GTK+ ?这些库文件是如何堆叠的?GTK+ 和 Glib,Pango 的关系如何等等诸如此类的问题。图一显示了 GTK+ 的一个整体架构,我们下面对每一个部分稍做解释。

Glib是一个通用的工具类库,它提供了类型定义、高级的数据结构(比如:linked list,hash tables,binary trees)、线程、宏、警告信息和断言、类型系统和对象系统等等。其中GObject是对象系统的类库,通常会编译成独立的共享库文件,但其源代码随着Glib代码树发布。在 GObject 内部有一个动态的通用类型系统,即 GType,它在非对象的类型比如 opaque pointers, strings, variously sized integers and floating point numbers 之外提供一个单根继承的类结构。

>>> 阅读全文

 

, , ,

在 Eclipse 中集成 MinGW64 开发环境

文章评价:

本文主要讨论如何集成 Eclipse 和 MinGW64 来进行 C/C++ 应用程序的开发。由于不同版本 Eclipse CDT 的稳定性表现不一,因此需要你合理选择 Eclipse 的版本,现在已知 Helios (Eclipse 3.6) 的 CDT 在开发中会频繁崩溃,不建议使用。本文选择的是 Eclipse 4.3.1 来讲解,其他版本方法基本相同。

GCC编译器
首先要做的是下载一个合适的 MinGW64 编译器。要开发并运行 64 位的 Windows 应用,你可以选择 32 位的 GNU C/C++ 编译器并通过交叉编译来产生 64 位应用, 也可以选择纯 64 位的编译器。在 MinGW64 的官方站点上提供了许多不同的版本,你可以在 EJB.CC 下载到 GCC 4.6.1 for Windows ,或者在SourceForge.net上选择最新的版本。

同时还需用到 MSYS(在 MinGW 的站点上提供),MSYS 提供一些常见的 Linux 命令工具用于 Windows GCC 的编程开发,目前只有 32 位版本,不过不会对代码生成有任何影响。

接下里要做的就是为 Eclipse 安装最新的 CDT,目前 Galieo 只支持 CDT 6.0,Helios 则支持 CDT 7.0,而 Indigo 则支持 CDT 8.0,以此类推。如果你下载的 Eclipse 并未集成 CDT,你可以从 “Help” 菜单下选择 “install new software”,之后在 “avaiable software” 中的 “programming languages” 中选择各个 C++ 子项并安装。

>>> 阅读全文

 

, , , , ,

轧名规则、修饰名、调用约定及其他

[b]一、轧名Name mangling和修饰名Decoration name[/b]

在现代编程语言中,许多情况下需要解析程序实体的唯一既定名称,轧名(Name mangling)(又叫命名修饰)是解决这些问题的一种手段。它在函数、结构、类或者其他数据结构的名称中加入额外的信息编码,因此能从编译器传递更多的语义信息给链接器。

轧名允许编程语言在不同的名称空间(不同的作用域)中为不同的实体提供同名的标识符,而不会有命名的冲突。

编译器产生的目标代码(object code)通常会和其他的目标代码相链接,这通常是由链接器来完成的。链接器需要知道每个程序实体的许多信息。例如,为了成功链接一个函数,需要知道的函数的名字、参数的数目、类型等等。

轧名解决了现代编程语言中的很多问题,比如重载等等。它为连接器提供了额外的信息,在编译器和连接器之间传递信息。

>>> 阅读全文

 

Previous Posts