在C ++中#define的适当使用场景是什么?

时间:2014-02-25 07:19:04

标签: c++ macros c-preprocessor

我知道基本规则,使用inlineenumconst代替#define,这不是我对此问题的追求。我想知道的是在C ++中你将使用#define宏以及如何使用宏的可接受场景。

请不要发布问题或链接到“define vs const”问题或“预处理器与编译器”,我已经浏览过Scott Meyers的Effective C ++,我知道一个优于另一个的优点。

然而,经过几个小时的网上冲浪,我觉得#define在C ++中被视为某种弱者,但我确信必须有一个案例可以接受,甚至是可取的用它。

为了让球滚动起来,我想我可以想到的一个场景是创建一个基于它的DEBUG宏,可以在整个代码中使用打印和诸如此类的东西进行调试。

5 个答案:

答案 0 :(得分:11)

以下是一些使用#define是一个很好的解决方案的场景:

在保留功能签名的同时添加诊断信息:

#ifdef _DEBUG
#define Log(MSG)  Log((MSG), __FILE__, __LINE__);
#endif

条件编译和包含警卫也是一个很好的例子(没有给出示例,因为你应该理解这一点:)。)

Boilerplate代码是另一个例子,但这很容易被滥用。使用宏作为样板代码的一个很好的例子是Boost.UnitTest中的BOOST_AUTO_TEST_CASE宏(更糟糕的例子是将Windows API映射到其CHAR或WCHAR宏的WinAPI宏集。)

另一个很好的例子是提供特定于编译器的关键字和设置:

#if (defined _WIN32) && (defined LIB_SHARED)
#   ifdef LIB_EXPORTS
#       define LIB_EXPORT __declspec(dllexport)
#   else
#       define LIB_EXPORT __declspec(dllimport)
#   endif /* LIB_EXPORTS */
#else
#   define LIB_EXPORT extern
#endif /* _WIN32 && LIB_SHARED */

用法:

// forward declaration of API function:
LIB_EXPORT void MyFunction(int);

答案 1 :(得分:8)

调试/发布或跨平台代码的简单设置。以下是我的计划示例:

void Painter::render()
{
    if (m_needsSorting)
    {
        _sort();
    }
    for (GameObject* o : m_objects)
    {
        o->render();
#ifdef _DEBUG
        o->renderDebug();
#endif
    }
}

还有一个用于win / ios:

#ifdef _WIN32

#include "EGL/egl.h"
#include "GLES2/gl2.h"
#include <Windows.h>

#ifdef _ANALYZE
#include <vld.h>
#endif

#else // IOS
#import <Availability.h>
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import <Foundation/Foundation.h>
#endif

另一件事是图书馆:

#ifdef VECTRY_INLINE
#define vinline inline
#else
#define vinline 
#endif

以及一些有用的东西:

#define MakeShared(T) \
    class T; \
    typedef shared_ptr<T> T##Ptr

答案 2 :(得分:6)

C ++中为数不多的有用案例之一包括警卫:

// A.h
#ifndef _A_H_
#define _A_H_

class A
{ /* ... */ };

#endif /* _A_H_ */

答案 3 :(得分:6)

有时,您希望生成代码而无需重复无休止的样板,或者不必使用其他语言来执行此操作。 From time to time,模板不够用,最终您将使用Boost.Preprocessor生成代码。

宏是“必需”的一个例子是Boost.TTI(类型特征内省)。底层机制以某种方式滥用语言来创建一些强大的元函数,但需要大量的样板。例如,宏BOOST_TTI_HAS_MEMBER_FUNCTION生成一个matefunction,用于检查类是否具有给定的成员函数。这样做需要创建一个新类,如果没有宏,就不能做空(解决问题的非宏解决方案示例here)。

有时您需要使用X-macros来生成代码。在编译时绑定东西非常有用。我不确定它们是否可以在今天完全替换,但无论如何,你可以找到一些非常有趣的应用程序示例here

总而言之,宏可以成为生成代码的强大工具,但需要谨慎使用。

答案 4 :(得分:5)

我认为当引入C时,C不会使用consts,所以#defines是提供常量值的唯一方法。但是后来在#define上没有太多使用,因为consts取而代之(或更好的话我们可以说consts更容易使用)。但我会说include guards仍然是使用它们的一个领域。并且它们被使用,因为您的代码更具可读性。

标题包含警卫是您无法使用争用的区域

example

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo {
    int member;
};

#endif /* GRANDFATHER_H */

您也可以查看Why would someone use #define to define constants?

还要补充一点,#defines不尊重范围,因此没有办法创建类范围的命名空间,而const变量可以在类中定义。(我知道你知道区别但是想到了添加它是非常重要的。

还要显示使用#define的一个示例:

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}