下面是宏:
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
并不比类型安全更优越:
inline bool succeeded(int hr) { return hr >= 0; }
但是宏确实有其用处,请列出您发现需要预处理器无法完成的宏使用情况。
请将每个用例放在单独的答案中,以便进行投票。如果您知道如何在没有预处理器的情况下实现其中的某个答案,请在该答案的评论中指出。
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
并不比类型安全更优越:
inline bool succeeded(int hr) { return hr >= 0; }
但是宏确实有其用处,请列出您发现需要预处理器无法完成的宏使用情况。
请将每个用例放在单独的答案中,以便进行投票。如果您知道如何在没有预处理器的情况下实现其中的某个答案,请在该答案的评论中指出。
C++的单元测试框架(例如UnitTest++)基本上都围绕着预处理宏展开。少量的单元测试代码会扩展成一系列类的层级结构,如果手动输入将不太好玩。如果没有像UnitTest++那样的工具和其预处理魔法,我不知道您如何有效地编写C++单元测试。
使用普通函数调用无法对函数调用参数进行短路计算。例如:
#define andm(a, b) (a) && (b)
bool andf(bool a, bool b) { return a && b; }
andm(x, y) // short circuits the operator so if x is false, y would not be evaluated
andf(x, y) // y will always be evaluated
当我们在C ++中编程时,我们不应该为它的C遗产感到羞耻,而是充分利用它。 C ++的唯一问题,甚至C的唯一问题,是当它们本身与其自身的逻辑不一致时。
使用预处理器(宏)仍然可以构建一些非常高级和有用的东西,这是您使用C++“语言结构”包括模板无法完成的。
例子:
vc12
中的stdio.h
和sal.h
文件以更好地理解。 - Elshan__FILE__
和__LINE__
宏,以及我们QA基础设施中的自动化日志文件扫描工具。OUR_OWN_THROW
可能会与该异常的异常类型和构造函数参数一起使用,包括文本描述。如下所示:OUR_OWN_THROW(InvalidOperationException, (L"Uninitialized foo!"));
这个宏会抛出InvalidOperationException
异常,其中包含构造函数参数的描述信息,但它还会写入一条消息到日志文件中,该消息由抛出异常的文件名和行号及其文本描述组成。抛出的异常将获得一个ID,并在日志中记录。如果代码的其他地方捕获了异常,则会标记该异常,并且日志文件将指示特定的异常已被处理,因此不太可能是后续记录的任何崩溃的原因。我们的自动化QA基础设施可以轻松捕获未处理的异常。
代码重复。
请查看boost预处理器库,它是一种元元编程。在主题->动机中,您可以找到一个很好的例子。
一个常见的用途是检测编译环境,对于跨平台开发,您可以为Linux编写一组代码,而对于Windows,则可以编写另一组代码,当不存在适用于您目的的跨平台库时。
因此,在一个粗略的例子中,跨平台互斥锁可以具有以下特点:
void lock()
{
#ifdef WIN32
EnterCriticalSection(...)
#endif
#ifdef POSIX
pthread_mutex_lock(...)
#endif
}
对于函数而言,当您想要明确忽略类型安全时,它们非常有用。例如上下文中进行ASSERT的许多示例。当然,像许多C/C++特性一样,您可能会自找麻烦,但语言会为您提供工具,并让您决定如何处理。
/*
* List of fields, names and values.
*/
FIELD(EXAMPLE1, "first example", 10)
FIELD(EXAMPLE2, "second example", 96)
FIELD(ANOTHER, "more stuff", 32)
...
#undef FIELD
对于公共枚举,可以定义为仅使用名称:
#define FIELD(name, desc, value) FIELD_ ## name,
typedef field_ {
#include "field_list.h"
FIELD_MAX
} field_en;
在私有的初始化函数中,所有字段都可以用来填充数据表:
#define FIELD(name, desc, value) \
table[FIELD_ ## name].desc = desc; \
table[FIELD_ ## name].value = value;
#include "field_list.h"
类似于
void debugAssert(bool val, const char* file, int lineNumber);
#define assert(x) debugAssert(x,__FILE__,__LINE__);
这样你就可以举个例子
assert(n == true);
如果n为false,则在日志中打印出问题的源文件名和行号。
如果您使用普通的函数调用,例如
void assert(bool val);
与其使用宏,你只能在日志中得到断言函数所在的行号,这对于问题的定位不够有帮助。
<cassert>
提供了 assert()
宏,可以输出文件/行号/函数信息时,为什么还要重新发明轮子呢?(至少在我看到的所有实现中都是这样) - underscore_d#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])
char src[23];
int dest[ARRAY_SIZE(src)];
template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
- David Stone