c语言 模板元编程,模板元编程(1):选择API

C与C++ API的比较

在c语言中,API体现为c函数,如操作系统提供的一系列API,在c++中,API体现为自由函数,这里的自由函数是指除普通成员函数、静态成员函数、友元函数外的能在某命名空间作用域或全局空间内直接访问的函数,而这更多地体现为函数模板,如stl提供的一系列算法swap、count和sort等。相对于c API,c++ API具有类型安全和封闭开放的优点,类型安全是因为c++本身就是一种比c更强的静态类型语言,而封闭开放是指函数的设计实现一部分是固定的,而另一部分可以是灵活扩展的,这表现为函数模板的重载和全局特化。本文主要讲述如何运用函数模板来设计应用程序API,并以windows平台为例说明。Windows双版本API

在windows中,很多API通常都有ANSI和UNICODE两种字符集形式,其命名对应为xxxA和xxxW。如果应用层需要针对这些API来封装,为完备起见,就需要考虑ANSI和UNICODE两种版本。一般有两种方法:第一种是先实现一个A(或W)版本,而W(或A)版本的实现则是在其内部将UNICODE(或ANSI)型数化转化为ANSI(或UNICODE)类型,再调用A(或W)版本,这种方法因需要作字符集的转换来实现,因而效率较低;第二种是两个版本平行实现,即A版本调用系统A版本API实现,W版本调用系统W版本API实现,这种方法的缺点是结果产生除了A或W API调用不同外很多的重复代码。在A和W版本都实现后,进一步,可根据编译器的宏定义_UNICODE或UNICODE来定义一个自己的API宏。那么除以上两种方法外,还有没有更好的方法呢?而这种方法必然要能够兼顾效率和避免代码的重复冗余。在使用这个方法前,有下列几个问题:1)如何根据泛型参数来选择定义正确的结构体,因为有些系统API的参数中不仅字符串类型有A和W两种类型,而且凡是其内部包含字符串类型的结构体因而也带有A和W两种类型。2)如何根据泛型参数来选择调用正确版本的系统API。

针对第1个问题,泛型参数通常只有A或W两种,因此可以使用选择特征类模板来实现,如boost中的if_c,softstl中的select_first_type类模板,也可以自己实现这样的类模板。对第2个问题,与第1个问题不同的是,它是选择函数而不是类型,本质上就是选择变量,因此需要实现一种基于类型或非类型参数选择变量的模板,并且使用方式如下select_variable(xxxA,xxxW)(arg1,arg2,...,argN)

flag是一个布尔非类型模板实参,当值为true时表示选择返回xxxA,反之选择返回xxxW,然后接下来跟着一系列参数,表示调用对应的A或W版本API,因此select_variable应该实现为函数模板比较方便,若实现为类模板(关于实现可以参考boost中的function),则需要显式指定函数返回值和参数类型,这样一来,当函数参数过多,就是一个噩梦了,因为模板实参演绎不能用于类模板及其构造函数,只能应用于其成员函数模板。

综上分析解决,下面给出select_variable的实现与应用。

select_variable实现

使用类模板特化与函数模板重载技术

1

