你所遇到的最糟糕的实际宏/预处理器滥用是什么(请不要回答显然的IOCCC*哈哈*)?
如果有一个有趣的小片段或故事,请添加进来。目标是教授一些东西,而不是总是告诉人们“永远不要使用宏”。
p.s .:我以前使用过宏……但通常我最终会摆脱它们,当我有了“真正的”解决方案时(即使真正的解决方案是内联的,因此类似于宏)。
奖励:给出一个示例,其中宏确实比非宏解决方案更好。
相关问题:C++宏何时有利?
你所遇到的最糟糕的实际宏/预处理器滥用是什么(请不要回答显然的IOCCC*哈哈*)?
如果有一个有趣的小片段或故事,请添加进来。目标是教授一些东西,而不是总是告诉人们“永远不要使用宏”。
p.s .:我以前使用过宏……但通常我最终会摆脱它们,当我有了“真正的”解决方案时(即使真正的解决方案是内联的,因此类似于宏)。
奖励:给出一个示例,其中宏确实比非宏解决方案更好。
相关问题:C++宏何时有利?
这不是一个C宏,而是...
很多年前,我有幸负责将原始的《运输大亨》从PC移植到Mac。PC版本完全是用汇编语言编写的,所以我们必须逐行查看整个源代码,并首先将其移植为“PC” C代码,然后再将其移植到Mac上。大部分代码都还好,甚至在某些地方还使用了面向对象的编程。然而,世界渲染系统简直令人难以置信。对于那些没有玩过这款游戏的人来说,世界可以以三种缩放级别之一进行查看。相关代码大致如下:
macro DrawMacro <list of arguments>
a couple of thousand lines of assembler with loads of conditionals
based on the macro arguments
DrawZoomLevel1:
DrawMacro <list of magic numbers>
DrawZoomLevel2:
DrawMacro <list of more magic numbers>
DrawZoomLevel3:
DrawMacro <list of even more magic numbers>
我们一定使用了稍旧版本的MASM,因为当我们尝试汇编时,该宏会导致汇编器崩溃。
Skizz
我已经将头文件用作大宏:
// compile-time-caller.h
#define param1 ...
#define param2 ...
#include "killer-header.h"
// killer-header.h
// uses param1 and param2
我还创建了递归头文件。
// compile-time-caller.h
#define param1 ...
#define param2 ...
#include "killer-header.h"
// killer-header.h"
#if ... // conditional taking param1 and param2 as parameters
#define temp1 param1
#define temp2 param2
#define param1 ... // expression taking temp1 and temp2 as parameters
#define param2 ... // expression taking temp1 and temp2 as parameters
#include "killer-header.h"
// some actual code
#else
// more actual code
#endif
请温柔一点,我写这个是因为这是我能想到的通用捕获异常的唯一方法。
我使用它来捕获并阻止异常从我的公共接口函数中传播出去...
/// Catch all generic exceptions and log appropriately.
/// Logger is insulated from throwing, so this is a NO THROW operation.
#define CatchAll( msg ) \
catch( const Poco::Exception &e ) \
{ \
try{ LogCritical( Logs.System(), std::string( e.displayText() ).append( msg ) );}catch(...){assert(0);} \
} \
catch( const std::exception &e ) \
{ \
try{LogCritical( Logs.System(), std::string( e.what() ).append( msg ) );}catch(...){assert(0);} \
} \
catch(...) \
{ \
try{ LogCritical( Logs.System(), std::string( "Exception caught in " __FUNCTION__ ". " ).append( msg ) );}catch(...){assert(0);} \
}
我不喜欢复杂性,也讨厌宏,但除此之外你怎么“做”一个通用的catch处理程序呢?这并不意味着就此结束,这只是我的通用捕获处理程序,用于隔离遗留公共函数并在我知道该函数在可能会抛出C++异常的边界上被调用时快速添加至少一定程度的保护(感谢JNI)。
那么,这会让你惊恐躲藏吗,还是这是唯一可以做到这样的方式?
基本上...
try{
// some block of code capable of throwing
}
CatchAll()
#undef near
#undef far
当我刚开始学习游戏编程时,我在自己写的一个游戏中为相机类编写了一个视锥体,但是我的代码出现了一些奇怪的错误。
后来发现,微软在windows.h中定义了一些关于近和远的#defines,这导致我的_near和_far变量在包含它们的行上出现错误。由于我当时还是个新手,很难追踪问题,因为它们只存在于整个项目的四行中,所以我没有立即意识到。
我在libtidy中找到了它:
/* Internal symbols are prefixed to avoid clashes with other libraries */
#define TYDYAPPEND(str1,str2) str1##str2
#define TY_(str) TYDYAPPEND(prvTidy,str)
TY_(DocParseStream)(bar,foo);
#define TY_(...)
,而不能找到所需的DocParseStream
声明。#define protected private
有时候似乎是个好主意,但如果需要的话,你可能还是应该直接进行字符串替换。protected关键字相当不好,允许内部访问派生类并不比将成员设为public好多少...
我不喜欢 Boost 预处理器的东西。虽然我们项目中已经使用了 Boost,但我曾试图弄清楚如何使用它,但据我所知,使用它会使我的错误消息变得非常难以阅读,这并不值得。
我喜欢循环宏的等效想法,但那太过繁琐了。
任何使用令牌连接运算符##
的宏。
我曾经看到一个同事使用过的宏,他试图自己实现字符串池,所以重新使用了大量宏来实现字符串(当然)并没有正常工作。尝试理解它的功能让我的眼睛因为到处都是##
而爆炸。
任何使用Token连接运算符##的东西。我曾经看到过它被用来在C++和其他可怕的事情中拼凑伪模板系统。使用它最糟糕的事情是你的错误消息变得难以理解。
然而,我见过它有一个好用途。有一个宏#MONITOR_COMPONENT(classname),它在编译时生成从预定义监视器类和classname继承并自动注册到单例类的类,该单例类用于监视每个组件。
它有效吗?是的,但这可能不是最好的方法。