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 概念运用到多种技术和操作系统中去。

>>> 阅读全文

 

, , , ,

C++ 设计模式之一 建造型模式

设计模式既不是某种静态解决方案,也不是某种算法。设计模式通过其名称(大多数是对目标的简单描述)来介绍或引入一些常见设计问题的重复解决方案或方法,或者说是解决一般性问题的常用方法。因此作为对具体实现的一种抽象,设计模式在设计阶段就十分有用。有了这个概念,在做设计选择时作为标准技术进行沟通,可以让团队成员更容易分享其设计理念。

根据所解决的设计问题,设计模式主要分为以下几大类:

  • 创建型模式 (Creational Patterns)
  • 结构型模式 (Structural Patterns)
  • 行为型模式 (Behavioral Patterns)

模式在 C++ 或 Java 这类面向对象的编程语言中十分常见。它可被视为模板,用来说明如何解决某个在不同场景或应用中反复出现的问题。设计模式不是代码重用,因为它通常没有具体代码,但通过设计模式轻松创建代码。面向对象的设计模式通常显示的是类或对象之间的关系和互动,而无需涉及应用最终呈现的类或对象。

每个设计模式由以下几个部分组成:

  • 问题或需求(Problem/Requirement):它阐述了所要解决的问题的需求。通常是在多个应用中都会遇到的常见问题。
  • 制约条件(Forces): 它描述了技术限制,用来帮助指导创建解决方案.
  • 解决方案(Solution): 它描述了介绍如何编码来解决上述问题。这是设计模式的设计部分。它可能包含类图
    时序图等等。

设计模式可以视为一个标准,是广泛认可的用来解决某个特定设计问题的最佳实践。在应用程序内采用良好的设计模式,可以大大加速你的设计工作,并有助于和其他程序员进行沟通,从而避免解决方案陷入繁琐晦涩的境地。

>>> 阅读全文

 

,

无锁双向链表

基于锁的同步机制总是受到象优先级反转(priority inversion )和死锁的困扰,即使这样,无锁同步还未引起重视。原因是要编写正确且高效的无锁代码实在困难1。本文演示了一个无锁双向链表的实现,实用且易于理解。第一个无锁双向链表是由 H°akan Sundell 实现的,本文中的方案利用了单字 (single-word) 的比较并转换(CAS — compare-and-swap)原子原语。在附录中提供了本方案与 H°akan Sundell 实现的链表的比较结果。 2

背景: 原子操作与 CAS
原子操作是指一系列必须整体完成的操作步骤,如果任何一步操作没有完成,那么所有完成的步骤都必须回滚,这样就可以保证要么所有操作步骤都未完成,要么所有操作步骤都被完成。在单核系统里,单个的机器指令可以看成是原子操作(如果有编译器优化、乱序执行等情况除外);在多核系统中,单个的机器指令就不是原子操作,因为多核系统里是多指令流并行运行的,一个核在执行一个指令时,其他核同时执行的指令有可能操作同一块内存区域,从而出现数据竞争现象。多核系统中的原子操作通常使用内存栅障(memory barrier)来实现,即一个 CPU 核在执行原子操作时,其他 CPU 核必须停止对内存操作或者不对指定的内存进行操作,这样才能避免数据竞争问题。3

因此在 X86 平台上,Intel 提供了在指令执行期间对总线加锁的手段。CPU 芯片上有一条引线 #HLOCKpin,如果汇编程序中的某条指令带了前缀 “LOCK”,CPU 执行到这条指令时就会 #HLOCKpin 的电位拉低直到该指令结束,从而将总线锁住,这样同一总线上其它 CPU 就暂时不能通过总线访问内存了,这样保证了指令在多处理器环境中的原子性。

所谓比较并交换 (CAS) 是在多线程中可用来获得同步的原子指令。CAS 操作包含三个操作数 —— 目标值(V)、原值(A)和新值(B)。CAS 指令会先将目标值 V 与原值 A 进行比较,如果二者相同,那么就可以新值 B 赋予目标值。

