worstreal-worldマクロ/プリプロセッサの悪用とは何ですか(不自然なIOCCCの回答はありません*笑*)?
本当に面白い場合は、短いスニペットまたはストーリーを追加してください。目標は、常に「マクロを使用しない」ことを人々に伝えるのではなく、何かを教えることです。
p.s .:以前にマクロを使用したことがありますが、通常、「実際の」解決策があれば最終的にそれらを取り除きます(実際の解決策がインライン化されてマクロに似ている場合でも)。
ボーナス:マクロが非マクロソリューションよりも優れていた例を示します。
関連する質問: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ではなく、構文の色付けがあります!)
彼はほとんどアセンブリ言語で働いていたロシアのプログラマーでした。彼は以前は非常に限られたメモリのシステムで働いていたため、可能な限り多くのバイトを節約することに夢中でした。 「衛星用でした。ごくわずかなバイトしかないので、各バイトを多くのことに使います。」 (ビット操作、数値に機械命令バイトを再利用)どんな種類の衛星を見つけようとしても、「軌道衛星を軌道に乗せるために」しか取得できませんでした。
彼には他にも2つの癖がありました。「誰が見ているのかを知るために」モニターの上に取り付けられた凸面鏡と、10の腕立て伏せをするために時々椅子から突然出ることです。彼はこの最後のことを「コンパイラがコードでエラーを見つけました。これは罰です」と説明しました。
私の最悪:
#define InterlockedIncrement(x) (x)++
#define InterlockedDecrement(x) (x)--
馬鹿者がこれをヘッダーファイルに入れたため、マルチスレッドCOM参照カウントの問題の追跡に2日間を費やしました。当時働いていた会社については言及しません。
この物語の教訓は?何かを理解できない場合は、ドキュメントを読んでそれについて学んでください。ただ消さないでください。
#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には、max
およびmin
と呼ばれるminmax.hのマクロがあり、標準のstd::numeric_limits<T>::max()
関数を使用するたびにコンパイラエラーが発生します。
Pascal構文とフランス語のキーワードの組み合わせ:
#define debut {
#define fin }
#define si if(
#define alors ){
#define sinon }else{
#define finsi }
レイモンド・チェンには、 フロー制御マクロの使用に対して本当に良い暴言 があります。彼の最良の例は、元の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をコンテストに提出したいと思います。
例の1つは、プリプロセッサによって完全に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
これはとても悪いことです。ランダムです。つまり、常に異なる場所で起動し、returnステートメントを変更します。通常は、それ自体に失敗する可能性のあるコードがあります。 stdスペースからの例外なので、ソースを検索してソースを見つけることはできません。ただ素晴らしい。
同僚と私は、オブジェクトストリーミング用のコードの一部でこれら2つの宝石を見つけました。これらのマクロは、ストリーミングを行うクラスファイルEVERY SINGLEでインスタンス化されました。この恐ろしいコードがコードベース全体に吐き出されるだけでなく、元の作者に問い合わせたとき、彼は内部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インタープリターには、リストの最初の要素を返すためにLISPで使用される関数carと、リストの残りを返す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で頻繁に使用され、短く、実装言語では関数呼び出しがあまり速くないため、これら2つの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))
私はかつて、UnixからCアプリケーションを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)は、マルコ地獄、変な名前、古英語の専門用語など、同様のスタイルで記述されました。幸いなことに、同じアルゴリズムを実行する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
これにより、class
というメンバー変数を含むC構造体をC++コンパイラで処理できます。このコンストラクトを含む2つのヘッダーがあります。それらの1つには、最後に「#undefクラス」が含まれ、もう1つには含まれていません。
国際難読化Cコーディングコンテストの1年間で、プログラム全体が次のようなエントリになりました。
P
ただし、メイクファイルでP
を任意のプログラムに定義できるという条件があります。
私が覚えているように、それはカテゴリーの1つで勝ちました、そして、翌年、規則は現れて、そのスタイルのエントリーを禁止しました。
(編集:6か月後か何か...私がこれを書いたとき、「No 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 ...ため息を呼ぶオーバーヘッドが欲しくないので)。
彼らは日本語の文字には機能しないので、最初にASCIIまたはUnicodeを実行するために「if」を追加しました。その時点で、コードは画面の長さ程度でした...キャッシュの一貫性を破壊し、コードのインライン化のために貯金を削除していた可能性があります。
コードは、型を除いて同一でした(マクロを使用する必要がありました)。
もちろん、彼らが書いたstrcpyは、標準ライブラリにある手動で調整されたアセンブラーよりもはるかに遅かった...
もちろん、すべてをマクロとして実行した場合は、strcpyの呼び出しで置き換えることができます。
もちろん、私は会社を辞めました(そのため直接ではありません...)
#define TRUE 0 // dumbass
これを行った人は、数年後に自分自身について説明しました-すべてではないにしてもほとんどのCライブラリ関数は、すべてがうまくいったことを示すものとして0を返します。そこで、彼は次のようなコードを書きたいと思っていました。
if (memcpy(buffer, packet, BUFFER_SIZE) == TRUE) {
; // rape that packet
}
言うまでもなく、私たちのチーム(テスターまたは開発者)の誰も彼のコードをあえて見たことがありません。
義務
#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 TWO_HUNDRED_AND_EIGHTY_THREE_POINT_ONE 283.1
#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!");
}
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++プロジェクトに次のwonderfulマクロを導入しました。DOM解析ルーチンでNULL
値をチェックするのはあまりにも大変だと思ったためです。
TRYSEGV
CATCHSEGV
カバーの下で、これらはsetjmp
、longjmp
、およびSIGSEGV
の-信号ハンドラーを使用して、セグメンテーション違反を「キャッチ」する機能をエミュレートしました。
もちろん、コードが元のTRYSEGV
マクロ呼び出しのスコープを抜けると、コード内のジャンプがリセットされることはないため、コード内のany segfaultは(現在は無効)に戻ります。 jump_env
ポインター。
コードはそこですぐに死にますが、プログラムスタックを破壊し、デバッグを多かれ少なかれ無意味にする前ではありません。
AI Game Programming Wisdom には、有限状態マシン用のスクリプト言語を作成するためにマクロが使用された章があります。
本とコードは著作権で保護された資料であるため、マクロの詳細を示すページへの Googleブックリンク です(結果のスクリプト言語は324ページにあります)。
ルーセントで、私はかつてSteve Bourneの元のUnixシェルのソースコードを見て、Cプリプロセッサを使用してCを次のように見せていたことがわかりました。 パスカルまたは アルゴル。 ifステートメントを扱う部分は次のようになりました。
#define IF if (
#define THEN ) {
#define ELSE } else {
#define Elif } else if (
#define FI ; }
私の友人は、1990年代半ばにいくつかのメンテナンスを行ったと言っていましたが、それでも同じでした。 (ここには、コードベースの固有の保守主義に関する教訓があります。)
もちろん、スティーブは初期の段階でこれを実験として行っており、彼が後でそれを書いていたなら、私は間違いなく考えていただろう。
更新:ウィキペディアの 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()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_
_-_-_-_
}
もう1つはc
プログラムです
c
コンパイルするには、c
を次のように定義する必要があります。
-Dc="#include <stdio.h> int main() { char *t =\"Hello World\n\"; while(*t) putc(*t++, stdout); return 0; }"
Cのコルーチン(別名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
プログラマーは悪いタイピストであり、これは彼の最も一般的な間違いでした。
これはメモリから行う必要がありますが、次のようなものでした。Symbianアプリを作成するためのlibの使用。ヘッダーファイルに隠されている必要は、この小さな宝石でした:
// Here come the register defines:
#define C <something>
#define N <something>
<two more single letter defines>
このコードでは、ハードコードされたファイル名のファイルの読み込みに失敗しました。ファイルの場所をCドライブからDドライブに変更すると、魔法のように機能しました...
私が見た最悪の例は、現在のプロジェクトで、次のようなケースがたくさんあります。
#if PROGRAMA
.
.
if(...)
{
.
.
.
#else
.
.
if(...)
{
.
.
.
#endif
}
ええ、彼は2つのオープンを1回のクローズで閉じます。
#define unless(cond) if(!cond)
#define until(cond) while(!cond)
中古:
unless( ptr == NULL)
ptr->foo();
これは人気のあるオープンソースプログラムから取られています。実際、itいレガシーを隠すことでコードの一部を読みやすくします。
#define EP_STATUS CASTLING][(BOARD_FILES-2)
#define HOLDINGS_SET CASTLING][(BOARD_FILES-1)
ここには本当に悪いことは何もないと思います。ただ面白いと思います。
私が最初に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)
Sendmailとその魔法の設定構文を使用するもの
#define PROCESS_AND_RETURN(X) \
X.process(); \
// Important: Return only after invoking virtual method process() \
return X
「重要」なコメントのため、マクロはオブジェクトとクラッシュを返しません。
この回答 を参照してください。失読症の同僚が#define fasle false
のようなものでいっぱいの共通のヘッダーファイルを使用して、どのように自分たちの生活を楽にしたかを確認してください。
DLLのインポートテーブルに関数をフックするのに役立つマクロを使用したこの恐ろしいC++コードをまとめました。
#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);
</ code>
}
過去の雇用者は、最新のUnixシステムには BASIC-PLUS の実装がないことを発見したため、Cプリプロセッサマクロを使用して再実装しようとしました。
#define IF if(
#define THEN ) {
#define ENDIF }
#define GOTO goto L
...等。
恐ろしい。
私は時間をかけて私を困らせ始めた別のものを追加しています:
#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
そして、それは彼らがそれを正しくする場合です。カッコのすべての可能な組み合わせが存在するかどうかのバージョンを見てきました。同じヘッダーファイルで2回定義されているのを見ました。
主に私の議論はWindowsに当てはまります(他のOS SDKにも同様のものがあると思いますが)、ほとんどの人がプロジェクトのヘッダーでこのマクロを定義する必要性を感じているようで、その理由はわかりません。
WinNT.h(Windows.hに含まれています)は、配列の代わりにポインター型を渡すとコンパイル時間エラーを引き起こすテンプレートブードゥーを行う非常に素晴らしいバージョンを定義します。
もちろん、あなたがCプログラムを構築している場合、私が上で書いた通りにフォールバックしますが、理由もなくSDKがデフォルトで持っているものを再定義しません。
良いマクロ:(個人的には、この構文を使用するために必要な二重括弧は嫌いですが、引数の数に応じて、可変引数マクロ(C99のみ)またはPRINTF_0、PRINTF_1などのようなものを好みます)
#ifdef DEBUG
#define PRINTF(x) printf x
#else
#define PRINTF(x)
#endif
非デバッグビルドのコードサイズ/実行時間(最初のコードが2番目のコードよりも長い)を削減します。また、わずかなセキュリティリスクを引き起こす可能性のあるデバッグテキスト文字列の漏洩を防ぎます。
#define "CR_LF" '\r'
それはしばらくの間私から地獄を混乱させました!
レイモンドの暴言に関連するのは、次の恐ろしい(もちろん、私の意見では)マクロです。
#define CALL_AND_CHECK(func, arg) \
int result = func(arg); \
if(0 != result) \
{ \
sys.exit(-1); \
} \
私はマクロを使う慣習にかなり慣れていなかったのでこのマクロを使いましたが、expected失敗するために渡した関数です。そして、私はバックグラウンドスレッドでそれを行っていたので、アプリ全体が「クラッシュ」する理由が何日もありました。
余談ですが、このマクロが作成されたときにstd :: tr1 :: functionのみが存在していた場合、1週間の人生が戻ってきます。
Cマクロではありませんが...
何年も前、私はオリジナルのTransport TycoonをPCからMacに移植するという楽しい仕事をしていました。 PCバージョンは完全にアセンブラーで記述されているため、ソースコード全体を調べて「PC」Cコードに移植してから、Macに移植する必要がありました。ほとんどのコードは問題ありませんでした。しかし、世界のレンダリングシステムは信じられないほどでした。ゲームをプレイしていない人は、3つのズームレベルのいずれかで世界を見ることができます。このためのコードは、次の行に沿ったものでした。
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の少し古いバージョンを使用していたはずです。
スキズ
最悪の悪用(およびこれを時折行うことで有罪です)は、何らかの種類のデータファイルの置き換えとしてプリプロセッサを使用することです。
#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ステートメントを生成するか、他の場合は巨大なブロックを生成します。それを使用して、:: cough ::非ooコンテキストメニューシステム:: cough ::でコンテキストメニューを生成することもできます。私がこれまでに何か下手なことをしたことはありません。
編集:括弧の不一致と展開例を修正
マクロを本当に愛する大きなプロジェクトをデバッグしてみてください。他のマクロなどを呼び出す他のマクロを呼び出すマクロがたくさんあります(5〜10レベルのマクロはそれほど珍しくありませんでした)
そして、多くの#ifdefこのマクロを#elseそのマクロで補うので、コードに従うと、別のパスのツリーのようになります。
唯一の解決策は、ほとんどの場合、代わりにプリコンパイルして読み取ることです。
クリンゴンで効果的にプログラムできるように、すべてのCキーワードをエイリアス化するマクロパッケージを見たことがあります。そう、クリンゴン。 (残念なことに)数年前にプロジェクトは放棄され、廃止されました。
プリプロセッサのもう1つの「創造的な」使用方法は、メカニクス(信じられないほどありふれたもの)よりも採用されている用語の方が多くなっています。
/***********************************************************************
* 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 */
本物のコード-私はそれを削除したと思ったが、明らかにそうではなかった。私は一時的なブランチでそうしなければならず、それをメインコードにチェックインする許可を得ていなかったはずです。 「やること」リストのもう1つの項目。
当時、マクロを引数として別のマクロに「渡す」ことは良い考えのように思えました。 (複数の場所で値のリストを定義するという考えに我慢できませんでした。)ここのコードは不自然です(あまり動機付けられていません)が、考えを与えてくれます。
#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)
{
// ...
}
BSDカーネルのNFSコードは、マクロ間でgotoを使用します。まだ使用されており、コードは実際に機能します。私はそれを片付けようとした数人の人を知っています、しかし、彼ら全員はしばらく後にあきらめました-それはあまりにも面倒です。
ここで見ることができます: http://www.openbsd.org/cgi-bin/cvsweb/src/sys/nfs/nfsm_subs.h?rev=1.4
約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
これを使用するソースファイルにはany includeファイルがないため、これを見つけるのは困難でした。次のようなtop.c
ソースファイルというファイルがありました。
#include <namechanged.h>
#include <foo.c>
#include <bar.c>
#include <baz.c>
案の定、これはMakefileで参照される唯一のファイルでした。何かを変更するたびに、everythingを再コンパイルする必要がありました。これは「コードを高速化する」ことでした。
ほとんどの場合、マクロは使用するのが恐ろしいことには同意しますが、マクロが役立ついくつかの例を見つけました。
これは実際に素晴らしい私見です.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」はファイルの最後にあるすべてのマクロを#undefするため、マクロ自体は他のコードと干渉せず、「Callback.inl」を書くのが少し難しくなります。 (ただし、読み取りとデバッグはそれほど難しくありません)
宣言で見つかりましたが、かなり混乱しています。
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*/
#undef near
#undef far
ゲームプログラミングを始めたばかりの頃、カメラクラスのフラスタムを書いていたので、自分が書いたゲームです。コードに本当に奇妙なエラーがありました。
Microsoftはwindows.hでnearとfarの#definesをいくつか持っていたため、_near変数と_far変数を含む行でエラーが発生しました。問題を追跡することは非常に困難でした(当時私は初心者でした)が、プロジェクト全体で4行しか存在しなかったため、すぐにはわかりませんでした。
ヘッダーファイルを大きなマクロとして使用しました。
// compile-time-caller.h
#define param1 ...
#define param2 ...
#include "killer-header.h"
// killer-header.h
// uses param1 and param2
recursiveヘッダーファイルも作成しました。
// 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
#define protected private
良いアイデアのように思えるかもしれませんが、必要な場合は、とにかく単に文字列を置換する必要があります。 Protectedはかなり悪であり、子孫への内部アクセスを許可することは、単にアイテムを公開することほど良くありません...
トークン連結演算子##
を使用するマクロ。
私の同僚が一緒に仕事をする喜びを持っている人を見ました。彼らは文字列インターンのカスタム実装を試みたので、(もちろん)適切に動作しなかった膨大な数のマクロを使用して文字列を再実装しました。 ##
が散在しているために、それが私の目を爆発させた原因を理解しようとしました。
優しくして、これを例外を一般的にキャプチャする唯一の方法として書いた。
私はそれを使用して、例外がキャプチャされ、パブリックインターフェイス関数から伝達されないようにします...
/// 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);} \
}
私は複雑さを嫌い、マクロは嫌いですが、汎用キャッチハンドラを他にどのように「行う」でしょうか。これはすべてを意味するものではありません。これは、レガシーパブリック関数を隔離し、関数が呼び出されることを知っているときに少なくともある程度のレベル保護をすばやく追加するための単なる汎用キャッチハンドラーです。 C++例外がスローされる場合にスローされる可能性のある境界(ようこそ、JNI)。
だからそれはあなたを走らせて隠しますか、それともこのようなことをする唯一の方法ですか?
基本的に...
try{
// some block of code capable of throwing
}
CatchAll()
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およびその他のgo to definition
およびgo to declaration
機能が#define TY_(...)
のみを検出し、必要なDocParseStream
宣言を検出しないことです。
この方法の方が安全かもしれません。
私は彼らが各機能に接頭辞を置くべきであり、仕事をするためにマクロを呼び出すべきではないと思います。それはコードを乱雑にしています。どう思いますか..?
追伸:ほとんどすべての内部関数constやその他の接頭辞はこれを使用して接頭辞が付けられているようです。たぶん私は何かを見逃した。