clq

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

几个难懂的 C 语言宏[还是没搞清楚]

 

可变参数宏

有些时候定义一个宏来代替某个函数 但是这个函数是可变参数的话 那就需要考虑办法了

定义方法如下

CODE

#define PRINT(...) printf(__VA_ARGS__)
#include <stdio.h>
int main(){
PRINT("%d %s %s",1,"吃饭了吗 smile MM:)","\n");
return 0;
}

 

宏组合

也就是## 和 #的用法
## 是连接符号 连接两个宏
#是把名字代替成字符串

如下

CODE

#define s5(a) supper_ ## a
#include <stdio.h>
void supper_printf(const char* p )
{
printf("this is supper printf:\n%s\n",a);
}

int main()
{
 s5(printf)("hello owrld");
 return 0;
}



#用法如下
#include <stdio.h>
#define s(p) #p
int main(){
printf(s(p)"\n");
return 0;
}

运行一下就知道了
--------------------------------------------------

#pragma once

#ifndef _MALLOC_SAFE_H_
#define _MALLOC_SAFE_H_


#include <stdio.h>
#include <string>

//#define malloc3(size) ( malloc_fast_safe(size, __FILE__, __FUNCTION__, # __LINE__) )
#define malloc(size) ( malloc2(size, __LINE__) )
#define malloc_line(size, line) ( malloc_fast_safe(size, __FILE__, __FUNCTION__, #line) )
#define free(buf) ( free_fast_safe(buf) )


//一个简单的快速检测方式
void * malloc_fast_safe(int len, const char * c__FILE__, const char * c__FUNCTION__, const char * c__LINE__);

void free_fast_safe(void * p);



#endif
--------------------------------------------------

还是不行,要这样


#pragma once

#ifndef _MALLOC_SAFE_H_
#define _MALLOC_SAFE_H_


#include <stdio.h>
#include <string>

//宏里面转换成字符串还是很啰嗦,要用两级宏加 "#" 来处理
#define __STRINGIFY__(x) #x
#define __TOSTRING__(x) __STRINGIFY__(x)

//#define malloc(size) ( malloc_fast_safe(size, __FILE__, __FUNCTION__, #__LINE__) )
#define malloc(size) ( malloc_fast_safe(size, __FILE__, __FUNCTION__, __TOSTRING__(__LINE__)) )
#define free(buf) ( free_fast_safe(buf) )


//一个简单的快速检测方式
void * malloc_fast_safe(int len, const char * c__FILE__, const char * c__FUNCTION__, const char * c__LINE__);

void free_fast_safe(void * p);



#endif

参考自 google "__LINE__ 字符串"

http://www.cppblog.com/heath/archive/2008/08/05/58046.html

--------------------------------------------------

使用__FILE__和__LINE__定位错误

[前言:使用__FILE__和__LINE__来定位错误已经屡见不鲜,然而其中一些道理又有几个人仔细探究过。本文参考了Curtis Krauskopf的一篇名为Using __FILE__ and __LINE__ to Report Errors的文章,希望达到解惑之效。]

问题:当运行时错误产生时,我怎样才能得到包含C++文件名和行号的字符串信息?
回答:在C++中的__FILE__预编译指示器包含了被编译的文件名,而__LINE__则包含了源代码的行号。__FILE__和__LINE__的前后都包含了两个下划线,让我们仔细看看__FILE__所包含的每个字符:

_ _ F I L E _ _

下面展示了在控制台程序中如果显示文件名和代码行号。

#include <stdio.h>

int main(int , char**)
{
     printf(
"This fake error is in %s on line %d\n",         __FILE__, __LINE__);
     
return 0;
}


输出结果:

This fake error is in c:\temp\test.cpp on line 5

让我们更上一层楼

我想通过一个通用函数error()来报告错误,以使当某个错误发生时我能设置断点以及隔离错误处理(例如,在屏幕上打印错误信息或者写入日志)。因此,函数的原型应该是这样的吧:

void error(const char *file, const unsigned long line, const char *msg); 


调用方式如下:

error(__FILE__, __LINE__, "my error message");


预处理魔法

这里有三个问题需要解决:

  1. __FILE__和__LINE__在每次调用error时作为参数传入。
  2. __FILE和__LINE__前后的下划线很容易被遗忘,从而导致编译错误。
  3. __LINE__是一个整数,这无疑增加了error函数的复杂度。我绝不想直接使用整型的__LINE__,而通常都是将转换为字符串打印到屏幕或写入日志文件。

__FILE__和__LINE__应该被自动处理,而非每次作为参数传递给error,这样会让error的使用者感觉更爽些,它的形式可能是下面这样:

error(AT, "my error message");

 
我希望上面的宏AT展开为:"c:\temp\test.cpp:5"。而新的error函数则变成:

void error(const char *location, const char *msg);

 

因为Borland C++ Builder编译器能够自动合并相邻的字符串,因此我把AT写成下面这样:

#define AT __FILE__ ":" __LINE__ 


然而它却罢工了,因为__LINE__被扩展成了整数而非字符串,所以宏展开后变成:

"c:\temp\test.cpp" ":"
5

这是一个无效的字符串,因为末尾有一个未被双引号包含的整数。

怎么办?别着急,一个特殊的预编译指示器“#”能够帮我们将一个变量转换成字符串。所以重新定义宏:

#define AT __FILE__ ":" #__LINE__
嘿嘿,这样总行了吧。别高兴得太早,这样也是不行的。因为编译器会抱怨#是个无效字符。其实,问题是#预编译指示器只有这样使用才会
被正确识别:
#define symbol(X) #X 


因此,我把代码改为:

#define STRINGIFY(x) #x
#define AT __FILE__ ":" STRINGIFY(__LINE__) 


然而,奇怪的结果产生了,__LINE__居然被作为了输出的一部分:

c:\temp\test.cpp:__LINE__: fake error

解决方法是再用一个宏来包装STRINGIFY():

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define AT __FILE__ ":" TOSTRING(__LINE__)


OK,我们用下面的代码来试试:

#include <stdio.h>
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define AT __FILE__ ":" TOSTRING(__LINE__)
void error(const char *location, const char *msg)
{
  printf(
"Error at %s: %s\n", location, msg);
}
int main(int , char**)
{
  error(AT, 
"fake error");
  
return 0;
}


输出结果:

Error at c:\temp\test\test.cpp:11: fake error

Visual Studio下的实践
在《Windows核心编程》中,Jeffrey Richter提供了下面的宏在编译期输出有用信息:

#define chSTR2(x) #x
#define chSTR(x)  chSTR2(x)
#define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc)


message是一个预编译指令,上面宏的使用方法是:

#pragma chMSG(Fix this later)


结论

  1. 预编译指示器__FILE__和__LINE__能够提供有用的信息。
  2. __LINE__的处理方式远比我们想象的要复杂。
  3. __FILE__被处理成字符串,给我们带来了不少方便。

posted on 2008-08-05 11:55

posted on 2012-02-16 15:34  clq  阅读(678)  评论(2编辑  收藏  举报