在 Windows 和 .NET 平台,由于历史原因,CAS 被写做 Interlocked API。原子操作在 Intel 架构 CPU 对应的汇编指令有 XCHG、CMPXCHG、INC 等,当然还得加上 LOCK 作为前缀。

>>> 阅读全文

 

, , ,

用 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,它是我收集的资料中最好的参考之一。

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

>>> 阅读全文

 

, , , , ,

C++ 11 中的多线程

文章评价:

第一个 C++ 标准发布至今已经过去十三年了,随着新的 C++11(或称 C++0X)标准的推出,C++ 标准委员会所做出的显著改变之一就是支持多线程编程。这是首次在 C++ 语言中为并行编程提供支持,而且与平台无关。

在 C++11 标准之前,编写多线程应用需要依赖于特定的平台扩展,如 Intel TBB、OpenMP、Pthreads 等等。现在有了 C++11 的标准线程库就方便了应用移植(例如 Windows 上的 C++11 多线程应用将很容易移植到 Linux 平台),另外对于熟知 Boost 线程库的用户来说,由于 C++11 标准库中许多命名与结构都和 Boost 相同,上手非常容易。1

C++11 标准库中的类可用于线程操作与同步、公用保护数据及低层次的原子操作。

C++11 标准中涉及多线程编程的主要有四个头文件,分别是:<atomic> ,<thread>,<mutex>,<condition_variable>和<future>:

>>> 阅读全文

 

, , ,

你应该了解的 10 个 C++ 11 的新特性

文章评价:

本文 1对一些 C++11 标准中的新功能进行了探讨。这里我建议所有 C++ 开发者应该学习和使用这些特性。C++ 11 增加了许多语言特性和标准库,本文涉及的不过是皮毛。不过,我相信其中的一些新特性应该为所有的 C++ 开发人员所熟知,并在日常工作中普遍使用。网上可以找到很多类似的文章,涉及不同的 C++11 特性。这里我尝试将一些应该经常使用的 C++11 新功能串联起来。

auto
在 C++98 标准中,auto 关键字就用在存储期规范中(Storage duration: 定义了对象在内存中的最小生存时间。最常见的存储期是自动存储期:在堆栈上分配的对象是自动分配和自动释放的)。在新的 C++11 标准中,其新用途是类型推断(Type inference)。auto 现在变成了类型占位符,它告诉编译器,应该从变量的初始化式中推断出它的实际类型。auto 关键字可以在不同的作用域中声明变量如:名称空间、块、for 循环语句的初始化声明等等。

auto i = 42;        // i 为整型
auto l = 42LL;      // l 为 long long 型
auto p = new foo(); // p 为 foo* 指针

使用 auto 意味着减少代码。(除非是 int 型,这样比 auto 要少输入一个字母)。回忆以前为了遍历 STL 容器元素,你会使用 typedef 来简化迭代器的编写,现在这种方法落伍了。请看:

std::map<std::string, std::vector<int>> map;
for(auto it = begin(map); it != end(map); ++it)
{

}

请注意,auto 不能被用作函数的返回类型,不过在函数有尾随返回值(trailing return type 2)的情况下,你可以用 auto 来代替函数的返回类型。此时, auto 告诉编译器不需要推断而只是直接在函数末尾查找返回类型。比如在下面的例子中,compose 函数的返回值就是操作符+ 的返回值,该操作符用于将类型 T1 和类型 T2 的值相加。

>>> 阅读全文

 

, ,

C++11 智能指针

文章评价:

许多程序员总避免使用指针,因为一旦处理不当,就会带来很多问题。所以新手程序员都不太喜欢指针。使用指针可能会引发许多问题,例如需确保指针所引用的对象的生命周期、虚引用(dangling references)和内存泄漏。

如果某内存块为多个指针变量所引用,而其中有个变量释放了这片内存,就会造成虚引用(dangling references)。另一种情况是,程序员从堆里申请了内存块,但未进行释放,这时则会造成内存泄漏。

某些资深程序员可能会说,你看我正确地从堆里获得了内存,进行相关操作,最后也正确地释放了内存, 这段代码干净漂亮,为什么还需要智能指针呢?

