什么是预处理器宏有用?

时间:2009-08-12 12:42:29

标签: c++ macros

在阅读了关于使用宏的另一个问题之后,我想知道: 它们有什么用处?

我很快就会看到被其他语言构造取代的一件事就是减少你需要在下面输入的相关单词的数量:

void log_type( const bool value ) { std::cout << "bool: " << value; }
void log_type( const int value ) { std::cout << "int: " << value; }
...
void log_type( const char  value ) { std::cout << "char: "  << value; }
void log_type( const double  value ) { std::cout << "int: "  << value; }
void log_type( const float  value ) { std::cout << "float: "  << value; }

而不是

#define LOGFN( T ) void log_type( const T value ) { std::cout << #T ## ": " << value; }
LOGFN( int )
LOGFN( bool )
...
LOGFN( char )
LOGFN( double )
LOGFN( float )

还有其他“不可替代的”吗?

编辑: 试图总结原因 - 为什么在答案中遇到;因为那是我感兴趣的主要原因。主要是因为我觉得他们中的大多数都归功于我们仍然在原始文本文件中编程,但仍然是支持环境不佳。

  • 编译代码的灵活性(例如#ifdef DEBUG,平台问题)(SadSido,Catalin,Goz)
  • 调试目的(例如__LINE__, __TIME__);我也在这个原因下使用'字符串'(SadSido,Jla3ep,Jason S)
  • 替换例如PHP的requireinclude功能(#pragma once)(SadSido,Catalin)
  • 通过替换复杂代码来增强可读性(例如MESSAGEMAPBOOST_FOREACH)(SadSido,fnieto)
  • DRY原则(Jason S)
  • 内联替换(Matthias Wandel,Robert S. Barnes)
  • stringifying(Jason S)

8 个答案:

答案 0 :(得分:14)

  • 在不同条件下编译不同的代码(#ifdef __DEBUG__);
  • 保护每个翻译单元包含一次标题(#pragma once);
  • __FILE____LINE__ - 替换为当前文件名和当前行;
  • 构造代码以使其更具可读性(例如:BEGIN_MESSAGE_MAP());

在这里看到有趣的宏观讨论:

http://www.gotw.ca/gotw/032.htm

http://www.gotw.ca/gotw/077.htm

答案 1 :(得分:7)

最有用 - 头文件保护:

#ifndef MY_HEADER_GUARD
#define MY_HEADER_GUARD

// Header file content.

#endif 

稍后添加[仅限Windows ]

将类导出到DLL:

#ifdef EXPORT_MY_LIB
#define    MY_API __declspec( dllexport)
#else
#define    MY_API __declspec( dllimport)
#endif

示例类:

class MY_API MyClass { ... };

答案 2 :(得分:7)

平台特定部分。

#ifdef WINDOWS
#include "WindowsImplementation.h"
#elif defined( LINUX )
#include "LinuxImplementation.h"
#else
#error Platform undefined.
#endif

答案 3 :(得分:4)

我以前发过这个,但当然现在找不到它。如果您想访问__FILE____LINE__宏,那么另一个宏是目前最便捷的方式 - 例如:

#define ATHROW(msg)                                         \
{                                                           \
    std::ostringstream os;                                  \
    os << msg;                                              \
    throw ALib::Exception( os.str(), __LINE__, __FILE__ );  \
}

答案 4 :(得分:3)

BOOST_FOREACHinjecting variables中制作酷炫的魔术技巧。

BOOST_FOREACH( char c, "Hello, world!" )
{
   ... use char variable c here ...
}   // c's scope ends here
// if there's an outer c defined, its scope resumes here

答案 5 :(得分:3)

因为不重复自己(DRY)的原因。在编译时涉及重复构造的事情,不能在其他方法(模板或你有什么)中抽象出来。如果您发现重复相同的代码构造20次,那么这是人为错误的潜在根源 - 希望可以使用模板将其抽象出去,但有时却不会。它总是在看到可以进行类型检查和清晰审查的原始代码的优势与使用宏来实现任意替换模式(通常无法通过自动编程工具检查)的优势之间取得平衡。

模板无法执行字符串化和连接(#和##预处理器模式)。

当然,在某些时候,您可能最好使用工具(无论是自定义还是现成的)来自动生成代码。

答案 6 :(得分:0)

现代语言采用的理念是,需要一个支持处理器的符号表示缺少语言功能,因此它们定义了预处理器在旧的K&amp; R样式C中非常简单地处理的各种语言功能。

上面的代码示例可以通过内联函数进行简化,例如。

就个人而言,预处理器最不可或缺的方面是清楚地表明在源代码中有些事情是在编译时完成的。在编译时消除死代码路径的java方法在阅读代码时并不那么明显。

答案 7 :(得分:0)

他们的一个用途基本上是一个糟糕的勒芒(内联)模板功能。

For example

#define MIN(X,Y) ((X) < (Y) ? : (X) : (Y))

这允许您为支持这些运算符的任何类型生成自定义MIN函数,这些函数在使用时实际上是内联的。当然没有类型检查,如果你没有恰到好处的话,很容易就会出现奇怪的语法错误或不正确的行为。