65.9K
CodeProject 正在变化。 阅读更多。
Home

定义一个接收多个任意类型参数的唯一宏

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.34/5 (31投票s)

2003年12月29日

2分钟阅读

viewsIcon

96941

downloadIcon

520

本文介绍了如何定义一个接收多个不同类型参数并将其自动转换为连接字符串的宏

引言

C++ 宏的一个常见用途是处理诸如跟踪或记录到文件的函数。由于这些函数通常使用由动态数据生成的字符串,因此将多个参数传递给宏而无需将每个参数转换为字符串可能很有用,有时开发人员不知道该怎么做。这可以通过很多方式来实现,并且 Code Project 上有类似的文章讨论了这个主题,但我在这里提出的解决方案使用了一种不同的方法,这种方法产生了一个简洁的语法,没有像“%d” / “%s” / “%c”这样的字符格式化... 一个示例用法可能是

MYTRACE( “The username found is ”, strUserName,
         “ having ”, intAge,
         “ years old. The database key is ”, lngUserKey );

我将使用跟踪作为一个示例应用程序,一个真正的跟踪类可能会考虑诸如多线程代码保护等问题,这些问题不在本文的讨论范围内。

背景

假设您需要在代码中跟踪信息。通常你会将其封装在一个类中

class MyTraceClass
{
   static void Trace( const string &message )
   {
      // trace code...
   }
};

由于我们需要一种简单的方法来在发布版本中禁用跟踪代码,我们将定义一个宏,例如

#define MYTRACE( x ) MyTraceClass::Trace( x )
它可以正常工作,但这样我们只能传递一个字符串参数。一些程序员定义了各种宏版本,例如TRACE / TRACE2 / TRACE3...,每个宏版本对应于不同数量的参数,显然这不是一个优雅的解决方案,因为宏用户需要根据传递的参数来了解正确的宏版本。使用 va_arg 可以解决这个问题。MFC 将 TRACE 宏定义为
void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...);
.
.
.
#define TRACE  ::AfxTrace
因此,我们可以使用 printf 格式说明符(如“%d” / “%s” / “%c”...)将许多参数传递给宏。
int i=10;
.
.
.
   TRACE( “Iteration number:  %d”, i );

上面的代码可以正常工作,但我们必须注意格式说明符,并且我们不能将非 LPCTSTR 数据作为第一个参数传递给 TRACE,而无需手动转换为 LPCTSTR。由于跟踪代码将被“关闭”,因此任何额外的代码都是一种开销,并且会使代码的其余部分混乱。

提出的解决方案

使用模板和重载,可以使用任意类型的可变数量的参数调用宏。 stringstream 类有助于轻松地将每个参数转换为字符串,从而组成最终信息。

首先,将宏定义为静态方法的名称

#define MYTRACE  MyTraceClass::Trace

要接收任何类型的参数,我们将定义接收 typename 参数的跟踪函数

template<typename T1> static void Trace( T1 par1 );

要接收多个参数,请提供该重载方法的多个版本。现在可以使用 stringstream 类将参数转换为字符串

template<typename T1, typename T2> static void Trace( T1 par1, T2 par2 )
{
stringstream ss;
   ss << par1 << par2;
   Trace( ss.str() );
}

template<typename T1, typename T2, typename T3> 
  static void Trace( T1 par1, T2 par2, T3 par3 )
{
stringstream ss;
   ss << par1 << par2 << par3;
   Trace( ss.str() );
}

template<typename T1, typename T2, typename T3, typename T4> 
  static void Trace( T1 par1, T2 par2, T3 par3, T4 par4 )
{
stringstream ss;
   ss << par1 << par2 << par3 << par4;
   Trace( ss.str() );
}

.
.
.

这样,我们可以使用如下所示的语法调用宏。屏幕显示了一个简单的 cout 输出。

MYTRACE( "Method Add() Called !" );
MYTRACE( "Iteration number: ", i );
MYTRACE( “The username found is ”, strUserName,
         “ having ”, intAge,
         “ years old. The database key is ”, lngUserKey );

显然,参数的数量不能超过重载方法中定义的最大值,但这不能被认为是一个问题,跟踪/日志记录代码通常不需要太多参数,最坏的情况下,很容易扩展该类以支持更多重载方法。

这是我在 Code Project 上的第一篇文章。我希望有人觉得它有用 :)

© . All rights reserved.