您遇到过的最糟糕的现实世界宏/预处理器滥用是什么(请不要做作的 IOCCC 回答 *哈哈* ) ?
如果真的很有趣,请添加一个简短的片段或故事。目标是教一些东西,而不是总是告诉人们“永远不要使用宏”。
ps:我以前使用过宏......但通常当我有一个“真正的”解决方案时我最终会摆脱它们(即使真正的解决方案是内联的,所以它变得类似于宏)。
奖励:举一个宏确实比非宏解决方案更好的例子。
相关问题: C++ 宏何时有益?
您遇到过的最糟糕的现实世界宏/预处理器滥用是什么(请不要做作的 IOCCC 回答 *哈哈* ) ?
如果真的很有趣,请添加一个简短的片段或故事。目标是教一些东西,而不是总是告诉人们“永远不要使用宏”。
ps:我以前使用过宏......但通常当我有一个“真正的”解决方案时我最终会摆脱它们(即使真正的解决方案是内联的,所以它变得类似于宏)。
奖励:举一个宏确实比非宏解决方案更好的例子。
相关问题: C++ 宏何时有益?
根据记忆,它看起来像这样:
#define RETURN(result) return (result);}
int myfunction1(args) {
int x = 0;
// do something
RETURN(x)
int myfunction2(args) {
int y = 0;
// do something
RETURN(y)
int myfunction3(args) {
int z = 0;
// do something
RETURN(z)
是的,没错,任何函数中都没有右括号。语法高亮一团糟,所以他用 vi 来编辑(不是 vim,它有语法着色!)
他是一位俄罗斯程序员,主要从事汇编语言工作。他热衷于保存尽可能多的字节,因为他以前曾在内存非常有限的系统上工作。“它是用于卫星的。只有很少的字节,所以我们将每个字节用于很多事情。” (有点摆弄,将机器指令字节重用于它们的数值)当我试图找出什么样的卫星时,我只能得到“轨道卫星。用于进入轨道”。
他还有两个怪癖:一个安装在显示器上方的凸面镜“用于知道谁在看”,以及偶尔突然从椅子上下来做十个快速俯卧撑。他将最后一个解释为“编译器在代码中发现错误。这是惩罚”。
我最糟糕的:
#define InterlockedIncrement(x) (x)++
#define InterlockedDecrement(x) (x)--
我花了两天的时间来追踪一些多线程 COM 引用计数问题,因为某些白痴将其放在了头文件中。我不会提及我当时工作的公司。
这个故事的寓意是什么?如果您不理解某些内容,请阅读文档并了解它。不要只是让它消失。
#define ever (;;)
for ever {
...
}
#include <iostream>
#define System S s;s
#define public
#define static
#define void int
#define main(x) main()
struct F{void println(char* s){std::cout << s << std::endl;}};
struct S{F out;};
public static void main(String[] args) {
System.out.println("Hello World!");
}
挑战:任何人都可以用更少的定义和结构来做到这一点吗?;-)
#define private public
#define if while
这是拿某人开的玩笑,受影响的人并不觉得这很有趣
可怕的:
#define begin {
#define end }
/* and so on */
说真的,如果你想用 Pascal 编码,买一个 Pascal 编译器,不要破坏美丽的 C 语言。
一个“建筑师”,非常谦虚的人,你知道的类型,有以下几点:
#define retrun return
因为他喜欢打字快。这位脑外科医生过去喜欢对比他聪明的人(几乎所有人)大喊大叫,并威胁要对他们使用黑带。
真实世界?MSVC 在 minmax.h 中有宏,称为max
and min
,每次我打算使用标准std::numeric_limits<T>::max()
函数时都会导致编译器错误。
Pascal 语法和法语关键字的混合:
#define debut {
#define fin }
#define si if(
#define alors ){
#define sinon }else{
#define finsi }
Raymond Chen非常反对使用流量控制宏。他最好的例子直接来自原始的 Bourne shell 源代码:
ADDRESS alloc(nbytes)
POS nbytes;
{
REG POS rbytes = round(nbytes+BYTESPERWORD,BYTESPERWORD);
LOOP INT c=0;
REG BLKPTR p = blokp;
REG BLKPTR q;
REP IF !busy(p)
THEN WHILE !busy(q = p->word) DO p->word = q->word OD
IF ADR(q)-ADR(p) >= rbytes
THEN blokp = BLK(ADR(p)+rbytes);
IF q > blokp
THEN blokp->word = p->word;
FI
p->word=BLK(Rcheat(blokp)|BUSY);
return(ADR(p+1));
FI
FI
q = p; p = BLK(Rcheat(p->word)&~BUSY);
PER p>q ORF (c++)==0 DONE
addblok(rbytes);
POOL
}
我想为比赛提交一个名为chaos-pp的 gem ,它通过预处理器宏实现了一种功能语言。
示例之一是完全由预处理器计算第 500 个斐波那契数:
预处理器之前的原始代码如下所示:
int main(void) {
printf
("The 500th Fibonacci number is "
ORDER_PP(8stringize(8to_lit(8fib(8nat(5,0,0)))))
".\n");
return 0;
}
预处理文件我们得到以下结果(经过相当长的等待):
$ cpp -I../inc fibonacci.c 2>/dev/null | tail
return fib_iter(n, 0, 1);
}
# 63 "fibonacci.c"
int main(void) {
printf
("The 500th Fibonacci number is "
"139423224561697880139724382870407283950070256587697307264108962948325571622863290691557658876222521294125"
".\n");
return 0;
}
直接来自Qt:
#define slots /* */
#define signals /* */
与 boost::signals 等其他库进行交互真的很不错......只是一个例子,Qt 中还有许多其他库可以创建看起来很有趣的代码,例如:
class X : public QObject {
Q_OBJECT
private slots:
//...
public signals:
//...
};
那就是 C++ ......但突然间:
boost::signals::trackable
不再是有效的 C++。
Windows.h 有很多滥用宏的函数。
MrValdez 对 Windows.h 中的 GetObject 宏感到恼火
GetObject 宏将 GetObject() 函数更改为 GetObjectA() 或 GetObjectW() (取决于构建是否分别以非 unicode 和 unicode 编译)
MrValdez 讨厌在 GetObject 函数行之前做
#undef GetObject
Object *GetObject()
另一种方法是将函数名称更改为其他名称,例如 GetGameObject()
jdkoftinoff 在评论中指出:问题是所有的 windows API 函数都是宏。
Adam Rosenfield 提到,可以通过在包含 windows.h 之前定义 NOGDI、WIN32_LEAN_AND_MEAN、NOMINMAX 等来解决问题以消除问题。
#define return if (std::random(1000) < 2) throw std::exception(); else return
这太邪恶了。它是随机的,这意味着它一直在不同的地方触发,它改变了返回语句,它通常有一些代码可能会自行失败,它改变了你永远不会怀疑的无辜关键字,它使用来自 std 空间的异常,因此您不会尝试搜索您的来源以找到它的来源。简直太棒了。
我和一位同事在我们的一些对象流代码中发现了这两个宝石。这些宏在每个进行流式传输的单个类文件中实例化。这个可怕的代码不仅遍布我们的代码库,当我们就它联系原作者时,他在我们的内部 wiki 上写了一篇 7 页的文章,捍卫这是完成他在这里尝试做的事情的唯一可能方法。
不用说,它已经被重构并且不再在我们的代码库中使用。
不要被突出显示的关键字所迷惑。这都是一个宏
#define DECLARE_MODIFICATION_REQUEST_PACKET( T ) \
namespace NameSpace \
{ \
\
class T##ElementModificationRequestPacket; \
} \
\
DECLARE_STREAMING_TEMPLATES( IMPEXP_COMMON_TEMPLATE_DECLARE, NameSpace::ElementModificationRequestPacket<T>, OtherNameSpace::NetPacketBase ) \
DLLIMPEXP_COMMON_TEMPLATE_DECLARE( NameSpace::ElementModificationRequestPacket<T> ) \
DECLARE_AUTOGENERATION_TEMPLATES( DLLIMPEXP_COMMON_TEMPLATE_DECLARE, NameSpace::T##ModificationRequestPacket, NameSpace::ElementModificationRequestPacket<T> ) \
\
namespace NameSpace { \
class DLLIMPEXP_COMMON T##ModificationRequestPacket : public ElementModificationRequestPacket<T>\
{ \
public: \
T##ModificationRequestPacket( NetBase * pParent ) \
: ElementModificationRequestPacket<T>( pParent ), m_Gen() {} \
\
T##ModificationRequestPacket( NetBase * pParent, \
Action eAction, \
const T & rT ) \
: ElementModificationRequestPacket<T>( pParent, eAction, rT ), m_Gen() {} \
\
T##ModificationRequestPacket( const T##ModificationRequestPacket & rhs ) \
: ElementModificationRequestPacket<T>( rhs ), m_Gen() {} \
\
virtual ~T##ModificationRequestPacket( void ) {} \
\
virtual Uint32 GetPacketTypeID( void ) const \
{ \
return Net::T##_Modification_REQUEST_PACKET; \
} \
\
virtual OtherNameSpace::ClassID GetClassID ( void ) const \
{ \
return OtherNameSpace::NetBase::GenerateHeader( OtherNameSpace::ID__LICENSING, \
Net::T##_Modification_REQUEST_PACKET ); \
} \
\
virtual T##ModificationRequestPacket * Create( void ) const \
{ return new T##ModificationRequestPacket( m_pParent ); } \
\
T##ModificationRequestPacket() {} \
\
protected: \
OtherNameSpace::ObjectAutogeneration<T##ModificationRequestPacket> m_Gen; \
\
friend class OtherNameSpace::StreamingBase::StreamingClassInfoT<T##ModificationRequestPacket >; \
OtherNameSpace::StreamingBase::Streaming<T##ModificationRequestPacket, ElementModificationRequestPacket<T> > m_Stream; \
\
}; \
} \
DLLIMPEXP_COMMON_TEMPLATE_DECLARE( ThirdNameSpace::ListenerBase<const NameSpace::T##ModificationRequestPacket> ) \
DLLIMPEXP_COMMON_TEMPLATE_DECLARE( ThirdNameSpace::BroadcasterT<const NameSpace::T##ModificationRequestPacket> ) \
typedef ThirdNameSpace::BroadcasterT<const T##ModificationRequestPacket> T##ModifiedBroadcaster;
#define IMPLEMENT_MODIFICATION_REQUEST_PACKET( T ) \
DLLIMPEXP_COMMON_TEMPLATE_INSTANTIATE( NameSpace::ElementModificationRequestPacket<T> ) \
DLLIMPEXP_COMMON_TEMPLATE_INSTANTIATE( ThirdNameSpace::ListenerBase<const NameSpace::T##ModificationRequestPacket> ) \
DLLIMPEXP_COMMON_TEMPLATE_INSTANTIATE( ThirdNameSpace::BroadcasterT<const NameSpace::T##ModificationRequestPacket> ) \
INSTANTIATE_STREAMING_TEMPLATES( DLLIMPEXP_COMMON_TEMPLATE_INSTANTIATE, NameSpace::ElementModificationRequestPacket<T>, OtherNameSpace::NetPacketBase ) \
INSTANTIATE_AUTOGENERATION_TEMPLATES( DLLIMPEXP_COMMON_TEMPLATE_INSTANTIATE, NameSpace::T##ModificationRequestPacket, NameSpace::ElementModificationRequestPacket<T> )
更新(2009 年 12 月 17 日):
关于这个可怕的宏作者的更多好消息。截至 8 月,负责这一怪物的员工已被解雇。
我自己做了以下事情,我想我从中学到了一些东西。
1992 年左右,我写了一个小型的 Lisp 解释器。它不是用普通的 C 语言实现的,而是用类似 C 的解释语言实现的。不过,这种类 C 语言使用了标准的 C 预处理器。
Lisp 解释器当然包含函数car,它在 Lisp 中用于返回列表中的第一个元素,以及cdr,它返回列表的其余部分。它们是这样实现的:
LISPID car(LISPID id) {
CHECK_CONS("car", 1, id);
return cons_cars[id - CONS_OFFSET];
} /* car */
LISPID cdr(LISPID id) {
CHECK_CONS("cdr", 1, id);
return cons_cdrs[id - CONS_OFFSET];
} /* cdr */
(数据存储在数组中,因为没有结构。CONS_OFFSET是常数 1000。)
car和cdr在 Lisp 中经常使用,而且很短,而且由于在实现语言中函数调用不是很快,我通过将这两个 Lisp 函数实现为宏来优化我的代码:
#define car(id) (CHECK_CONS("car", 1, (id)), cons_cars[(id) - CONS_OFFSET])
#define cdr(id) (CHECK_CONS("car", 1, (id)), cons_cdrs[(id) - CONS_OFFSET])
CHECK_CONS检查它的参数是否实际上是一个列表,并且由于它在解释器中也经常使用并且很短,所以我也将它写为一个宏:
#define CHECK_CONS(fun, pos, arg) \
(!IS_CONS(arg) ? \
LISP_ERROR("Arg " + pos + " to " + fun + \
" must be a list: " + lispid2string(arg)) : 0)
IS_CONS和LISP_ERROR也经常使用,所以我也把它们做成了宏:
#define IS_CONS(id) \
( intp(id) && (id) >= CONS_OFFSET \
&& ((id) - CONS_OFFSET) < sizeof(cons_cars))
#define LISP_ERROR(str) (throw((str) + "\n"))
似乎合理?
但是,为什么整个系统会在这一行崩溃:
id2 = car(car(car(car((id1))));
我花了很长时间才找到问题所在,直到最后我检查了预处理器将那条短线扩展为什么。它被扩展为一个 31370 个字符的行,为了清楚起见,我在这里将其分成几行(其中 502 行):
id2 = ((!(intp( (((!(intp( (((!(intp( (((!(intp( (id1)) && (
(id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (id1))) + "\n")) : 0), cons_cars[(id1) - 1000])))
&& ( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp( (id1)) && (
(id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (id1))) + "\n")) : 0), cons_cars[(id1) - 1000]))) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (((!(intp( (id1)) && ( (id1))
>= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg "
+ 1 + " to " + "car" + " must be a list: " + lispid2string(
(id1))) + "\n")) : 0), cons_cars[(id1) - 1000])))) + "\n")) : 0),
cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) && ( (((!(intp( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) >= 1000 && (( (((!(intp(
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) - 1000) < sizeof(cons_cars))
? (throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 &&
(( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to
" + "car" + " must be a list: " + lispid2string( (id1))) + "\n"))
: 0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && (
(id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (id1))) + "\n")) : 0), cons_cars[(id1) - 1000])))
>= 1000 && (( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])))) + "\n")) : 0),
cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && ((
(id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to "
+ "car" + " must be a list: " + lispid2string( (id1))) + "\n")) :
0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1))
>= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg "
+ 1 + " to " + "car" + " must be a list: " + lispid2string(
(id1))) + "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && ((
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])) - 1000]))) && ( (((!(intp(
(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) && ( (((!(intp( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) >= 1000 && (( (((!(intp(
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) - 1000) < sizeof(cons_cars))
? (throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 &&
(( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to
" + "car" + " must be a list: " + lispid2string( (id1))) + "\n"))
: 0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && (
(id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (id1))) + "\n")) : 0), cons_cars[(id1) - 1000])))
>= 1000 && (( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])))) + "\n")) : 0),
cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && ((
(id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to "
+ "car" + " must be a list: " + lispid2string( (id1))) + "\n")) :
0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1))
>= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg "
+ 1 + " to " + "car" + " must be a list: " + lispid2string(
(id1))) + "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && ((
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])) - 1000]))) >= 1000 && ((
(((!(intp( (((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && ((
(id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to "
+ "car" + " must be a list: " + lispid2string( (id1))) + "\n")) :
0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1))
>= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg "
+ 1 + " to " + "car" + " must be a list: " + lispid2string(
(id1))) + "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && ((
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) && ( (((!(intp( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) >= 1000 && (( (((!(intp(
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) - 1000) < sizeof(cons_cars))
? (throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 &&
(( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to
" + "car" + " must be a list: " + lispid2string( (id1))) + "\n"))
: 0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && (
(id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (id1))) + "\n")) : 0), cons_cars[(id1) - 1000])))
>= 1000 && (( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])))) + "\n")) : 0),
cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && ((
(id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to "
+ "car" + " must be a list: " + lispid2string( (id1))) + "\n")) :
0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1))
>= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg "
+ 1 + " to " + "car" + " must be a list: " + lispid2string(
(id1))) + "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && ((
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])) - 1000]))) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (((!(intp( (((!(intp( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) && ( (((!(intp( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) >= 1000 && (( (((!(intp(
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && (( (((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000]))) - 1000) < sizeof(cons_cars))
? (throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 &&
(( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to
" + "car" + " must be a list: " + lispid2string( (id1))) + "\n"))
: 0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && (
(id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (id1))) + "\n")) : 0), cons_cars[(id1) - 1000])))
>= 1000 && (( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])))) + "\n")) : 0),
cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && ((
(id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to "
+ "car" + " must be a list: " + lispid2string( (id1))) + "\n")) :
0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1)) && ( (id1))
>= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg "
+ 1 + " to " + "car" + " must be a list: " + lispid2string(
(id1))) + "\n")) : 0), cons_cars[(id1) - 1000]))) >= 1000 && ((
(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1))
- 1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car"
+ " must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])))) + "\n")) : 0), cons_cars[(((!(intp(
(id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) <
sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" + " must
be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000])) - 1000])) - 1000])))) + "\n")) : 0),
cons_cars[(((!(intp( (((!(intp( (((!(intp( (id1)) && ( (id1)) >=
1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg " +
1 + " to " + "car" + " must be a list: " + lispid2string( (id1)))
+ "\n")) : 0), cons_cars[(id1) - 1000]))) && ( (((!(intp( (id1))
&& ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ?
(throw(("Arg " + 1 + " to " + "car" + " must be a list: " +
lispid2string( (id1))) + "\n")) : 0), cons_cars[(id1) - 1000])))
>= 1000 && (( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) -
1000) < sizeof(cons_cars)) ? (throw(("Arg " + 1 + " to " + "car" +
" must be a list: " + lispid2string( (id1))) + "\n")) : 0),
cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars))
我曾经不得不将一个 C 应用程序从 unix 移植到 windows,其具体性质将保持未命名以保护有罪。编写它的人是一位不习惯编写生产代码的教授,显然是从其他语言开始接触 C 的。也碰巧英语不是他的第一语言,尽管他来自的国家大多数人都说得很好。
他的应用程序大量使用预处理器将 C 语言转换成他可以更好理解的格式。但他最常使用的宏定义在一个名为“Thing.h”的头文件中(说真的),其中包括以下内容:
#define I Any void_me
#define thou Any void_thee
#define iam(klas) klas me = (klas) void_me
#define thouart(klas) klas thee = (klas) void_thee
#define my me ->
#define thy thee ->
#define his him ->
#define our my methods ->
#define your thy methods ->
...然后他用它来写下如下的怪物:
void Thing_setName (I, const char *name) {
iam (Thing);
if (name != my name) {
Melder_free (my name);
my name = Melder_wcsdup (name);
}
our nameChanged (me);
}
void Thing_overrideClass (I, void *klas) {
iam (Thing);
my methods = (Thing_Table)klas;
if (! ((Thing_Table) klas) -> destroy)
((Thing_Table) klas) -> _initialize (klas);
}
整个项目(约 60,000 LOC)是用类似的风格编写的——marco hell、怪异的名字、旧英语行话等。幸运的是,我们能够把代码扔掉,因为我找到了一个执行相同算法的 OSS 库几十个快几倍。
(我已经复制并编辑了我最初在这个问题上做出的这个答案)。
我遇到过的最糟糕的情况是在一个产品中包含一组可执行文件,而指定的技术负责人没有找到库。
相反,他拥有在多个 Visual Source Safe 文件夹中共享的文件集。然后他意识到他们需要对每个应用程序的行为略有不同。
您可以在此处应用许多重构步骤。
相反,他使用#ifdefs
void DisplayLoadError()
{
#if defined __TIMETABLE_EDITOR
MessageBox("Timetable Editor failed to load the correct timetable", MB_ERROR);
#else if defined __SCHEDULESET_EDITOR
MessageBox("Schedule Set Editor faied to load the correct Schedule Set", MB_ERROR);
#else if defined __ROSTER_EDITOR
MessageBox("Roster Editor failed to load the correct Roster", MB_ERROR);
#endif
}
使用 LINE 预处理器为通过网络传递的消息生成唯一 ID:
NetworkMessages.h
#define MSG_LOGIN __LINE__
#define MSG_LOGOUT __LINE__
#define MSG_CHAT __LINE__
这是一个宏确实比非宏解决方案更好的示例:
在非宏解决方案中,必须构建函数和变量来跟踪消息的 ID。开发人员可能会也可能不会使消息 ID 跟踪变得复杂,而这更易于阅读和调试。
此外,只需将消息添加到源中即可更轻松地添加新消息。
这种情况的缺点是该文件必须包含在所有使用消息的代码中。每当编辑消息时,编译时间都会增加。
一个相当糟糕的例子:
#ifdef __cplusplus
#define class _vclass
#endif
这允许包含调用的成员变量的 C 结构class
由 C++ 编译器处理。其中有两个带有此构造的标题;其中一个在末尾还包含“#undef class”,而另一个则不包含。
在国际混淆 C 编码竞赛的一年中,有一个条目,整个程序是:
P
附带条件是您可以P
在 makefile 中定义为您想要的任何程序。
我记得,它在其中一个类别中获胜,第二年出现了一条规则,不允许这种进入方式。
(编辑:六个月后或什么......我确信当我写这篇文章时,“没有 IOCCC”的事情不是主要问题......)
有一天我很无聊,正在玩 Objective-C 中的积木......
#define Lambda(var, body) [^ id(id (var)) { return (body);} copy]
#define Call(f, arg) ((id(^)(id))(f))(arg)
#define Int(num) [NSNumber numberWithInteger:(num)]
#define Mult(a, b) Int([(a) integerValue] * [(b) integerValue])
#define Add(a, b) Int([(a) integerValue] + [(b) integerValue])
#define Sub1(n) Int([(n) integerValue] - 1)
#define Add1(n) Int([(n) integerValue] + 1)
#define If(cond, thenblock, elseblock) ([(cond) integerValue] ? (thenblock) : (elseblock))
#define Cons(car, cdr_) [[ConsType alloc] initWithCar:(car) cdr:(cdr_)]
#define Car(list) [(list) car]
#define Cdr(list) [(list) cdr]
#define Define(var, value) id var = (value)
#define Nullq(value) Int(value == nil)
允许“有趣”的事情,例如:
Define(Y, Lambda(f, Call(Lambda(x, Call(x, x)),
Lambda(x, Call(f, Lambda(y, Call(Call(x, x), y)))))));
Define(AlmostTotal, Lambda(f, Lambda(list, If(Nullq(list), Int(0),
Add(Car(list), Call(f, Cdr(list)))))));
Define(Total, Call(Y, AlmostTotal));
Print(Call(Total, Cons(Int(4), Cons(Int(5), Cons(Int(8), nil)))));
(为简洁起见,未显示一些函数和类定义)
我看到的最糟糕的是不使用:-)
有人在方法内部编写了一个 strcpy (我认为就是这样......现在已经超过 10 年了)函数(因为他们不想要调用 strcpy 的开销......叹息)。
他们发现它不适用于日文字符,因此他们在开始时添加了一个“if”来处理 ASCII 或 Unicode。那时代码大约有一个屏幕长......可能会破坏缓存的一致性并抹去他应该为代码内联而节省的钱。
除了类型之外,代码是相同的(所以应该使用宏)。
当然,他们编写的 strcpy 比标准库中手动调整的汇编程序慢得多……
当然,如果他们只是将这一切都作为一个宏来完成,它可能会被调用 strcpy 所取代......
当然我退出了公司(不是直接因为那个......)
强制性的
#define FOR for
和
#define ONE 1
#define TWO 2
...
谁知道?
我维护在宏中有 goto 的代码。所以一个函数最后会有一个标签,但函数代码中没有可见的 goto。更糟糕的是,除非您水平滚动,否则宏通常位于其他语句的末尾,通常不在屏幕上。
#define CHECK_ERROR if (!SomeCondition) goto Cleanup
void SomeFunction()
{
SomeLongFunctionName(ParamOne, ParamTwo, ParamThree, ParamFour); CHECK_ERROR
//SomeOtherCode
Cleanup:
//Cleanup code
}
#define TRUE 0 // dumbass
几年后,这样做的人解释了自己 - 大多数(如果不是全部)C 库函数返回 0 表示一切顺利。因此,他希望能够编写如下代码:
if (memcpy(buffer, packet, BUFFER_SIZE) == TRUE) {
; // rape that packet
}
不用说,我们团队中的任何人(测试人员或开发人员)都不敢再看他的代码。
#include <iostream>
#define public_static_void_main(x) int main()
#define System_out_println(x) std::cout << x << std::endl
public_static_void_main(String[] args) {
System_out_println("Hello World!");
}
一位对幻数规则看不懂的同学:
#define TWO_HUNDRED_AND_EIGHTY_THREE_POINT_ONE 283.1
ASA - http://www.ingber.com/#ASA
你真的必须下载它才能欣赏它。整个工作流程由宏决定。它完全不可读。举个例子 -
if (asa_open == FALSE) {
asa_open = TRUE;
++number_asa_open;
#if ASA_PRINT
if (number_asa_open == 1) {
/* open the output file */
#if USER_ASA_OUT
if (!strcmp (OPTIONS->Asa_Out_File, "STDOUT")) {
#if INCL_STDOUT
ptr_asa_out = stdout;
#endif /* INCL_STDOUT */
} else {
#if ASA_SAVE
ptr_asa_out = fopen (OPTIONS->Asa_Out_File, "a");
#else
ptr_asa_out = fopen (OPTIONS->Asa_Out_File, "w");
#endif
}
#else /* USER_ASA_OUT */
if (!strcmp (ASA_OUT, "STDOUT")) {
#if INCL_STDOUT
ptr_asa_out = stdout;
#endif /* INCL_STDOUT */
} else {
#if ASA_SAVE
ptr_asa_out = fopen (ASA_OUT, "a");
#else
ptr_asa_out = fopen (ASA_OUT, "w");
#endif
}
#endif /* USER_ASA_OUT */
} else {
#if USER_ASA_OUT
if (!strcmp (OPTIONS->Asa_Out_File, "STDOUT")) {
#if INCL_STDOUT
ptr_asa_out = stdout;
#endif /* INCL_STDOUT */
} else {
ptr_asa_out = fopen (OPTIONS->Asa_Out_File, "a");
}
#else
if (!strcmp (ASA_OUT, "STDOUT")) {
#if INCL_STDOUT
ptr_asa_out = stdout;
#endif /* INCL_STDOUT */
} else {
ptr_asa_out = fopen (ASA_OUT, "a");
}
#endif
fprintf (ptr_asa_out, "\n\n\t\t number_asa_open = %d\n",
number_asa_open);
}
#endif /* ASA_PRINT */
} else {
++recursive_asa_open;
#if ASA_PRINT
if (recursive_asa_open == 1) {
/* open the output file */
#if ASA_SAVE
#if USER_ASA_OUT
if (!strcmp (OPTIONS->Asa_Out_File, "STDOUT")) {
#if INCL_STDOUT
ptr_asa_out = stdout;
#endif /* INCL_STDOUT */
} else {
ptr_asa_out = fopen (OPTIONS->Asa_Out_File, "a");
}
#else
if (!strcmp (ASA_OUT, "STDOUT")) {
#if INCL_STDOUT
ptr_asa_out = stdout;
#endif /* INCL_STDOUT */
} else {
ptr_asa_out = fopen (ASA_OUT, "a");
}
#endif
#else /* ASA_SAVE */
#if USER_ASA_OUT
if (!strcmp (OPTIONS->Asa_Out_File, "STDOUT")) {
#if INCL_STDOUT
ptr_asa_out = stdout;
#endif /* INCL_STDOUT */
} else {
等等等等
这只是设置选项。整个程序就是这样。
一位曾经是编码员的“技术经理”在我们的 C++ 项目中引入了以下精彩的宏,因为他认为NULL
在 DOM 解析例程中检查值的工作量太大:
TRYSEGV
CATCHSEGV
在幕后,这些使用setjmp
,longjmp
和信号处理程序来SIGSEGV
模拟“捕获”段错误的能力。
当然,一旦代码退出原始TRYSEGV
宏调用的范围,代码中的任何内容都不会重置跳转指向,因此代码中的任何段错误都会返回到(现在无效的)jump_env
指针。
代码会立即死在那里,但不会在破坏程序堆栈并使调试或多或少毫无意义之前死掉。
AI Game Programming Wisdom有一章,其中使用宏为有限状态机创建脚本语言。
由于这本书和代码是受版权保护的材料,这里有一个谷歌书链接到详细说明宏的页面(生成的脚本语言可以在第 324 页找到。)
在 Lucent,我曾经看过 Steve Bourne 最初的 Unix shell 的源代码,发现他使用 C 预处理器使 C 看起来像Pascal 或Algol。处理 if 语句的部分如下所示:
#define IF if (
#define THEN ) {
#define ELSE } else {
#define ELIF } else if (
#define FI ; }
我的一个朋友告诉我,他在 1990 年代中期对其进行了一些维护,但还是一样。(这里有一个关于代码库固有的保守主义的教训。)
当然,史蒂夫在早期这样做是作为一个实验,如果他以后再写的话,我肯定会重新考虑。
更新:根据 Wikipedia 的Bourne Shell 文章,宏赋予它Algol 68的味道。而且,全套宏在这里!他们显然影响了国际混淆 C 代码竞赛的创始人。
我喜欢这个例子,它使用宏来近似 PI 的值。圆圈越大,近似值越准确。
#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_
_-_-_-_
}
另一个是c
程序
c
要编译,您需要定义c
为
-Dc="#include <stdio.h> int main() { char *t =\"Hello World\n\"; while(*t) putc(*t++, stdout); return 0; }"
C中的协程(AKA Stackless线程)。:)这是邪恶的诡计。
#define crBegin static int state=0; switch(state) { case 0:
#define crReturn(i,x) do { state=i; return x; case i:; } while (0)
#define crFinish }
int function(void) {
static int i;
crBegin;
for (i = 0; i < 10; i++)
crReturn(1, i);
crFinish;
}
int decompressor(void) {
static int c, len;
crBegin;
while (1) {
c = getchar();
if (c == EOF)
break;
if (c == 0xFF) {
len = getchar();
c = getchar();
while (len--)
crReturn(c);
} else
crReturn(c);
}
crReturn(EOF);
crFinish;
}
void parser(int c) {
crBegin;
while (1) {
/* first char already in c */
if (c == EOF)
break;
if (isalpha(c)) {
do {
add_to_token(c);
crReturn( );
} while (isalpha(c));
got_token(WORD);
}
add_to_token(c);
got_token(PUNCT);
crReturn( );
}
crFinish;
}
switch (device_id)
{
#ifndef PROD_1
#ifndef PROD_2
#ifdef PROD_3
case ID_1:
#endif
#ifdef PROD_4
#ifdef PROD_5
case ID_2:
case ID_3:
case ID_4:
#elif defined(PROD_4)
#ifndef PROD_6
case ID_1:
#endif // PROD_6
case ID_5:
#endif
case ID_6:
#endif
#ifdef PROD_7
#ifndef PROD_8
case ID_7:
#endif
#endif
(改名是为了保护不那么无辜的人)
请注意,我们甚至还没有得到任何代码,这只是为了得到第一个实际的代码位。这实际上发生在几个函数中(以几乎但不完全相同的方式),每个函数最终只有 4 种可能的变化(它们也大多是复制/粘贴,略有变化和它们自己的#ifdefs)。
#define interface struct
在一些 Optima++ 头文件中(Optima++ 是/曾经是我必须使用的 Watcom/Powersoft IDE)。
#define FLASE FALSE
程序员是一个糟糕的打字员,这是他最常犯的错误。
必须从记忆中执行此操作,但大概是这样的:使用 lib 编写 Symbian 应用程序。隐藏在您需要包含的头文件中的是这个小宝石:
// Here come the register defines:
#define C <something>
#define N <something>
<two more single letter defines>
在我们的代码中,加载具有硬编码文件名的文件失败。当我们将文件位置从 C 盘更改为 D 盘时,它神奇地工作了......
#define unless(cond) if(!cond)
#define until(cond) while(!cond)
用过的:
unless( ptr == NULL)
ptr->foo();
我见过的最糟糕的是在我当前的项目中,其中有很多案例:
#if PROGRAMA
.
.
if(...)
{
.
.
.
#else
.
.
if(...)
{
.
.
.
#endif
}
是的,他用一个关闭来关闭 2 个打开。
当我第一次遇到 C 中的宏时,他们让我难过了好几天。以下是我所面临的。我想这对 C 专家来说非常有意义并且非常高效,但是对我来说尝试弄清楚到底发生了什么意味着将所有不同的宏剪切和粘贴在一起,直到可以查看整个函数。这肯定不是好习惯?!使用普通的旧功能有什么问题?!
#define AST_LIST_MOVE_CURRENT(newhead, field) do { \
typeof ((newhead)->first) __list_cur = __new_prev; \
AST_LIST_REMOVE_CURRENT(field); \
AST_LIST_INSERT_TAIL((newhead), __list_cur, field); \
} while (0)
好的宏:(虽然我个人不喜欢使用这种语法所需的双括号;我更喜欢可变参数宏(仅限 C99)或 PRINTF_0、PRINTF_1 等,具体取决于参数的数量)
#ifdef DEBUG
#define PRINTF(x) printf x
#else
#define PRINTF(x)
#endif
减少非调试构建的代码大小/执行时间(第一个比第二个多);还可以防止泄漏调试文本字符串,这可能会带来很小的安全风险
与雷蒙德的咆哮相关的是以下可怕的(在我看来,当然)宏:
#define CALL_AND_CHECK(func, arg) \
int result = func(arg); \
if(0 != result) \
{ \
sys.exit(-1); \
} \
我对使用宏的做法很陌生,并且使用了这个宏,但我预计我传递给它的函数会失败。而且我是在后台线程中执行此操作的,所以这让我困扰了好几天,为什么我的整个应用程序都“崩溃”了。
顺便说一句,如果在编写这个宏时只有 std::tr1::function 存在,我的生活就会恢复一周!
我曾经把这个可怕的 C++ 代码放在一起,它使用宏来帮助将函数挂钩到 DLL 的导入表中。
#define ARGLIST(...) __VA_ARGS__
#define CPPTYPELESSARG(typelessParams) thisptr, typelessParams
#define CPPTYPEDARG(typedParams) void* thisptr, typedParams
#define CPPTYPELESSNOARG thisptr
#define CPPTYPEDNOARG void* thisptr
#define CPPHOOKBODY(hookName, params) void *thisptr; \
__asm { mov thisptr, ecx } \
return On##hookName ( params );
#define CHOOKBODY(hookName, typelessParams) return On##hookName( typelessParams );
#define CPPHOOK(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, CPPTYPEDARG(typedParams), typelessParams, \
typedParams, __thiscall, __stdcall, CPPHOOKBODY(hookName, CPPTYPELESSARG(typelessParams)))
#define CPPHOOKNOARG(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, CPPTYPEDNOARG, typelessParams, \
typedParams, __thiscall, __stdcall, CPPHOOKBODY(hookName, CPPTYPELESSNOARG))
#define CDECLHOOK(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams, \
typedParams, __cdecl, __cdecl, CHOOKBODY(hookName, typelessParams))
#define CDECLFUNC(name, address, returnType, args) \
typedef returnType (__cdecl *name##Ptr)(args); \
name##Ptr name = (name##Ptr) address;
#define CPPFUNC(name, address, returnType, args) \
typedef returnType (__thiscall *name##Ptr)(void* thisptr, args); \
name##Ptr name = (name##Ptr) address;
#define STDFUNC(name, address, returnType, args) \
typedef returnType (__stdcall *name##Ptr)(args); \
name##Ptr name = (name##Ptr) address;
#define STDHOOK(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams, \
typedParams, __stdcall, __stdcall, CHOOKBODY(hookName, ARGLIST(typelessParams)))
#define HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams, hookParams, fnPtrCall, hookCall, hookBody) \
typedef returnType (fnPtrCall *##hookName##OrigPtr )( typedParams ); \
class hookName : public IHook \
{ \
public: \
typedef hookName##OrigPtr func_type; \
private: \
static void* m_origFunction; \
static bool m_bModifyImport; \
static std::string m_lib; \
static std::string m_importFunc; \
static std::string m_sHookName; \
static returnType hookCall hookName##FnHook ( hookParams ) \
{ \
hookBody \
} \
static bool ImplIsModifyImport() { return hookName::m_bModifyImport; } \
static void ImplSetModifyImport(bool bModify) { hookName::m_bModifyImport = bModify; } \
static const std::string& ImplGetLibName() { return hookName::m_lib; } \
static const std::string& ImplGetImportFunctionName() { return hookName::m_importFunc; } \
static void ImplSetOriginalAddress(void* fn) { hookName::m_origFunction = fn; } \
static void* ImplGetOriginalAddress() { return hookName::m_origFunction; } \
static returnType On##hookName ( typedParams ); \
static void* ImplGetNewAddress() { return hookName::##hookName##FnHook; } \
static const std::string& ImplGetHookName() { return hookName::m_sHookName; } \
public: \
hookName() \
{ \
InjectHookRef.AddHook((IHook*)this); \
hookName::m_lib = importLib; \
hookName::m_importFunc = importFunc; \
hookName::m_sHookName = #hookName; \
hookName::m_origFunction = NULL; \
hookName::m_bModifyImport = true; \
} \
virtual bool IsModifyImport() const { return hookName::ImplIsModifyImport(); } \
virtual void SetModifyImport(bool bModify) { hookName::ImplSetModifyImport(bModify); } \
virtual const std::string& GetHookName() const { return hookName::ImplGetHookName(); } \
virtual const std::string& GetLibName() const { return hookName::ImplGetLibName(); } \
virtual const std::string& GetImportFunctionName() const { return hookName::ImplGetImportFunctionName(); } \
virtual void* GetOriginalAddress() const { return hookName::ImplGetOriginalAddress(); } \
virtual void* GetNewAddress() const { return hookName::ImplGetNewAddress(); } \
virtual void SetOriginalAddress(void* fn) { hookName::m_origFunction = fn; } \
static func_type GetTypedOriginalAddress() { return reinterpret_cast(hookName::m_origFunction); } \
}; \
void* hookName::m_origFunction = NULL; \
bool hookName::m_bModifyImport = false; \
std::string hookName::m_lib; \
std::string hookName::m_importFunc; \
std::string hookName::m_sHookName; \
static hookName g##hookName##Inst;
这反过来又允许我这样做:
CPPHOOK(gIH, "SimEngine.dll", "?AddEntity@Player@@UAEXPAVEntity@@@Z", PlayerAddEntity, void, void* ent, ent);
/* Called when the engine calls Player::AddEntity(entity) */
void PlayerAddEntity::OnPlayerAddEntity(void *thisptr, void *ent)
{
unsigned int id = getPlayerID(thisptr);
gIH.GetLog()->Info("Player %d adding entity %s.",
getPlayerID(thisptr), getEntityName(ent));
gPlayers[id] = thisptr;
/*if( id == 2 && gPlayers[1] && gPlayers[2] )
EntitySetOwner::GetTypedOriginalAddress() (ent, gPlayers[1]);*/
//gEnts[ent] = Entity(ent, Vector3f());
PlayerAddEntity::GetTypedOriginalAddress() (thisptr, ent);
}
任何使用 sendmail 及其神奇的配置语法的东西
我正在添加另一个随着时间的推移已经开始惹恼我的人:
#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
那就是如果他们做对了;我见过所有可能的括号排列是否存在的版本。我已经看到它在同一个头文件中定义了两次。
我的论点主要适用于 Windows(尽管我假设其他 OS SDK 也有类似的东西),几乎每个人似乎都觉得有必要在他们的项目标题中定义这个宏,我不明白为什么。
WinNT.h(包含在 Windows.h 中)定义了一个非常好的版本,如果您传递指针类型而不是数组 ,它会执行一些模板巫术来导致编译时错误。
当然,如果你正在构建一个 C 程序,它会完全回到我上面写的内容,但我仍然不会无缘无故地重新定义 SDK 默认具有的东西。
#define "CR_LF" '\r'
这让我困惑了一段时间!
一位过去的雇主发现现代 Unix 系统上没有BASIC-PLUS的实现,因此他们尝试使用 C 预处理器宏重新实现它:
#define IF if(
#define THEN ) {
#define ENDIF }
#define GOTO goto L
...ETC。
太可怕了。
这取自一个流行的开源程序。事实上,它通过隐藏丑陋的遗产使代码的某些部分更具可读性。
#define EP_STATUS CASTLING][(BOARD_FILES-2)
#define HOLDINGS_SET CASTLING][(BOARD_FILES-1)
我想这里没有什么不好的,我只是觉得很有趣。
请参阅此答案,了解阅读障碍的同事如何通过一个包含#define fasle false
.
#define PROCESS_AND_RETURN(X) \
X.process(); \
// Important: Return only after invoking virtual method process() \
return X
由于“重要”注释,宏永远不会返回对象和崩溃!
预处理器的另一个“创造性”使用,尽管它更多地是在使用的术语中而不是在机制中(这是非常平凡的):
/***********************************************************************
* OS2 and PCDOS share a lot of common codes. However, sometimes
* OS2 needs codes similar to those of UNIX. NOTPCDOS is used in these
* situations
*/
#ifdef OS2
#define PCDOS
#define NOTPCDOS
#else /* OS2 */
#ifndef PCDOS
#define NOTPCDOS
#endif /* PCDOS */
#endif /* OS2 */
正版代码 - 我以为我已经删除了它,但显然没有。我一定是在某个临时分支中这样做了,并且没有获得将其检回主代码的权限。“待办事项”列表的另一项。
在声明中发现,非常混乱:
NON_ZERO_BYTE Fixed(8) Constant('79'X),
后来发现:
IF WORK_AREA(INDEX) = ZERO_BYTE THEN /* found zero byte */
WORK_AREA(INDEX) = NON_ZERO_BYTE ; /* reset to nonzero*/
最严重的滥用(我偶尔会这样做)是使用预处理器作为某种数据文件替换,即:
#define FOO_RELATION \
BAR_TUPLE( A, B, C) \
BAR_TUPLE( X, Y, Z) \
然后在其他地方:
#define BAR_TUPLE( p1, p2, p3) if( p1 ) p2 = p3;
FOO_RELATION
#undef BAR_TUPLE
这将导致:
if( A ) B = C;
if( X ) Y = Z;
这种模式可以用来做各种(可怕的)事情......生成 switch 语句或巨大的 if else 块,或与“真实”代码接口。您甚至可以使用它来 ::cough:: 在非 oo 上下文菜单系统 ::cough:: 中生成上下文菜单。并不是说我会做任何如此蹩脚的事情。
编辑:固定不匹配的括号和扩展示例
尝试调试真正喜欢宏的大项目,并且有很多宏调用其他宏调用其他宏等。(5-10级宏并不少见)
然后用很多 #ifdef 这个宏 #else 那个宏来填充它,所以如果你按照代码,它就像一棵不同路径的树,它可以去。
唯一的解决方案是大多数情况下是预编译并阅读它......
我同意在大多数情况下,宏使用起来很糟糕,但我发现了一些它们有用的例子。
恕我直言,这实际上非常出色,因为您只能使用 sprintf 获得类似的东西,然后需要资源分配等等,此外,所有工作都完全由预处理器完成
// Macro: Stringize
//
// Converts the parameter into a string
//
#define Stringize( L ) #L
// Macro: MakeString
//
// Converts the contents of a macro into a string
//
#define MakeString( L ) Stringize(L)
// Macro: $LINE
//
// Gets the line number as a string
//
#define $LINE MakeString( __LINE__ )
// Macro: $FILE_POS
//
// Gets the current file name and current line number in a format the Visual Studio
// can interpret and output goto
//
// NOTE: For VS to properly interpret this, it must be at the start of the line (can only have whitespace before)
//
#define $FILE_POS __FILE__ "(" $LINE ") : "
我讨厌使用的另一个,但发现它非常有用的是做这样的事情,它基本上允许我快速生成具有可变数量模板参数的模板
#define TEMPLATE_DEFS typename ReturnType
#define TEMPLATE_DECL ReturnType
#define FUNCTION_PARAMS void
#define FUNCTION_PASS
#define GENERIC_CALLBACK_DECL_NAME CallbackSafePointer0
#include "Callback.inl"
#define TEMPLATE_DEFS typename ReturnType, typename P1
#define TEMPLATE_DECL ReturnType, P1
#define FUNCTION_PARAMS P1 param1
#define FUNCTION_PASS param1
#define GENERIC_CALLBACK_DECL_NAME CallbackSafePointer1
#include "Callback.inl"
#define TEMPLATE_DEFS typename ReturnType, typename P1, typename P2
#define TEMPLATE_DECL ReturnType, P1, P2
#define FUNCTION_PARAMS P1 param1, P2 param2
#define FUNCTION_PASS param1, param2
#define GENERIC_CALLBACK_DECL_NAME CallbackSafePointer2
#include "Callback.inl"
#define TEMPLATE_DEFS typename ReturnType, typename P1, typename P2, typename P3
#define TEMPLATE_DECL ReturnType, P1, P2, P3
#define FUNCTION_PARAMS P1 param1, P2 param2, P3 param3
#define FUNCTION_PASS param1, param2, param3
#define GENERIC_CALLBACK_DECL_NAME CallbackSafePointer3
#include "Callback.inl"
// and so on...
虽然这使得阅读“Callback.inl”有点可怕,但它确实完全消除了用不同数量的参数重写相同的代码。我还应该提到“Callback.inl”#undefs 文件末尾的所有宏,因此,宏本身不会干扰任何其他代码,它只会使“Callback.inl”更难编写(虽然阅读和调试并不难)
当时,将宏作为参数“传递”到另一个宏中似乎是个好主意。(我只是无法忍受在多个地方定义一个值列表的想法。)这里的代码是人为的(并且不是很激励),但给了你这个想法:
#define ENUM_COLORS(CallbackMacro) \
CallbackMacro(RED) \
CallbackMacro(GREEN) \
CallbackMacro(BLUE) \
// ...
#define DEFINE_COLOR_TYPE_CALLBACK(Color) \
Color,
enum MyColorType {
ENUM_COLORS(DEFINE_COLOR_TYPE_CALLBACK)
};
void RegisterAllKnownColors(void)
{
#define REGISTER_COLOR_CALLBACK(Color) \
RegisterColor(Color, #Color);
ENUM_COLORS(REGISTER_COLOR_CALLBACK)
}
void RegisterColor(MyColorType Color, char *ColorName)
{
// ...
}
它不是 C 宏,但...
许多年前,我有一项有趣的任务,就是将原始的 Transport Tycoon 从 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 版本,因为当我们尝试汇编宏时,它会使汇编器崩溃。
斯基兹
BSD 内核中的 NFS 代码在宏之间使用 goto。它仍在使用中,并且代码确实有效。我知道有几个人试图清理它,但一段时间后他们都放弃了——太乱了。
你可以在这里看到它: http ://www.openbsd.org/cgi-bin/cvsweb/src/sys/nfs/nfsm_subs.h?rev=1.43
我曾经看到一个宏包,它可以为每个 C 关键字加上别名,让您可以有效地用克林贡语进行编程。没错,克林贡语。(联合国)幸运的是,该项目几年前被放弃并拆除。
我大约 10 年前使用的 ASIC 的驱动程序代码有很多部分,如下所示:
int foo(state_t *state) {
int a, b, rval;
$
if (state->thing == whatever) {
$
do_whatever(state);
}
// more code
$
return rval;
}
经过一番摸索,我们终于找到了定义:
#if DEBUG
#define $ dolog("%s %d", __FILE__, __LINE__);
#else
#define $
#endif
这很难找到,因为使用它的源文件都没有包含文件。有一个名为top.c
源文件的文件,如下所示:
#include <namechanged.h>
#include <foo.c>
#include <bar.c>
#include <baz.c>
果然,这是 Makefile 中唯一引用的文件。每当您更改任何内容时,都必须重新编译所有内容。这是“使代码更快”。
#define protected private
有时似乎是个好主意,但如果你需要,你可能应该只是字符串替换。受保护是相当邪恶的,允许内部访问后代并不比仅仅公开项目好多少......
任何使用标记连接运算符的宏##
。
我看到一个我的一位同事有幸与之合作。他们尝试自定义实现字符串实习,因此他们使用大量(当然)无法正常工作的宏重新实现字符串。试图弄清楚它做了什么让我的眼睛因为所有##
的散落而爆炸。
我使用头文件作为大宏:
// 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
我不喜欢 Boost Preprocessor 的东西。我曾经试图弄清楚如何使用它(无论如何我们在项目中都有 Boost ......),但据我所知,使用它会使我的错误消息变得如此难以阅读,以至于不值得。
我喜欢相当于循环宏的想法,但它太过分了。
#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);
问题是 Visual Studio 2005 和其他 IDEgo to definition
和go to declaration
功能只能找到#define TY_(...)
而不是所需的DocParseStream
声明。
也许这样更安全。
我认为他们应该为每个函数加上一个前缀,而不是调用一个宏来完成这项工作..它使代码混乱..但也许我错了。你觉得怎么样..?
ps:貌似内部函数const和其他的几乎都是用this..前缀的。。刚才同事跟我说是常用的..wtf?也许我错过了什么。
温柔点,我写这篇文章是我能想到的一般捕获异常的唯一方法。
我用它来捕获和阻止异常从我的公共接口函数中传播出来......
/// 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 处理程序?这并不是最终目的,这只是我的通用 catch 处理程序,用于隔离遗留的公共函数,并在我知道函数被跨边界调用时快速添加至少某种程度的保护,如果一个边界可能会抛出将抛出 C++ 异常(欢迎您,JNI)。
那么它会让你逃跑和躲藏,还是这样做的唯一方法?
基本上...
try{
// some block of code capable of throwing
}
CatchAll()
任何使用 Token 连接运算符 ## 的东西。我已经看到这用于将 C++ 中的伪模板系统和其他可怕的东西一起破解。使用它最糟糕的事情是你的错误消息变得多么神秘。
然而,我已经看到了它的一个很好的用途。有一个宏#MONITOR_COMPONENT(classname) 在编译时生成类,这些类将从预定义的监视器类和类名继承,并且会自动注册到用于监视每个组件的单例类。
它奏效了吗?是的,这是最好的方法吗..可能不是。