void Foo( )
{
    int* iPtr = new int[5];
    //manipulate the memory block
    .
    .
    .
    delete[ ] iPtr;    
}

的确,上面的代码目前工作正常,在理想情况下也能正确地释放内存。但如果考虑运行代码时的实际环境,在内存分配与释放之间存在着许多指令,这些指令可以做一些乱七八糟的事,比如访问无效的内存位置、除零错,甚至可能你的同事修改了你的代码,添加某种条件下的 return 语句。

>>> 阅读全文

 

,

C++11 一览 (二)

简介
在本系列文章中的第一部分,我们介绍了 C++11 的一些特性。在本篇文章,我们会继续介绍其它特性。

Strongly typed enums:

As quoted by Stroustrup “C enumerations constitute a curiously half-baked concept” and very few modifications are done to rectify there shortfalls resulting in manifestation of silent behavioral changes. Let’s check an example to support this,

namespace DefenceNetwork
{
   namespace WeatherMonitor
   {
     enum CycloneWarningLevels
     {
        GREY, // Medium winds
        RED, // High speed winds
        YELLOW, // Very High speed winds
        GREEN, // Severe Cyclone
        BLUE // Very Severe Cyclone
     };
   }
   namespace ThreatMonitor
   {
     enum AlertConditions
     {
        BLUE, // Lowest readiness
        GREEN, // Increased intelligence watch
        YELLOW, // Increase in force readiness
        RED, // Next step to nuclear war
        GREY // Nuclear war is imminent
     };
   }
 }
using namespace DefenceNetwork;
void SetDEFCONLevel(int value)
{
   // ….before setting level …lets check the weather once
   using namespace WeatherMonitor;
   // ……
   // …. Ok all is fine! Lets go ahead with setting DEFCON level
   if(value >= RED)
   {
      cout<<"Nuclear war is imminent…All Missiles GO…GO…GO"<<endl;
   }
   // Here, the confusion is AlertConditions::GREEN and CycloneWarningLevels::RED are same
   // as we messed up by putting ‘using namespace WeatherMonitor’.
 }
void main()
{
   using namespace ThreatMonitor;
   // Set level to Increase intelligence watch
   SetDEFCONLevel( AlertConditions::GREEN );
   
  // Oh no… what have I done …How the war is started
  // Hope in reality they have coded better than this…but hey can we take a chance !!!
}

The problem with the enums so far is that

1. They can be silently converted to int.
In the above example SetDEFCONLevel( ) method need int and when we pass an
enumerator it happily accepted.

>>> 阅读全文

 

,

C++11 一览 (一)

C++0x 现在成为了一个正式的标准,将以下称为 C++11。在2011年,它被批准成为ISO C++。本文的目的是鸟瞰大部分的 C++11 功能,并对那些已经添加进 VS2010 的功能做出深刻的分析。本文可以作为一个平台让您对个别功能开始进行全面的研究。

背景
几乎花了一个世纪的时间,查尔斯 · 巴贝奇(Charles Babbage)的分析机(Difference Engine)才演变成强大的计算机。在四十年代,受限于当时计算机的运算速度和内存,只能使用汇编级别的语言。十年后,事情开始有所变化,从 1950 年到 1970 年期间绽放出许多编程语言,其中许多一些延续到今天。

1979 年,为贝尔实验室工作的 Bjarne Stroustrup 开始着手加强”C”语言,首先添加了类,然后是虚函数、 运算符重载,多重继承、 模板和异常处理,以及其它功能。起初他管这个语言叫“带类的C -C with Classes”。在1983年更名为 c++ (+ + 可能说它是 C 的递增) 。

C++ 大事件/演化时间表如下:

  • 1983 – 第一个商业版本的 C++ 编译器
  • 1998 – C++ 标准委员会开始标准化 C++ [C++98]
  • 2003 – 推出不带任何新功能的补丁包 [C++03]
  • 2005 – 发布了“库技术报告- Library Technical Report 简称 TR1”
  • 2011 – 引入大量的功能并增强 C++ 标准库

我们可以看出,这个变化是最大的一次(好吧,添加 STL 也可以算是大变化)。

>>> 阅读全文

 

, , ,