What is the worst real-world macros/pre-processor abuse you've ever come across?
您遇到过的最糟糕的实际宏/预处理器滥用情况是什么(请不要人为的ioccc回答*haha*)?
如果真的很有趣,请添加一个简短的片段或故事。我们的目标是教授一些东西,而不是总是告诉人们"不要使用宏"。
P.S.:我以前用过宏…但通常当我有一个"真正的"解决方案时,我最终会去掉它们(即使真正的解决方案是内联的,所以它变得类似于一个宏)。
额外的好处:举个例子说明宏实际上比非宏解决方案要好。
相关问题:C++宏何时受益?
从记忆中看,它是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #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,它有语法着色!)
他是一名俄罗斯程序员,主要从事汇编语言工作。他热衷于节省尽可能多的字节,因为他以前在内存非常有限的系统上工作过。"它是为了卫星。只有很少的字节,所以我们把每一个字节用于许多事情。"(位的摆弄,重复使用机器指令字节的数值)当我试图找出什么类型的卫星时,我只能得到"轨道卫星"。为了进入轨道。"
他还有另外两个怪癖:一个凸面镜安装在他的显示器上,"因为知道谁在看",偶尔从椅子上突然出来做十个俯卧撑。他将最后一个解释为"编译器在代码中发现错误"。这就是惩罚"。
最糟糕的是:
1 2 | #define InterlockedIncrement(x) (x)++ #define InterlockedDecrement(x) (x)-- |
我花了两天的时间来追踪一些多线程的COM引用计数问题,因为有个白痴把它放在头文件中。我不会提我当时工作的公司。
这个故事的寓意是什么?如果您不理解某些内容,请阅读文档并了解它。不要让它消失。
1 2 3 4 | #define ever (;;) for ever { ... } |
1 2 3 4 5 6 7 8 9 10 11 12 | #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!"); } |
挑战:谁能用更少的定义和结构来完成它?;-)
1 | #define private public |
1 | #define if while |
这是别人开的玩笑,受影响的人不觉得有趣
丑陋的:
1 2 3 | #define begin { #define end } /* and so on */ |
说真的,如果你想用帕斯卡编码,买一个帕斯卡编译器,不要破坏漂亮的C语言。
"建筑师",一个非常谦虚的人,你知道的类型,有以下几点:
1 | #define retrun return |
因为他喜欢打字快。脑外科医生过去喜欢对比他聪明的人大喊大叫(几乎每个人都是这样),并威胁说要用他的黑带系在他们身上。
真实世界?msvc在minmax.h中有宏,称为
pascal语法和法语关键字的混合:
1 2 3 4 5 6 | #define debut { #define fin } #define si if( #define alors ){ #define sinon }else{ #define finsi } |
RaymondChen非常反对使用流控制宏。他最好的例子是直接从原始BourneShell源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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个斐波那契数:
预处理器之前的原始代码如下所示:
1 2 3 4 5 6 7 8 | int main(void) { printf ("The 500th Fibonacci number is" ORDER_PP(8stringize(8to_lit(8fib(8nat(5,0,0))))) ". "); return 0; } |
预处理文件,我们得到以下结果(经过相当长的等待之后):
1 2 3 4 5 6 7 8 9 10 11 12 | $ 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" ". "); return 0; } |
直接从qt:
1 2 | #define slots /* */ #define signals /* */ |
作为boost::信号与其他libs交互真的很好…举个例子,qt中还有许多其他代码可以创建有趣的代码,比如:
1 2 3 4 5 6 7 | class X : public QObject { Q_OBJECT private slots: //... public signals: //... }; |
这就是C++…但突然:
1 | boost::signals::trackable |
不再是有效的C++。
windows.h有很多滥用宏的函数。
mrvaldez对windows.h中的getobject宏很恼火。
getobject宏将getobject()函数更改为getobjecta()或getobjectw()(取决于生成是否分别以非Unicode和Unicode编译)
mrvaldez不喜欢在getobject函数行之前进行操作
1 2 3 | #undef GetObject Object *GetObject() |
另一种方法是将函数名更改为其他名称,如getgameobject()。
注释中的jdkoftinoff指出:问题是所有WindowsAPI函数都是宏。
Adam Rosenfield提到,可以通过在包括windows.h之前定义nogdi、win32_lean_和_mean、nominmax等来解决这些问题。
1 | #define return if (std::random(1000) < 2) throw std::exception(); else return |
这真是太邪恶了。它是随机的,这意味着它总是在不同的地方触发,它改变返回语句,返回语句上通常有一些代码,这些代码本身可能会失败,它改变了看起来无辜的关键字,你永远不会怀疑它,它使用来自std空间的异常,这样你就不会试图通过搜索源来找到它的源。才华横溢。
我和一个同事在我们的一些对象流代码中发现了这两个gem。这些宏在执行流式处理的每个类文件中被实例化。不仅如此,这个可怕的代码在我们的代码库中随处可见,当我们联系到最初的作者时,他在我们的内部wiki上写了一篇7页的文章,为这一点辩护,这是完成他在这里所做尝试的唯一可能方式。
不用说,它已经被重构出来,不再用于我们的代码库中。
不要被突出显示的关键字抛弃。这都是宏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #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年左右,我写了一个小口齿不清的翻译。它没有正常执行但是在一种解释过的类C语言中。不过,这种类C语言使用标准的C预处理器。
Lisp解释器当然包含函数car,即在lisp中用于返回列表中的第一个元素和cdr,其中返回列表的其余部分。它们是这样实现的:
1 2 3 4 5 6 7 8 9 | 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函数实现为宏来优化代码:
1 2 | #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]) |
检查检查它的参数是否实际上是一个列表,由于这个列表在解释器中也经常使用,而且很短,所以我把它也写为一个宏:
1 2 3 4 | #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也经常使用,所以我也把它们做成了宏:
1 2 3 4 5 6 | #define IS_CONS(id) \ ( intp(id) && (id) >= CONS_OFFSET \ && ((id) - CONS_OFFSET) < sizeof(cons_cars)) #define LISP_ERROR(str) (throw((str) +" ")) |
看起来合理吗?
但是,为什么整个系统在这条线上崩溃了:
1 | id2 = car(car(car(car((id1)))); |
我花了很长时间才找出问题所在,直到我最终检查了这条短线由预处理器扩展到了。它被扩展为31370个字符的行,为了清晰起见,我在这里将其拆分为行(502个)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | id2 = ((!(intp( (((!(intp( (((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" + " must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 0), cons_cars[(id1) - 1000])) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 0), cons_cars[(id1) - 1000])) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 0), cons_cars[(id1) - 1000])) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 0), cons_cars[(id1) - 1000])) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000])))) +" ")) : 0), cons_cars[(((!(intp( (id1)) && ( (id1)) >= 1000 && (( (id1)) - 1000) < sizeof(cons_cars)) ? (throw(("Arg" + 1 +" to" +"car" +" must be a list:" + lispid2string( (id1))) +" ")) : 0), cons_cars[(id1) - 1000])) - 1000])) - 1000])))) +" ")) : 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))) +" ")) : 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))) +" ")) : 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))) +" ")) : 0), cons_cars[(id1) - 1000]))) - 1000) < sizeof(cons_cars)) |
我曾经不得不将一个C应用程序从Unix移植到Windows,而Windows的具体性质将保持匿名以保护罪犯。写这篇文章的人是一位不习惯写生产代码的教授,他显然是从其他语言来的C语言。尽管他来自大多数人的国家,但英语并不是他的第一语言。
他的应用程序大量使用预处理器将C语言转换成他能更好理解的格式。但他使用最多的宏是在名为"thing.h"(严肃地说)的头文件中定义的,其中包括以下内容:
1 2 3 4 5 6 7 8 9 | #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 -> |
…他曾经写过这样的怪兽:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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); } |
整个项目(~60000个loc)都是用类似的风格写的——马可地狱、奇怪的名字、古老的英语行话等等。幸运的是,由于我发现了一个OSS库,它执行相同的算法快了几十倍,所以我们能够将代码抛出。
(我已经复制并编辑了这个问题的原始答案)。
我遇到过的最糟糕的情况是在一个包含一组可执行文件的产品中,指定的技术负责人没有发现库。
相反,他在几个可视化的源代码安全文件夹中共享了一组文件。然后他意识到,对于每个应用程序,它们的行为需要略有不同。
这里有许多重构步骤可以应用。
相反,他用ifdefs
1 2 3 4 5 6 7 8 9 10 | 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 } |
使用行预处理器为通过网络传递的消息生成唯一ID:
1 2 3 4 5 | NetworkMessages.h #define MSG_LOGIN __LINE__ #define MSG_LOGOUT __LINE__ #define MSG_CHAT __LINE__ |
这是一个宏确实比非宏解决方案更好的示例:
在非宏解决方案类中,必须构建函数和变量来跟踪消息的ID。开发人员可能会或可能不会使消息ID跟踪变得复杂,而这更易于读取和调试。
此外,只需将消息添加到源中,就可以更容易地添加新消息。
这种情况的缺点是文件必须包含在所有使用消息的代码中。每当编辑消息时,编译时间就会增加。
一个相当糟糕的例子:
1 2 3 | #ifdef __cplusplus #define class _vclass #endif |
这允许一个C结构包含一个名为EDCOX1(0)的成员变量,由C++编译器处理。其中有两个包含此构造的头;其中一个在末尾还包含"undef class",另一个不包含。
在为期一年的国际模糊C编码比赛中,有一个项目的整个程序是:
但前提是,您可以在makefile中将
我记得,它在其中一个类别中获胜,第二年,一条规则突然出现,禁止这种类型的进入。
(编辑:六个月后或其他…我确信在我写这篇文章的时候,"没有IOCCC"这件事不在主要问题中…)
有一天我很无聊,在Objective-C玩积木。
1 2 3 4 5 6 7 8 9 10 11 12 13 | #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) |
允许"有趣"的事情,如:
1 2 3 4 5 6 | 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))))); |
(一些函数和类定义不是为了简洁而显示的)
1 | #define TRUE 0 // dumbass |
这样做的人几年后解释了自己——大多数(如果不是全部)C库函数返回0表示一切顺利。因此,他希望能够编写如下代码:
1 2 3 | if (memcpy(buffer, packet, BUFFER_SIZE) == TRUE) { ; // rape that packet } |
不用说,我们团队(测试人员或开发人员)中没有人敢再看一眼他的代码。
我维护宏中有goto的代码。因此函数的末尾将有一个标签,但函数代码中没有可见的goto。更糟的是,宏位于其他语句的末尾,通常不在屏幕上,除非水平滚动。
1 2 3 4 5 6 7 8 9 | #define CHECK_ERROR if (!SomeCondition) goto Cleanup void SomeFunction() { SomeLongFunctionName(ParamOne, ParamTwo, ParamThree, ParamFour); CHECK_ERROR //SomeOtherCode Cleanup: //Cleanup code } |
我看到的最糟糕的是没用的——)
有人写了一篇文章(我想是这样的…十多年前的现在)函数内部的一个方法(因为他们不希望调用strcpy的开销…叹息)
他们包括它对日文字符不起作用,所以他们在开始时添加了一个"if"来做ascii或unicode。在那一点上,代码是关于一个屏幕长…可能会破坏缓存的一致性,并抹去他为代码嵌入所节省的成本。
代码与类型相同(所以应该使用宏)。
当然,他们编写的strcpy比标准库中的手工调优汇编程序慢得多…
当然,如果他们只是作为一个宏来完成这一切,那么就可以用对strcpy的调用来替换它…
当然,我辞职了(不是直接因为这个…)
必修课
1 | #define FOR for |
和
1 2 3 | #define ONE 1 #define TWO 2 ... |
谁知道?
1 2 3 4 5 6 7 | #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!"); } |
一个不懂魔法数字规则的同学说:
曾经是一个编码器的"技术经理"把下面的奇妙宏引入到我们的C++项目中,因为他认为在DOM解析例程中检查EDCOX1的0个值是太多的工作:
1 2 | TRYSEGV CATCHSEGV |
在封面下,它们使用
当然,一旦代码退出了原始
代码会立即死在那里,但不会在破坏程序堆栈并或多或少地使调试变得毫无意义之前。
ASA-http://www.ingber.com/ASA
你真的要下载才能欣赏它。整个工作流程由宏决定。它完全不可读。作为一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | 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," \t\t number_asa_open = %d ", 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 { |
等。
这只是设置选项。整个程序就是这样的。
人工智能游戏编程智慧有一章,宏被用来为有限状态机创建脚本语言。
由于这本书和代码是受版权保护的材料,这里有一个谷歌图书链接到详细说明宏的页面(生成的脚本语言可以在第324页找到)。
我喜欢这个例子,它使用宏来近似pi的值。圆越大,近似值越精确。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #define _ -F<00||--F-OO--; int F=00,OO=00;main(){F_OO();printf("%1.3f ",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ } |
另一个是
1 | c |
要编译,需要将
1 2 | -Dc="#include <stdio.h> int main() { char *t ="Hello World "; while(*t) putc(*t++, stdout); return 0; }" |
在Lucent,我曾经查看过Steve Bourne最初的Unix shell的源代码,发现他使用C预处理器使C看起来像Pascal或Algol。处理if语句的部分如下:
1 2 3 4 5 | #define IF if ( #define THEN ) { #define ELSE } else { #define ELIF } else if ( #define FI ; } |
我的一个朋友告诉我,他在20世纪90年代中期对它做了一些维护,但情况还是一样的。(这里有一个关于代码库固有的保守主义的教训。)
当然,史蒂夫在早期是作为一个实验来做的,如果他以后再写,我肯定会有第二个想法的。
更新:根据维基百科的BourneShell文章,宏给了它一个algol 68风格。而且,全套宏就在这里!他们显然影响了国际模糊C代码竞赛的创始人。
coroutines(又名stackless threads)在c.:)这是邪恶的诡计。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #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; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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个可能的变化(也大多是复制/粘贴,但有轻微的变化,并且有自己的ifdef)。
1 | #define FLASE FALSE |
程序员打字不好,这是他最常犯的错误。
1 | #define interface struct |
在一些optima++头文件中(optima++是我必须使用的watcom/powersoftide)。
必须从记忆中做这件事,但大概是这样的:使用lib编写Symbian应用程序。你需要把这个小宝石藏在一个头文件里:
1 2 3 4 | // Here come the register defines: #define C <something> #define N <something> <two more single letter defines> |
在我们的代码中,加载带有硬编码文件名的文件失败。当我们将文件位置从C更改为D时,它神奇地工作了…
我所看到的最糟糕的情况是在我当前的项目中,有很多情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #if PROGRAMA . . if(...) { . . . #else . . if(...) { . . . #endif } |
是的,他关闭了2个打开一个关闭。
1 2 | #define unless(cond) if(!cond) #define until(cond) while(!cond) |
用于:
1 2 | unless( ptr == NULL) ptr->foo(); |
我正在添加另一个随着时间的推移而开始困扰我的:
1 | #define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0])) |
如果他们正确的话,我已经看到了所有可能的括号排列的版本。我看到它在同一个头文件中定义了两次。
主要是我的论点适用于Windows(尽管我假设其他OS SDK也有类似的东西),在Windows中,几乎每个人都觉得有必要在他们的项目头中定义这个宏,我不明白为什么。
winnt.h(由windows.h提供)定义了一个非常好的版本,如果传递指针类型而不是数组,它会执行一些模板voodoo来导致编译时错误。
当然,如果您正在构建一个C程序,它会回到我上面所写的内容,但是我仍然不会毫无理由地重新定义默认情况下SDK具有的内容。
1 2 3 4 | #define PROCESS_AND_RETURN(X) \ X.process(); \ // Important: Return only after invoking virtual method process() \ return X |
由于"重要"注释,宏永远不会返回对象并崩溃!
看看这个答案,你会发现一个读写困难的同事用一个包含
好的宏:(尽管我个人不喜欢使用这种语法所需的双括号;但我更喜欢vararg宏(仅限C99)或类似printf_0、printf_1等的宏,具体取决于参数的数量)
1 2 3 4 5 | #ifdef DEBUG #define PRINTF(x) printf x #else #define PRINTF(x) #endif |
减少非调试生成的代码大小/执行时间(第一个超过第二个);还可以防止泄漏调试文本字符串,这可能会带来较小的安全风险。
任何使用sendmail及其神奇配置语法的内容
我曾经把这个可怕的C++代码放在一起,它使用宏来帮助钩子函数进入DLL的导入表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | #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; |
这反过来又允许我这样做:
}
[/cc]
这是从一个流行的开源程序中获取的。事实上,它通过隐藏丑陋的遗产使代码的某些部分更具可读性。
1 2 | #define EP_STATUS CASTLING][(BOARD_FILES-2) #define HOLDINGS_SET CASTLING][(BOARD_FILES-1) |
我想这里没有什么不好的地方,我觉得很有趣。
网址:http://git.savannah.gnu.org/cgit/xboard.git/tree/common.h
以前的一位雇主发现在现代的Unix系统上没有basic-plus的实现,所以他们尝试使用C预处理器宏重新实现它:
1 2 3 4 | #define IF if( #define THEN ) { #define ENDIF } #define GOTO goto L |
……等
可怕的。
与雷蒙德的咆哮相关的是以下可怕的(当然,在我看来)宏观:
1 2 3 4 5 6 | #define CALL_AND_CHECK(func, arg) \ int result = func(arg); \ if(0 != result) \ { \ sys.exit(-1); \ } \ |
我对使用宏的实践还相当陌生,并且使用了这个宏,但是我希望传递给它的函数会失败。我是在一个后台线程中做的,所以它困扰了我好几天,为什么我的整个应用程序都"崩溃"。
顺便提一句,如果在编写这个宏时只有std::tr1::函数,我将有一个星期的生命!
1 2 | #define"CR_LF" ' ' |
把我搞糊涂了一段时间!
当我第一次看到C语言中的宏时,它们让我苦恼了好几天。下面是我面临的问题。我认为这对C专家来说是非常有意义的,而且是非常有效的,但是对于我来说,试着弄清楚到底发生了什么意味着将所有不同的宏剪切和粘贴在一起,直到可以看到整个函数为止。那肯定不是好的做法吗?!使用简单的旧函数有什么问题?!
1 2 3 4 5 | #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) |
我曾经看到过一个宏包,它可以为每一个C关键字添加别名,以便在Klingon中有效地编程。没错,克林贡。(联合国)幸运的是,该项目在几年前被废弃并拆除。
BSD内核中的nfs代码在宏之间使用goto。它仍在使用中,而且代码实际上可以工作。我知道有几个人试图清理它,但他们都在一段时间后放弃了-这太混乱了。
你可以在这里看到:http://www.openbsd.org/cgi-bin/cvsweb/src/sys/nfs/nfsm-subs.h?转速=1.43
我10年前使用的ASIC的驱动程序代码有很多部分,看起来像:
1 2 3 4 5 6 7 8 9 10 11 12 13 | int foo(state_t *state) { int a, b, rval; $ if (state->thing == whatever) { $ do_whatever(state); } // more code $ return rval; } |
经过多次的挠头之后,我们终于找到了定义:
1 2 3 4 5 | #if DEBUG #define $ dolog("%s %d", __FILE__, __LINE__); #else #define $ #endif |
这很难找到,因为使用它的源文件中没有包含任何文件。有一个名为
1 2 3 4 | #include <namechanged.h> #include <foo.c> #include <bar.c> #include <baz.c> |
当然,这是makefile中引用的唯一文件。每当您更改任何内容时,都必须重新编译所有内容。这是"使代码更快"。
预处理器的另一种"创造性"使用,尽管它更多地是在术语中使用,而不是在力学中使用(这是非常普通的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /*********************************************************************** * 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 */ |
真正的密码-我以为我已经删除了,但显然没有。我一定是在某个临时分支中这样做的,没有得到将它签回主代码的许可。"待办事项"列表的另一项。
它不是C宏,但…
许多年前,我有一个有趣的任务,把原来的运输大亨从PC移植到Mac。PC版本完全是用汇编程序编写的,所以我们必须先浏览整个源代码并将其导入"PC"C代码,然后再将其导入Mac。大多数代码都是正常的,甚至是面向对象的。然而,世界渲染系统却令人难以置信。对于没有玩过游戏的人来说,可以在三个缩放级别中查看世界。这方面的代码大致如下:
1 2 3 4 5 6 7 8 9 10 11 12 | 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,因为当我们试图组装它时,宏会使汇编程序崩溃。
斯基兹
在声明中发现的,非常混乱:
1 | NON_ZERO_BYTE Fixed(8) Constant('79'X), |
后来发现:
1 2 | IF WORK_AREA(INDEX) = ZERO_BYTE THEN /* found zero byte */ WORK_AREA(INDEX) = NON_ZERO_BYTE ; /* reset to nonzero*/ |
最严重的滥用(我偶尔这么做是有罪的)是使用预处理器作为某种数据文件替换,即:
1 2 3 | #define FOO_RELATION \ BAR_TUPLE( A, B, C) \ BAR_TUPLE( X, Y, Z) \ |
然后在其他地方:
1 2 3 | #define BAR_TUPLE( p1, p2, p3) if( p1 ) p2 = p3; FOO_RELATION #undef BAR_TUPLE |
这将导致:
1 2 | if( A ) B = C; if( X ) Y = Z; |
这种模式可以用来做各种(可怕的)事情…生成switch语句或大型if-else块,或与"real"代码接口。甚至可以使用它来::咳嗽::在非OO上下文菜单系统中生成上下文菜单::咳嗽::。我从来没有做过这么差劲的事。
编辑:修复了不匹配的括号和展开的示例
尝试调试真正喜欢宏的大项目,还有很多宏调用其他宏调用其他宏等等。(5-10级宏并不罕见)
然后再加上大量的ifdef这个macro,或者那个macro,因此,如果您像一棵不同路径的树一样遵循代码,它就可以运行。
唯一的解决方案是,大多数情况下都是预编译并读取它……
当时,将宏作为参数"传递"到另一个宏似乎是个好主意。(我只是无法忍受在多个地方定义一个值列表的想法。)这里的代码是人为的(并不是很有激励性),但给了你一个想法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #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) { // ... } |
我同意在大多数情况下,宏是可怕的,但我发现了一些有用的实例。
这一个实际上是非常出色的imho,因为您只能得到与sprintf类似的东西,然后需要资源分配,而什么都不需要,另外,所有的工作都完全由预处理器完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // 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") :" |
另一个我不喜欢使用,但发现它非常有用的是这样做,它基本上允许我快速生成具有可变数量模板参数的模板。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #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"在文件末尾取消隐藏所有宏,因此宏本身不会干扰任何其他代码,它只会使"callback.inl"编写起来有点困难(不过读取和调试并不太困难)。
我在利比提发现的:
1 2 3 4 5 | /* 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和其他IDE
也许这样比较安全。
我认为他们应该为每个函数加一个前缀,而不是调用宏来完成这项工作。它把代码弄乱了。但也许我错了。你怎么认为。。?
PS:看起来几乎所有的内部函数const和其他函数都使用这个作为前缀。我的同事刚刚告诉我这是平常的……世界跆拳道联盟?也许我错过了什么。
使用标记串联运算符
我看到一个同事很乐意和我一起工作。他们试图定制一个字符串内部的实现,所以他们使用大量宏(当然)不能正常工作的字符串重新实现。为了弄清楚它做了什么,我的眼睛爆炸了,因为所有的
我将头文件用作大宏:
1 2 3 4 5 6 7 | // compile-time-caller.h #define param1 ... #define param2 ... #include"killer-header.h" // killer-header.h // uses param1 and param2 |
我还创建了递归头文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // 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 |
1 2 | #undef near #undef far |
当我刚开始游戏编程时,我正在为一个照相机类编写一个截锥,这是我编写的一个游戏,我的代码中有一些奇怪的错误。
事实证明,微软在windows.h中对near和far有一些定义,这导致了我的near和far变量在包含它们的行中出错。很难找到问题所在,因为(当时我是个新手),整个项目中只有四条线路,所以我没有马上意识到。
我不喜欢Boost预处理器。我曾经试图弄清楚如何使用它(无论如何,我们在项目中得到了提升…),但尽我所能,使用它会使我的错误消息变得难以阅读,不值得这样做。
我喜欢类似于循环宏的想法,但这太过分了。
1 | #define protected private |
有时似乎是个好主意,但如果需要,您可能只需要字符串替换。受保护是相当邪恶的,允许内部访问后代并不比公开项目好多少…
请温柔一点,我写这篇文章是我能想到的捕获异常的唯一方法。
我使用它来捕获并阻止异常从我的公共接口函数传播出去…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /// 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)。
所以,它是让你跑起来藏起来的,还是让你做这种事的唯一方法?
基本上…
1 2 3 4 | try{ // some block of code capable of throwing } CatchAll() |
任何使用标记串联运算符的内容。我曾经看到过,在C++中使用PSUDO模板系统和其他可怕的东西。使用它最糟糕的是你的错误信息变得多么不可能神秘。
不过,我看到了一个很好的用途。有一个宏#监视器组件(类名)它在编译时生成的类将从一个预定义的监视器类和类名继承,并将自动注册一个用于监视每个组件的单例类。
它起作用了吗?是的,这是最好的方法吗?大概不会。