2012年3月26日星期一

windows中的多线程处理

(一)建立新线程的函数


1.Win32 API函数 CreateThread
HANDLE WINAPI CreateThread(
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId
);


2.也可以用C运行库的函数 _beginthread 来创建新线程
uintptr_t _beginthread( 
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);


(二)线程间通讯方法:


1.消息通讯
可以在线程中用SendMessage发送自定义消息到指定窗口
LRESULT WINAPI SendMessage(
__in HWND hWnd,
__in UINT Msg,
__in WPARAM wParam,
__in LPARAM lParam
);


2.创建线程时可以传递一个结构体的指针
3.利用全局变量来通讯

对Window程序消息循环的理解



  • 1.每个(创建窗口的)线程有一个消息队列,程序用下面的循环接收、转换消息,并将消息分发到合适的窗口处理程序:



    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }



  • 2.windows系统负责把消息放入每个线程的消息队列或者直接调用合适的窗口处理程序




  • 3.Post消息和Send消息的区别



    a. Post一个消息(可以调用API函数PostMessage)表示消息将经过消息队列,通过消息循环来分发消息
    b. Send一个消息(可以调用API函数SendMessage)表示消息会跳过消息队列,操作系统直接调用窗口处理程序



  • 4.可以在任意线程中发送自定义的消息(消息值>= WM_USER)到指定的窗口




  • 5.消息队列


    操作系统维护一个系统消息队列并为每一个GUI线程维护一个线程消息队列。为了避免为非GUI线程创建消息队列的开销,所有的线程在最初创建时没有消息队列。仅当线程第一次调用特定的用户函数时系统才创建一个线程消息队列;没有任何GUI函数的调用会导致消息队列的创建。


  • 6.队列消息和非队列消息


    a. 队列消息

    移动或点击鼠标、敲击键盘等大部分消息为队列消息。
    仅当消息队列中没有其它消息时,WM_PAINTWM_TIMERWM_QUIT消息才被发送到窗口处理程序。
    其它的消息按先进先出的原则被发送到窗口处理程序
    用户可以用PostMessage等函数来发送一个队列消息

    b. 非队列消息

    比如当窗口转为active状态时,或者用户调用某些系统函数(如SetWindowPos)时,消息将跳过消息队列,直接发往窗口处理程序。
    用户可以调用SendMessage等函数来发送一个非队列消息


2012年3月24日星期六

C++ 字符串字面值的使用

以下语句将输出helloworld

//code1
char *a = "helloworld";

std::cout<

上面的a实际上是指向字符串字面值常量的起始地址,如果对a指向的字符进行赋值将会出现错误(比如 *(a+1) = 'E' 将出错)。
实际上应该像下面这样定义指针a(上面那样定义也能编译通过,但不推荐):

//code2
const char *a = "helloworld";


也可以用字符串字面值初始化字符数组,这样可以将字符数组当成字符串来使用:

//code3
char b[] = "helloworld";

std::cout<

注意上面是用字符串字面值来初始化字符数组b,数组b的长度为11,最后一位b[10] = '\0'. 可以直接在cout语句中输出b所存储的字符串的值,和code1类似。
但是请注意b为字符数组,数组b中的字符是可以改变的

//code4
char b[] = "helloworld";

b[0] = 'H';
std::cout<

上面代码将输出Helloworld,第一个字符由小写h修改为大写H。

稍微深入一点来说:
每个程序在内存中都有一块区域来存放常量、字符串字面值常量等。当用字符指针的方式来定义一个字符串时,实际上是将指针指向了字符串字面值常量的起始地址。
当用字符数组的方式来定义字符串时,程序首先开辟一段新的内存,然后将字符串字面值常量复制到这个位置。

可以用下面的程序来验证这点

#include

int main()
{
const char* p1 = "helloworld";
char array1[] = "helloworld";
const char* p2 = "helloworld";

std::cout<<" p1's address: "< <<"\n array1's address:"< <<"\n p2's address: "<
}


上面程序输出为:

p1's address: 13072640
array1's address:1899864
p2's address: 13072640


可以看到p1和p2指向相同的地址,而array1则为新的地址

2012年3月13日星期二

Boost中的Regex类库将部分中文识别为空白字符的问题原因及解决办法

问题描述:
使用字符类\s匹配一个字符串中的空白字符时,部分中文字符也将被识别为空白。

原因:
查看Boost的Regex源代码可以发现,Regex使用cpp_regex_traits类(在文件 boost/regex/v4/cpp_regex_traits.hpp 中定义)来处理字符类


// class cpp_regex_traits_base:
// acts as a container for locale and the facets we are using.
//
template
struct cpp_regex_traits_base
{
cpp_regex_traits_base(const std::locale& l)
{ imbue(l); }
std::locale imbue(const std::locale& l);

std::locale m_locale;
std::ctype const* m_pctype; //这个类用来识别空白符
//……
}


可以看到Regex使用std::ctype类来识别空白符等
ctype类有一个is成员函数用来判断某个字符是否属于空白字符或数字字符等,regex正是使用了这个成员函数。
查看这个成员函数的介绍可知字符类是在cctype头文件中定义的
cctype头文件定义了一系列函数来分类和转换单个字符,函数isspace来判断一个字符是否为空白字符
再来看函数isspace的说明,可知在C++中,指定了locale的此函数的模板版本存在于头文件中.
也就是说指定不同的locale,此函数的行为将不同。而默认的locale为"C" locale集。这就是问题的根源。

