中国开发网: 论坛: 程序员情感CBD: 贴子 68353
Yxd
俺很早以前写的在VC6中如何实现便餐轰的文章,希望对你有用。。。。
这里我会先举两个小例子阐述预处理器宏中实现变参的方法,然后结合一个临界类型模版类TCriticalType的简单例子讲述我在实际应用中如何用到变参宏的。

在C++ 中按ANSI标准变参函数的定义语法是这样的:

//typedef int SomeDataType1;

//typedef int SomeDataType2;

SomeDataType2 TestProc( SomeDataType1 first, … );



SomeDataType1 TestProc( … );

也就是用三个连续的 . 符号表示参数表。

而带参的宏定义语法如下:

#define TESTPROC(x)



#define TESTPROC(x1,x2)



总之如上,宏里面是不允许有像变参函数那样的参数表的定义语法的,那如何让宏具有变参的功能呢?宏其实就是一段代码,事实上在预处理器中就是用宏代码替代宏调用的,反应快的话,这里你就可以看出了——其实不太准确地,宏也就可以看成是一段脚本。脚本语言都有一个共通之处,那就是都提供字符引用之类的功能,在C++ 的宏中用#指示字符串的语法就类似于此——见如下例子:

#include <iostream>

using namespace std;



#define TEST(x) cout << #x" = " << x << endl

int main( int argc, char* argv[] )

{

int a = 12;

TEST(a);

return 0;

}

运行结果,屏幕输出:

a = 12



再看一个稍复杂的例子(这里我们先约定以下代码是路径名为c:\test.cpp的文件里的完整内容):

#include <iostream>



#define __MY_STR(x) #x

#define __MY_STR2(x) __MY_STR(x)

#define MY_FILE_LINE __FILE__ "(" __MY_STR2(__LINE__) "):"

#define prompt(desc) message(MY_FILE_LINE desc)

int main( int argc, char* argv[] )

{

#pragma prompt(“incomplete here…”) // 第8行

return 0;

}

编译器的编译输出显示:

Compiling...

test.cpp

c:\test.cpp(8):incomplete here...

Linking...



test.exe - 0 error(s), 0 warning(s)



宏prompt(desc)的作用就是显示其被调用的位置,上面的__LINE__从MSDN中可以查到其在ANSI Predefined Macros中的说明如下:

The line number in the current source file. The line number is a decimal integer constant. It can be altered with a #line directive.

如是,宏prompt(desc)就是对__LINE__用#做了字符串指示。



现在,可以开始用宏的这个特性实现变参功能了。

约定变参函数定义是:

void MyPrint( int iFirst, … )

{

va_list marker;

int i = first;

va_start( marker, first ); /* Initialize variable arguments. */

while ( -1 != i )

{

cout << i << endl;

i = va_arg( marker, int );

}

va_end( marker ); /* Reset variable arguments. */

}

没错!变参宏的定义其实就是像下面的这么简单:

#define _MYPRINT(x) MyPrint(x)

但还需要的是对传的参数做个directive

#define _ARGC(x) x##,

#define _LAST_ARGC(x) x

#define _ARGC_END _LAST_ARGC(-1)



宏_MYPRINT(x)的调用方法如下:

int x1 = 1, x2 = 2, x3 = 3;

//…

_MYPRINT(_ARGC(x1)_ARGC(x3)_ARGC_END);

_MYPRINT(_ARGC(x1)_ARGC(x2)_ARGC(x3)_ARGC_END);

屏幕输出:

1

3

1

2

3

OK!大功告成J



在下面的内容,你还将看到将变参宏与C++ 模板功能结合使用的具体示例——变参宏在临界类型模板类TCriticalType 中的使用。