cbef093dcc044b2793832001e2365e43.png#defineTEMPLATE_BOOL_TRAIT_DEF1(trait,T,c)\2cbef093dcc044b2793832001e2365e43.pngtemplate\3cbef093dcc044b2793832001e2365e43.pngstructtrait\42f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{\5df37983f39daa189b8c814e01a6a9011.pngstaticconstboolvalue=c;\60ac3a2d53663ec01c7f7225264eeefae.png};\7cbef093dcc044b2793832001e2365e43.png8cbef093dcc044b2793832001e2365e43.png#defineTEMPLATE_BOOL_TRAIT_SPEC1(trait,sp,c)\9cbef093dcc044b2793832001e2365e43.pngtemplate<>\10cbef093dcc044b2793832001e2365e43.pngstructtrait\112f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{\12df37983f39daa189b8c814e01a6a9011.pngstaticconstboolvalue=c;\130ac3a2d53663ec01c7f7225264eeefae.png};\14cbef093dcc044b2793832001e2365e43.png15cbef093dcc044b2793832001e2365e43.pngtemplate16cbef093dcc044b2793832001e2365e43.pngstructselect_type;17cbef093dcc044b2793832001e2365e43.png18cbef093dcc044b2793832001e2365e43.pngtemplate19cbef093dcc044b2793832001e2365e43.pngstructselect_type202f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{21df37983f39daa189b8c814e01a6a9011.pngtypedef T1 type;220ac3a2d53663ec01c7f7225264eeefae.png};23cbef093dcc044b2793832001e2365e43.png24cbef093dcc044b2793832001e2365e43.pngtemplate25cbef093dcc044b2793832001e2365e43.pngstructselect_type262f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{27df37983f39daa189b8c814e01a6a9011.pngtypedef T2 type;280ac3a2d53663ec01c7f7225264eeefae.png};29cbef093dcc044b2793832001e2365e43.png30cbef093dcc044b2793832001e2365e43.pngtemplate31cbef093dcc044b2793832001e2365e43.pnginline typename select_type::type select_variable(T1 t1,T2 t2)322f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{33df37983f39daa189b8c814e01a6a9011.pngreturnselect_variable_impl(typename select_type::type(),t1,t2);340ac3a2d53663ec01c7f7225264eeefae.png}35cbef093dcc044b2793832001e2365e43.png36cbef093dcc044b2793832001e2365e43.pngtemplate37cbef093dcc044b2793832001e2365e43.pnginline T1 select_variable_impl(int,T1 t1,T2 t2)382f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{39df37983f39daa189b8c814e01a6a9011.pngreturnt1;400ac3a2d53663ec01c7f7225264eeefae.png}41cbef093dcc044b2793832001e2365e43.png42cbef093dcc044b2793832001e2365e43.pngtemplate43cbef093dcc044b2793832001e2365e43.pnginline T2 select_variable_impl(long,T1 t1,T2 t2)442f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{45df37983f39daa189b8c814e01a6a9011.pngreturnt2;460ac3a2d53663ec01c7f7225264eeefae.png}47cbef093dcc044b2793832001e2365e43.png48cbef093dcc044b2793832001e2365e43.pngTEMPLATE_BOOL_TRAIT_DEF1(is_ansi_char,T,false)49cbef093dcc044b2793832001e2365e43.pngTEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,char,true)50cbef093dcc044b2793832001e2365e43.pngTEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,charconst,true)51cbef093dcc044b2793832001e2365e43.pngTEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,charvolatile,true)52cbef093dcc044b2793832001e2365e43.pngTEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,charconstvolatile,true)53cbef093dcc044b2793832001e2365e43.png54cbef093dcc044b2793832001e2365e43.png#undefTEMPLATE_BOOL_TRAIT_DEF155cbef093dcc044b2793832001e2365e43.png#undefTEMPLATE_BOOL_TRAIT_SPEC1

select_variable应用

有了select_variable,封装设计API就方便多了,在函数模板中,类型的参数化体现在其参数、返回值和内部实现三方面,下面就从这三方面来说明其应用:

参数类型化

IsDirectoryOrFile根据路径来判断是否为目录或文件,对于调用方来说,可以灵活指定A或W版本的字符串路径。

1

cbef093dcc044b2793832001e2365e43.pngtemplate2cbef093dcc044b2793832001e2365e43.pnginlineintIsDirectoryOrFile(constcharT*path)32f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{4df37983f39daa189b8c814e01a6a9011.pngDWORD dwFlag=select_variable::value>(GetFileAttributesA,GetFileAttributesW)(path);5df37983f39daa189b8c814e01a6a9011.pngif(INVALID_FILE_ATTRIBUTES==dwFlag)6df37983f39daa189b8c814e01a6a9011.pngreturn0;7df37983f39daa189b8c814e01a6a9011.pngreturn(dwFlag&FILE_ATTRIBUTE_DIRECTORY)?1:2;80ac3a2d53663ec01c7f7225264eeefae.png}

返回值类型化

GetExePath获取当前应用程序的路径,对于调用方来说,可以灵活指定想要返回A或W版本字符串表示的路径。

1