解决办法:1.使用[ \t\r\n]等特定的字符来代替\s
2.设置合适的locale并使用宽字符集和Regex的宽字符版本wregex

2012年3月7日星期三

Windows下编译和安装Boost库的指定模块

当然你可以使用www.boostpro.com提供的安装工具来安装,免去以下的步骤。但是那会安装许多不必要的文件,占用大量磁盘空间,有可能使你更迷惑。可参见这篇文章

如果你想了解如何一步步编译安装Boost库,请接着往下读。

一、到官网(www.boost.org)下载最新版Boost并解压到任意目录
先来看一下解压后的目录结构,以$BOOST_ROOT代表你Boost所在的目录

$BOOST_ROOT\ .................boost 根目录
index.htm .........A copy of www.boost.org starts here
boost\ .........................所有的Boost头文件
libs\ ............各种库的测试用例,文档说明。可以在这个文件夹中查看各种库怎么使用
index.html ........库文档从这里开始
algorithm\
any\
array\
…更多的库…
status\ .........................Boost范围测试套件
tools\ ...........一些有用工具, 比如 Boost.Build, quickbook, bcp
more\ ..........................政策等其它文档.
doc\ ...............所有Boost库文档的子集


二、编译需要的库
首先要说明的是大多数Boost库现在已经可以使用了,直接包含boost目录下相应的头文件即可,并不需要编译安装。

以下是必须编译安装的Boost库:


以下库拥有可选的编译组件(也就是说如果你要使用它们中的某些功能,则需要单独编译安装)

  • Boost.DateTime 当你使用它的 to_string/from_string 或者序列化特性, 或者目标平台为 Visual C++ 6.x 或 Borland.

  • Boost.Graph 仅仅当你想 处理 GraphViz 文件.

  • Boost.Math 含有TR1 和 C99 cmath 函数二进制组件.

  • Boost.Random 当你使用 random_device.

  • Boost.Test 可以 “header-only” 或 “separately compiled”
    模式使用, 尽管 对于郑重使用时推荐单独编译.



如果要编译上面的库,首先打开命令行,转到$BOOST_ROOT目录并运行目录下的bootstrap.bat脚本,这个脚本为Boost.Build系统的运行准备环境。

bootstrap.bat

可以看到现在$BOOST_ROOT目录下有b2.exe和bjam.exe两个可执行文件。这两个文件是一样的,只是名字不同,它们可以调用Boost.Build系统来创建上面那些需要编译安装的库。

比如我们想单独编译安装regex库,运行下面的命令即可

bjam stage --with-regex link=static threading=multi runtime-link=static

下面介绍相关参数的含义:
stage 仅创建和安装库文件(不创建头文件),可以用 --stagedir= 选项指定库的安装位置,默认安装在当前目录下的stage文件夹内。
--with- 创建和安装指定的库,如果使用了这个选项,则仅仅指定的库被创建,其它库不被创建。如果不指定这个选项,默认创建所有需要编译安装的库。
link=static指定生成静态regex库
threading=multi指定生成多线程库
runtime-link=static指定链接静态C和C++ 运行库

其他选项可参见这篇文章

三、配置开发环境
打开Visual Studio,在 工具->选项->项目和解决方案->VC++目录 条目下配置Boost包含文件和库文件目录,这样以后就不用每次都要配置Boost目录。

C++程序配置类库

一、INI文件读取库


除了使用Windows提供的API函数(见之前文章介绍)和BoostQTwxWidgets等类库提供的配置函数外,还可以使用下面的类库:

1.SimpleIni(code.jellycan.com/simpleini/)


一个跨平台的类库,提供简单的API读写INI文件,支持ASCII, MBCS and Unicode文件格式。开源,使用MIT协议。
使用方法:

CSimpleIniA ini;
ini.SetUnicode();
ini.LoadFile("myfile.ini");
const char * pVal = ini.GetValue("section", "key", "default");
ini.SetValue("section", "key", "newvalue");


2.RudeConfig(rudeserver.com/config/index.html)



// Create config object
//
Config config;


// load a configuration/.ini file
//
config.load("myfile.ini");


// read information
//
config.setSection("General Info");
double cost = config.getDoubleValue("Cost");
const char *company = config.getStringValue("Company Name");


// create information
//
config.setSection("new section");
config.setStringValue("animal type", "giraffe");
config.setBoolValue("mammal", true);

// save changes
//
config.save();


3.minIni(code.google.com/p/minini/)


minIni是一个在嵌入式系统中使用的读取INI文件库。minIni占用极少资源,可以为各种文件IO库配置。

二、XML文件读取库


1.TinyXML-2(grinninglizard.com/tinyxml2docs)


如名字所示,是简单的XML文件读取库,并且是第二版。仅包含一个.h文件和一个.cpp文件,直接和自己的源代码一起编译即可使用。


三、序列化文件(类似JSON格式)读取库


1.libconfig(hyperrealm.com/libconfig/)


Libconfig是一个简单的用来处理结构化配置文件的库,像这样的文件:test.cfg。这种文件格式比XML更紧凑易读。不像XML,它是可识别类型的,所以在程序代码中不必做字符串解析。

int var1;
double var2;
const char *var3;

if(config.lookupValue("values.var1", var1)
&& config.lookupValue("values.var2", var2)
&& config.lookupValue("values.var3", var3))
{
// use var1, var2, var3
}
else
{
// error handling here
}