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
#defineTEMPLATE_BOOL_TRAIT_DEF1(trait,T,c)\2template\3structtrait\4
{\5staticconstboolvalue=c;\6};\78#defineTEMPLATE_BOOL_TRAIT_SPEC1(trait,sp,c)\9template<>\10structtrait\11
{\12staticconstboolvalue=c;\13};\1415template16structselect_type;1718template19structselect_type20
{21typedef T1 type;22};2324template25structselect_type26
{27typedef T2 type;28};2930template31inline typename select_type::type select_variable(T1 t1,T2 t2)32
{33returnselect_variable_impl(typename select_type::type(),t1,t2);34}3536template37inline T1 select_variable_impl(int,T1 t1,T2 t2)38
{39returnt1;40}4142template43inline T2 select_variable_impl(long,T1 t1,T2 t2)44
{45returnt2;46}4748TEMPLATE_BOOL_TRAIT_DEF1(is_ansi_char,T,false)49TEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,char,true)50TEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,charconst,true)51TEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,charvolatile,true)52TEMPLATE_BOOL_TRAIT_SPEC1(is_ansi_char,charconstvolatile,true)5354#undefTEMPLATE_BOOL_TRAIT_DEF155#undefTEMPLATE_BOOL_TRAIT_SPEC1
select_variable应用
有了select_variable,封装设计API就方便多了,在函数模板中,类型的参数化体现在其参数、返回值和内部实现三方面,下面就从这三方面来说明其应用:
参数类型化
IsDirectoryOrFile根据路径来判断是否为目录或文件,对于调用方来说,可以灵活指定A或W版本的字符串路径。
1
template2inlineintIsDirectoryOrFile(constcharT*path)3
{4DWORD dwFlag=select_variable::value>(GetFileAttributesA,GetFileAttributesW)(path);5if(INVALID_FILE_ATTRIBUTES==dwFlag)6return0;7return(dwFlag&FILE_ATTRIBUTE_DIRECTORY)?1:2;8}
返回值类型化
GetExePath获取当前应用程序的路径,对于调用方来说,可以灵活指定想要返回A或W版本字符串表示的路径。
1
template2inline std::basic_stringGetExePath()3
{4T szExePath[MAX_PATH];5select_variable::value>(GetModuleFileNameA,GetModuleFileNameW)(NULL,szExePath);6returnszExePath;7}
内部实现类型化
GetDirSize计算某一目录或文件的大小,因内部用到了FirstFirstFile和FirstNextFile,而这两个API不仅路径,而且WIN32_FIND_DATA结构体都有A和W版本,因此需要选择定义正确的结构体变量和调用正确的API函数。
1
template2inline ULONGLONG GetDirSize(constcharT*path,constvolatileBOOL&bExitCalc)3
{4intret=IsDirectoryOrFile(path);5if(0==ret)return0L;67std::basic_stringstrPath=path;8if(1==ret)9
{10if(strPath.length()-1!=strPath.rfind((charT)'\\'))11strPath+=(charT)'\\';12strPath+=select_variable::value>("*.*",L"*.*");13}14ULONGLONG ullSumSize=0;15typename select_type::value,WIN32_FIND_DATAA,WIN32_FIND_DATAW>::type findData;16HANDLE hFindFile=select_variable::value>(FindFirstFileA,FindFirstFileW)(strPath.c_str(),&findData);1718for(BOOL bResult=(hFindFile!=INVALID_HANDLE_VALUE);bResult; bResult=select_variable::value>(FindNextFileA,FindNextFileW)(hFindFile,&findData))19
{20if(findData.cFileName[0]==(charT)'.')21continue;22if(findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)23
{24strPath=strPath.substr(0,strPath.rfind((charT)'\\')+1)+findData.cFileName;25ullSumSize+=GetDirSize(strPath.c_str(), bExitCalc);26}27else28ullSumSize+=(((ULONGLONG)findData.nFileSizeHigh)<<32)+findData.nFileSizeLow;29
}30::FindClose(hFindFile);31returnullSumSize;32}
posted on 2011-12-24 19:08 春秋十二月 阅读(2736) 评论(2) 编辑 收藏 引用 所属分类: C/C++