由于篇幅所限,我这里列出的将只是TCriticalType 的精简版本,完整代码可以从我的个人网站下载(http://www.personsoft.com/yxd/CriticalType.h),如果你还不能下载,那请稍等,我一有时间就会上传进去,并请注意URL的大小写。

TCriticalType实现的功能是提供一个受临界保护的变量类型

比如

#typedef TCriticalType<int> c_int;

//…

c_int ciTest;

ciTest的用法将和普通int类型变量的用法相似,但是其受临界保护。下面就是TCriticalType的可运行的部分实现代码清单(请注意红色字体部分):

template < class RawType >

class TCriticalType

{

public:

TCriticalType();



RawType GetValue() const; // 这里由于读操作的特点,我决定使用变参宏

void BeginGet(); // 进入读临界保护

void EndGet(); // 退出读临界保护

void SetValue( const RawType data ); // 进入写临界保护区,完成写操作并退出



~TCriticalType();



protected:

CRITICAL_SECTION m_cs; // critical section

RawType m_data; // protected data

};



template < class RawType >

TCriticalType< RawType >::TCriticalType()

{

InitializeCriticalSection( &m_cs );

}



template < class RawType >

TCriticalType< RawType >::~TCriticalType()

{

DeleteCriticalSection( &m_cs );

}



template < class RawType >

void TCriticalType< RawType >::BeginGet()

{

EnterCriticalSection( &m_cs );

}



template < class RawType >

void TCriticalType< RawType >::EndGet()

{

LeaveCriticalSection( &m_cs );

}



template < class RawType >

RawType TCriticalType< RawType >::GetValue() const

{

return m_data;

}



template < class RawType >

void TCriticalType< RawType >::SetValue( RawType data )

{

__try

{

EnterCriticalSection( &m_cs );

m_data = data;

}

__finally

{

LeaveCriticalSection( &m_cs );

}

}



// 变参模板函数定义

//------>BEGIN

template < class RawType >

static void BeginGetMulti( const RawType* pFirst, ... )

{

RawType* pos = ( RawType* )pFirst;

va_list marker;

va_start( marker, pFirst );

while ( NULL != pos )

{

pos->BeginGet();

pos = va_arg( marker, RawType* );

}

va_end( marker );

}



template < class RawType >

static void EndGetMulti( const RawType* pFirst, ... )

{

RawType* pos = ( RawType* )pFirst;

va_list marker;

va_start( marker, pFirst );

while ( NULL != pos )

{

pos->EndGet();

pos = va_arg( marker, RawType* );

}

va_end( marker );

}

//<------END

#define _BeginGetSingle(object) __try{ object.BeginGet();

#define _EndGetSingle(object) }__finally{ object.EndGet(); }

// 以下是变参宏的定义

//------>BEGIN

#define OBJ(x) &x##,

#define OBJ_END NULL

#define _BeginGetMulti(objlist) __try{ BeginGetMulti(objlist);

#define _EndGetMulti(objlist) }__finally{ EndGetMulti(objlist); }

//<------END

typedef TCriticalType<int> c_int;

typedef TCriticalType<short> c_short;

typedef TCriticalType<long> c_long;

typedef TCriticalType<unsigned int> c_uint;

typedef TCriticalType<unsigned short> c_ushort;

typedef TCriticalType<unsigned long> c_ulong;



typedef TCriticalType<char> c_char;

typedef TCriticalType<TCHAR> c_TCHAR;

typedef TCriticalType<WORD> c_WORD;

typedef TCriticalType<DWORD> c_DWORD;



typedef TCriticalType<bool> c_bool;

typedef TCriticalType<BOOL> c_BOOL;



typedef TCriticalType<float> c_float;

typedef TCriticalType<double> c_double;



TCriticalType的使用方法(同样请注意读操作部分):

class CCriticalUser

{

public:

CCriticalUser() {}

~CCriticalUser() {}

void Write();

void Read();

protected:

c_int m_ciTest1;

c_int m_ciTest2;

c_char m_ccTest3;

c_bool m_cbTest4;

}



void CCriticalUser::Write()

{

m_ciTest1.SetValue( 1 );

m_ciTest2.SetValue( 2 );

m_ccTest3.SetValue( ‘A’ );

m_cbTest4.SetValue( true );

}



void CCriticalUser::Read()

{

int iTest1, iTest2;

char cTest3;

BOOL bTest4;

_BeginGetMulti(OBJ(&m_ciTest1)OBJ(&m_ciTest2)OBJ(&m_ccTest3)OBJ(&m_cbTest4)OBJ_END);

iTest1 = m_ciTest1.GetValue();

iTest2 = m_ciTest2.GetValue();

cTest3 = m_ccTest3.GetValue();

bTest4 = m_cbTest4.GetValue();

cout << iTest1 << endl;

cout << iTest2 << endl;

cout << cTest3 << endl;

cout << bTest4 << endl;

_EndGetMulti(OBJ(&m_ciTest1)OBJ(&m_ciTest2)OBJ(&m_ccTest3)OBJ(&m_cbTest4)OBJ_END);

}



int main( int argc, char* argv[] )

{

CCriticalUser theUser;

theUser.Write();

theUser.Read();

return 0;

}

运行结果,屏幕输出:

1

2

A

1



如上,在CCriticalUser的成员方法Read()中的批读处理操作中,我为了使__try与__finally配对而使用了_BeginGetMulti(objlist)、_EndGetMulti(objlist)和相关的宏。同时你也可以看到_BeginGetMulti(objlist)、_EndGetMulti(objlist)和C++ 模板的结合使其仿佛沾上了泛型的光芒(当然,宏本身就无类型)。

最后,我的观点是少用、慎用宏,其实对本文的例子,GP会是更好的方法,但是正如对C++ 的一个非常恰当的比喻——C++ 就像一把双刃剑,所以就要求用“剑”的人有很高的技艺,才能写出高质量的程序。这篇文章你权且可当作是奇技淫巧,但或许能给你丝毫启发J。

全文完,欢迎指正,谢谢!



P.S. 文中源程序代码在VC6.0环境中编译通过

相关信息:


欢迎光临本社区,您还没有登录,不能发贴子。请在 这里登录