cbef093dcc044b2793832001e2365e43.pngtemplate2cbef093dcc044b2793832001e2365e43.pnginline std::basic_stringGetExePath()32f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{4df37983f39daa189b8c814e01a6a9011.pngT szExePath[MAX_PATH];5df37983f39daa189b8c814e01a6a9011.pngselect_variable::value>(GetModuleFileNameA,GetModuleFileNameW)(NULL,szExePath);6df37983f39daa189b8c814e01a6a9011.pngreturnszExePath;70ac3a2d53663ec01c7f7225264eeefae.png}

内部实现类型化

GetDirSize计算某一目录或文件的大小,因内部用到了FirstFirstFile和FirstNextFile,而这两个API不仅路径,而且WIN32_FIND_DATA结构体都有A和W版本,因此需要选择定义正确的结构体变量和调用正确的API函数。

1

cbef093dcc044b2793832001e2365e43.pngtemplate2cbef093dcc044b2793832001e2365e43.pnginline ULONGLONG GetDirSize(constcharT*path,constvolatileBOOL&bExitCalc)32f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{4df37983f39daa189b8c814e01a6a9011.pngintret=IsDirectoryOrFile(path);5df37983f39daa189b8c814e01a6a9011.pngif(0==ret)return0L;6df37983f39daa189b8c814e01a6a9011.png7df37983f39daa189b8c814e01a6a9011.pngstd::basic_stringstrPath=path;8df37983f39daa189b8c814e01a6a9011.pngif(1==ret)9f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png918e8df969f9f8c8d002f25cda86cade.png{10df37983f39daa189b8c814e01a6a9011.pngif(strPath.length()-1!=strPath.rfind((charT)'\\'))11df37983f39daa189b8c814e01a6a9011.pngstrPath+=(charT)'\\';12df37983f39daa189b8c814e01a6a9011.pngstrPath+=select_variable::value>("*.*",L"*.*");134a5daaec04350a363f186a4d2c5ed6ce.png}14df37983f39daa189b8c814e01a6a9011.pngULONGLONG ullSumSize=0;15df37983f39daa189b8c814e01a6a9011.pngtypename select_type::value,WIN32_FIND_DATAA,WIN32_FIND_DATAW>::type findData;16df37983f39daa189b8c814e01a6a9011.pngHANDLE hFindFile=select_variable::value>(FindFirstFileA,FindFirstFileW)(strPath.c_str(),&findData);17df37983f39daa189b8c814e01a6a9011.png18df37983f39daa189b8c814e01a6a9011.pngfor(BOOL bResult=(hFindFile!=INVALID_HANDLE_VALUE);bResult; bResult=select_variable::value>(FindNextFileA,FindNextFileW)(hFindFile,&findData))19

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png    918e8df969f9f8c8d002f25cda86cade.png{20df37983f39daa189b8c814e01a6a9011.pngif(findData.cFileName[0]==(charT)'.')21df37983f39daa189b8c814e01a6a9011.pngcontinue;22df37983f39daa189b8c814e01a6a9011.pngif(findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)23f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.png918e8df969f9f8c8d002f25cda86cade.png{24df37983f39daa189b8c814e01a6a9011.pngstrPath=strPath.substr(0,strPath.rfind((charT)'\\')+1)+findData.cFileName;25df37983f39daa189b8c814e01a6a9011.pngullSumSize+=GetDirSize(strPath.c_str(), bExitCalc);264a5daaec04350a363f186a4d2c5ed6ce.png}27df37983f39daa189b8c814e01a6a9011.pngelse28df37983f39daa189b8c814e01a6a9011.pngullSumSize+=(((ULONGLONG)findData.nFileSizeHigh)<<32)+findData.nFileSizeLow;29

4a5daaec04350a363f186a4d2c5ed6ce.png}30df37983f39daa189b8c814e01a6a9011.png::FindClose(hFindFile);31df37983f39daa189b8c814e01a6a9011.pngreturnullSumSize;320ac3a2d53663ec01c7f7225264eeefae.png}

posted on 2011-12-24 19:08 春秋十二月 阅读(2736) 评论(2)  编辑 收藏 引用 所属分类: C/C++

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值