Quel est le pire monde réel abus de macros/pré-processeur que vous ayez jamais rencontré (s'il vous plaît, pas artificiel) IOCCC répond * haha *)?
S'il vous plaît ajouter un extrait ou une histoire si c'est vraiment amusant. Le but est d’enseigner quelque chose au lieu de toujours dire aux gens "ne jamais utiliser de macros".
p.s .: J'ai déjà utilisé des macros auparavant ... mais d'habitude, je m'en débarrasse éventuellement lorsque j'ai une "vraie" solution (même si la vraie solution est en ligne, elle devient similaire à une macro).
Bonus: Donnez un exemple où la macro était vraiment meilleure qu'une solution non macro.
Question connexe: Quand les macros C++ sont-elles utiles?
De mémoire, cela ressemblait à quelque chose comme ça:
#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)
Oui c'est vrai, pas d'accolades de fermeture dans aucune des fonctions. La mise en évidence de la syntaxe était un désordre, il a donc utilisé vi pour éditer (pas vim, il a la coloration de la syntaxe!)
C'était un programmeur russe qui avait principalement travaillé en langue de montage. Il était fanatique de sauvegarder autant d'octets que possible car il avait déjà travaillé sur des systèmes avec une mémoire très limitée. "C’était pour le satellite. Seulement très peu d’octets, nous utilisons donc chaque octet pour beaucoup de choses." (peu bidouiller, réutiliser des octets d'instructions machine pour leurs valeurs numériques) Lorsque j'ai essayé de savoir quels types de satellites, je ne pouvais obtenir que "Satellite en orbite. Pour se mettre en orbite."
Il avait deux autres bizarreries: un miroir convexe monté au-dessus de son moniteur "Pour savoir qui regarde" et une sortie soudaine et occasionnelle de son fauteuil pour effectuer une dizaine de pompes. Il a expliqué ce dernier comme "Le compilateur a trouvé une erreur dans le code. C'est une punition".
Mon pire:
#define InterlockedIncrement(x) (x)++
#define InterlockedDecrement(x) (x)--
J'ai passé deux jours de ma vie à rechercher un problème de comptage de références COM multi-thread car un idiot l'a mis dans un fichier d'en-tête. Je ne mentionnerai pas la compagnie pour laquelle j'ai travaillé à l'époque.
La morale de cette histoire? Si vous ne comprenez pas quelque chose, lisez la documentation et découvrez-la. Ne vous contentez pas de le faire partir.
#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!");
}
Défi: Quelqu'un peut-il le faire avec moins de définitions et de structures? ;-)
#define private public
#define if while
C'était une blague jouée sur quelqu'un, ça n'a pas été trouvé amusant par les personnes touchées
Les hideux:
#define begin {
#define end }
/* and so on */
Sérieusement, si vous voulez coder en Pascal, acheter un compilateur Pascal, ne détruisez pas le beau langage C.
Un "architecte", un gars très humble, vous connaissez le type, avait ce qui suit:
#define retrun return
parce qu'il aimait taper vite. Le chirurgien du cerveau aimait crier sur des gens plus intelligents que lui (ce qui était à peu près tout le monde) et menaçait de porter sa ceinture noire sur eux.
Monde réel? MSVC a des macros dans minmax.h, appelées max
et min
, qui entraînent une erreur du compilateur chaque fois que j'ai l'intention d'utiliser la fonction standard std::numeric_limits<T>::max()
.
Un mélange de syntaxe Pascal et de mots-clés français:
#define debut {
#define fin }
#define si if(
#define alors ){
#define sinon }else{
#define finsi }
Raymond Chen a un vraiment bon discours contre l'utilisation de macros de contrôle de flux . Son meilleur exemple est directement tiré du code source original de 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
}
Je souhaite soumettre au concours une gemme appelée chaos-pp , qui implémente un langage fonctionnel au moyen des macros du préprocesseur.
Un des exemples est le calcul du 500ème nombre de fibonacci entièrement par le pré-processeur:
Le code original avant le préprocesseur ressemble à ceci:
int main(void) {
printf
("The 500th Fibonacci number is "
ORDER_PP(8stringize(8to_lit(8fib(8nat(5,0,0)))))
".\n");
return 0;
}
en prétraitant le fichier, nous obtenons le résultat suivant (après une attente assez longue):
$ 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;
}
Directement à partir de Qt:
#define slots /* */
#define signals /* */
Vraiment sympa d’interagir avec d’autres bibliothèques comme boost :: signaux ... Juste un exemple, beaucoup d’autres dans Qt créent un code très drôle, comme:
class X : public QObject {
Q_OBJECT
private slots:
//...
public signals:
//...
};
Et c'est C++ ... mais tout à coup:
boost::signals::trackable
N'est plus valide C++.
Windows.h a beaucoup de fonctions qui abusent des macros.
MrValdez est agacé par la macro GetObject trouvée dans Windows.h
La macro GetObject modifie la fonction GetObject () en GetObjectA () ou GetObjectW () (selon que la génération est compilée en non-unicode et en unicode, respectivement)
MrValdez déteste avoir à faire avant la ligne de fonction GetObject
#undef GetObject
Object *GetObject()
L'alternative est de changer le nom de la fonction en quelque chose comme GetGameObject ()
jdkoftinoff dans les commentaires l'a bien compris: le problème est que toutes les fonctions de l'API Windows sont des macros.
Adam Rosenfield a indiqué que les problèmes peuvent être résolus en définissant NOGDI, WIN32_LEAN_AND_MEAN, NOMINMAX, etc. avant d'inclure windows.h pour les supprimer.
#define return if (std::random(1000) < 2) throw std::exception(); else return
c'est tellement diabolique. C'est aléatoire, ce qui signifie qu'il se déclenche à des endroits différents tout le temps, il modifie l'instruction return, qui contient généralement du code qui pourrait échouer tout seul. exception de l'espace std afin que vous n'essayiez pas de chercher dans vos sources pour trouver sa source. Tout simplement génial.
Un collègue et moi avons trouvé ces deux joyaux dans certains de nos codes pour la diffusion en continu d'objets. Ces macros ont été instanciées dans EVERY SINGLE fichier de classe qui diffusait en continu. Non seulement ce code hideux est répandu dans notre base de code, mais lorsque nous avons contacté l'auteur à ce sujet, il a écrit un article de 7 pages sur notre wiki interne, le défendant comme le seul moyen possible d'accomplir ce qu'il tentait de faire ici.
Inutile de dire que depuis, il a été refait et n'est plus utilisé dans notre base de code.
Ne vous laissez pas impressionner par les mots-clés en surbrillance. C’est TOUT une macro
#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> )
Mise à jour (17 décembre 2009):
Plus de bonnes nouvelles concernant cet auteur de macro affreux. À partir d'août, l'employé responsable de cette monstruosité a été limogé.
J'ai moi-même fait ce qui suit et je pense en avoir tiré quelque chose.
En 1992 environ, j'ai écrit un petit interprète LISP. Cela n'a pas été implémenté en C normal, mais dans un langage interprété semblable à C. Ce langage C-like utilisait le pré-processeur standard C, cependant.
Bien entendu, l'interpréteur LISP contenait les fonctions car, qui sont utilisées dans LISP pour renvoyer le premier élément d'une liste, et cdr, qui renvoie le reste de la liste. Ils ont été implémentés comme ceci:
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 */
(Les données étaient stockées dans des tableaux car il n'y avait pas de structure. CONS_OFFSET est la constante 1000.)
car et cdr sont fréquemment utilisés dans LISP, ils sont courts et, comme les appels de fonction n'étaient pas très rapides dans le langage d'implémentation, j'ai optimisé mon code en implémentant ces deux LISP. fonctionne comme des macros:
#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 vérifie que son argument est en fait une liste, et comme celui-ci est également utilisé fréquemment dans l'interpréteur et qu'il est court, j'ai aussi écrit celui-ci sous forme de macro:
#define CHECK_CONS(fun, pos, arg) \
(!IS_CONS(arg) ? \
LISP_ERROR("Arg " + pos + " to " + fun + \
" must be a list: " + lispid2string(arg)) : 0)
IS_CONS et LISP_ERROR étaient également utilisés fréquemment, je les ai donc également transformés en macros:
#define IS_CONS(id) \
( intp(id) && (id) >= CONS_OFFSET \
&& ((id) - CONS_OFFSET) < sizeof(cons_cars))
#define LISP_ERROR(str) (throw((str) + "\n"))
Semble raisonnable?
Mais alors, pourquoi tout le système est-il tombé en panne sur cette ligne:
id2 = car(car(car(car((id1))));
J'ai travaillé longtemps pour trouver le problème, jusqu'à ce que je vérifie enfin à quoi cette ligne courte a été étendue par le pré-processeur. Elle a été étendue à une ligne de 31370 caractères, que j'ai divisée en 502 lignes pour plus de clarté:
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))
Une fois, j’ai dû transférer une application C d’Unix vers Windows, dont la nature spécifique doit rester anonyme pour protéger les coupables. Le type qui l’a écrit était un professeur qui n’était pas habitué à écrire du code de production et qui était clairement entré en langage C dans une autre langue. Il se trouve également que l'anglais n'était pas sa langue maternelle, bien que le pays dans lequel il était originaire parle très bien.
Son application a beaucoup utilisé le pré-processeur pour transformer le langage C en un format qu'il pourrait mieux comprendre. Mais les macros qu'il a le plus souvent utilisées ont été définies dans un fichier d'en-tête nommé 'Thing.h' (sérieux), qui inclut les éléments suivants:
#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 ->
... qu'il utilisait ensuite pour écrire des monstruosités comme suit:
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);
}
L'ensemble du projet (~ 60 000 LOC) a été écrit dans un style similaire: marco hell, noms étranges, jargon Olde-anglais, etc. Heureusement, nous avons pu supprimer le code car j'ai trouvé une bibliothèque OSS qui exécutait le même algorithme des dizaines de fois plus vite.
(J'ai copié et édité cette réponse que j'avais initialement faite sur cette question ).
Le pire que j'ai jamais rencontré était dans un produit contenant une suite de fichiers exécutables pour lesquels le responsable technique désigné n'avait pas trouvé de bibliothèques.
Au lieu de cela, il avait des ensembles de fichiers partagés dans plusieurs dossiers Visual Source Safe. Il s'est ensuite rendu compte qu'ils devaient se comporter légèrement différemment pour chaque application.
Vous pouvez appliquer ici un certain nombre d'étapes de refactoring.
Au lieu de cela, il a utilisé #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
}
L'utilisation du préprocesseur LINE pour générer un identifiant unique pour les messages transmis sur le réseau:
NetworkMessages.h
#define MSG_LOGIN __LINE__
#define MSG_LOGOUT __LINE__
#define MSG_CHAT __LINE__
Voici un exemple où la macro était vraiment préférable à une solution non macro:
Dans une classe de solutions non-macro, les fonctions et les variables doivent être créées pour garder une trace de l'ID du message. Le développeur peut ou non rendre compliqué le suivi des identifiants de messages, alors qu'il est plus facile à lire et à déboguer.
En outre, il est plus facile d'ajouter de nouveaux messages en ajoutant simplement le message dans la source.
L'inconvénient de cette situation est que le fichier doit être inclus dans tout le code utilisant des messages. Le temps de compilation augmenterait chaque fois qu'un message est édité.
Un assez mauvais exemple:
#ifdef __cplusplus
#define class _vclass
#endif
Cela permet à une structure C contenant une variable membre appelée class
d'être gérée par un compilateur C++. Il y a deux en-têtes avec cette construction dedans; l'un d'eux contient aussi 'classe #undef' à la fin et l'autre pas.
Au cours d'une année du concours international de codage C obscurci, il y avait une entrée où le programme entier était:
P
Sous réserve que vous puissiez définir P
dans le fichier Make comme étant le programme que vous souhaitiez.
Si je me souviens bien, il a gagné dans l'une des catégories et l'année suivante, une règle est apparue interdisant ce style d'entrée.
(Edit: six mois plus tard ou quelque chose comme ça ... je suis sûr que la question "Aucun IOCCC" n'était pas dans la question principale quand j'ai écrit ça ...)
Je m'ennuyais un jour et je jouais avec des blocs dans 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)
permettant des choses "intéressantes" comme:
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)))));
(certaines définitions de fonctions et de classes ne sont pas montrées par souci de brièveté)
Le pire que j'ai vu était le non-usage :-)
Quelqu'un a écrit une fonction strcpy (je pense que c'était ça ... il y a plus de 10 ans maintenant) à l'intérieur d'une méthode (parce qu'ils ne voulaient pas avoir à surcharger d'appeler strcpy ... soupir).
Ils ont compris que cela ne fonctionnerait pas pour les caractères japonais, ils ont donc ajouté un "if" au début pour faire ASCII ou Unicode. À ce stade, le code concernait un long écran ... tue probablement la cohérence du cache et efface ses économies supposées pour l’insertion du code.
Le code était identique, sauf pour les types (il aurait donc fallu utiliser une macro).
Bien sûr, la structure qu'ils ont écrite était beaucoup plus lente que celle de l'assembleur réglé à la main qui se trouvait dans la bibliothèque standard ...
Bien sûr, s'ils venaient de tout faire en macro, ils auraient pu être remplacés par un appel à strcpy ...
Bien sûr, j'ai quitté l'entreprise (pas directement à cause de ça ...)
#define TRUE 0 // dumbass
La personne qui a fait cela s’est expliquée quelques années plus tard - la plupart (sinon la totalité) des fonctions de bibliothèque C renvoient 0, ce qui indique que tout s’est bien passé. Donc, il voulait pouvoir écrire du code comme:
if (memcpy(buffer, packet, BUFFER_SIZE) == TRUE) {
; // rape that packet
}
Il va sans dire que personne dans notre équipe (testeur ou développeur) n'a jamais osé jeter un coup d'œil sur son code.
Le obligatoire
#define FOR for
et
#define ONE 1
#define TWO 2
...
Qui savait?
Je maintiens le code qui a gotos dans les macros. Donc, une fonction aura une étiquette à la fin mais pas visible dans le code de la fonction. Pour aggraver les choses, la macro se trouve généralement à la fin des autres déclarations, sauf si vous faites défiler horizontalement.
#define CHECK_ERROR if (!SomeCondition) goto Cleanup
void SomeFunction()
{
SomeLongFunctionName(ParamOne, ParamTwo, ParamThree, ParamFour); CHECK_ERROR
//SomeOtherCode
Cleanup:
//Cleanup code
}
Par un camarade de classe qui n'a pas compris les règles concernant les nombres magiques:#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
Il faut vraiment le télécharger pour l'apprécier. L'ensemble du flux de travail est déterminé par les macros. C'est complètement illisible. Par exemple -
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 {
etc.
Et ce n'est que la configuration des options. le programme entier est comme ça.
Un "responsable technique" qui avait été codeur a introduit les macros merveilleuses dans notre projet C++, car il pensait que vérifier les valeurs de NULL
dans les routines d'analyse DOM était une tâche bien trop ardue:
TRYSEGV
CATCHSEGV
Sous les couvertures, ceux-ci utilisaient setjmp
, longjmp
et un gestionnaire de signaux pour SIGSEGV
pour imiter la possibilité de "capturer" une erreur de segmentation.
Bien sûr, rien dans le code ne réinitialise le saut pointé une fois que le code est sorti de la portée de l'invocation de macro d'origine TRYSEGV
, donc toute segfault dans le code reviendrait à la ) jump_env
pointeur.
Le code mourrait immédiatement là-bas, mais pas avant de détruire la pile de programmes et de rendre le débogage plus ou moins inutile.
AI Game Programming Wisdom contient un chapitre dans lequel les macros ont été utilisées pour créer un langage de script pour les machines à états finis.
Le livre et le code étant protégés par des droits d'auteur, voici un lien du livre Google vers la page détaillant les macros (Le langage de script obtenu est disponible à la page 324.)
Une fois chez Lucent, j’ai jeté un coup d’œil au code source du shell Unix original de Steve Bourne et j’ai découvert qu’il avait utilisé le pré-processeur C pour lui donner l’impression suivante: Pascal ou ALGOL. La partie traitant des déclarations si ressemblait à ceci:
#define IF if (
#define THEN ) {
#define ELSE } else {
#define Elif } else if (
#define FI ; }
Un de mes amis m'a dit qu'il en avait fait l'entretien au milieu des années 90 et que c'était toujours pareil. (Nous pouvons tirer une leçon du conservatisme inhérent à une base de code.)
Bien sûr, Steve a fait cela à titre expérimental au début, et je suis sûr qu’il aurait hésité s’il l’avait écrit plus tard.
Mise à jour: Selon Wikipedia article de Bourne Shell , les macros lui ont donné une saveur ALGOL 68 . Et, le ensemble complet de macros est ici! Ils ont apparemment influencé les fondateurs du concours international de code C Obfuscated.
J'aime cet exemple, il utilise la macro pour approximer la valeur de PI. Plus le cercle est large, plus l'approximation est précise.
#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
_-_-_-_
_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_
_-_-_-_
}
Un autre est le programme c
c
Pour compiler, vous devez définir c
comme
-Dc="#include <stdio.h> int main() { char *t =\"Hello World\n\"; while(*t) putc(*t++, stdout); return 0; }"
Coroutines (threads AKA Stackless) dans C. :) C'est une supercherie.
#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
(Les noms ont été modifiés pour protéger les moins innocents)
Notez que nous n'avons même pas encore eu de code, c'est juste pour arriver au premier bit de code. Cela se produit réellement (de manière presque identique, mais pas exactement de la même manière) pour plusieurs fonctions, chacune d’entre elles n’ayant en fin de compte que 4 variations possibles (qui sont aussi principalement des fonctions copier/coller avec de légères variations et des #ifdefs propres).
#define interface struct
dans certains en-têtes Optima ++ (Optima ++ est/était un Watcom/Powersoft IDE je devais travailler avec).
#define FLASE FALSE
Le programmeur était un mauvais dactylographe et ce fut son erreur la plus commune.
Je devais le faire de mémoire, mais c'était à peu près comme ceci: Travailler avec une lib pour écrire des applications Symbian. Caché dans un fichier d'en-tête, vous nécessaire à inclure, était ce petit bijou:
// Here come the register defines:
#define C <something>
#define N <something>
<two more single letter defines>
Dans notre code, le chargement d'un fichier avec un nom de fichier en dur a échoué. Lorsque nous avons changé l'emplacement du fichier de C à D, cela a fonctionné comme par magie ...
Le pire que j'ai vu est dans mon projet actuel où il y a beaucoup de cas de:
#if PROGRAMA
.
.
if(...)
{
.
.
.
#else
.
.
if(...)
{
.
.
.
#endif
}
Oui, il ferme 2 ouvertures avec une seule fermeture.
#define unless(cond) if(!cond)
#define until(cond) while(!cond)
Utilisé:
unless( ptr == NULL)
ptr->foo();
Ceci est tiré d'un programme open source populaire. En fait, cela rend certaines parties du code plus lisibles en masquant le triste héritage.
#define EP_STATUS CASTLING][(BOARD_FILES-2)
#define HOLDINGS_SET CASTLING][(BOARD_FILES-1)
Je suppose qu'il n'y a rien de grave ici, je trouve ça drôle.
Quand j'ai découvert les macros en C, ils m'ont laissé perplexe pendant des jours. Ci-dessous est ce que j'ai été confronté. J'imagine que cela a du sens pour les experts C et est super efficace, mais pour moi, essayer de comprendre exactement ce qui se passait signifiait couper et coller toutes les différentes macros ensemble jusqu'à ce que toute la fonction puisse être visualisée. Ce n'est sûrement pas une bonne pratique?! Quel est le problème avec l'utilisation d'une vieille fonction simple?!
#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)
Tout ce qui utilise sendmail et sa syntaxe de configuration magique
#define PROCESS_AND_RETURN(X) \
X.process(); \
// Important: Return only after invoking virtual method process() \
return X
En raison du commentaire "Important", la macro ne renvoie jamais l'objet et CRASH!
Voir cette réponse Comment un collègue dyslexique s’est simplifié la vie avec un fichier d’en-tête commun rempli de choses comme #define fasle false
.
Une fois, j'ai assemblé cet horrible code C++ qui utilisait des macros pour aider les fonctions de raccordement dans la table d'importation des DLL.
#define ARGLIST(...) __VA_ARGS__
#define CPPTYPELESSARG(typelessParams) thisptr, typelessParams
#define CPPTYPEDARG(typedParams) void* thisptr, typedParams
#define CPPTYPELESSNOARG thisptr
#define CPPTYPEDNOARG void* thisptr
#define CPPHOOKBODY(hookName, params) void *thisptr; \
__asm { mov thisptr, ecx } \
return On##hookName ( params );
#define CHOOKBODY(hookName, typelessParams) return On##hookName( typelessParams );
#define CPPHOOK(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, CPPTYPEDARG(typedParams), typelessParams, \
typedParams, __thiscall, __stdcall, CPPHOOKBODY(hookName, CPPTYPELESSARG(typelessParams)))
#define CPPHOOKNOARG(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, CPPTYPEDNOARG, typelessParams, \
typedParams, __thiscall, __stdcall, CPPHOOKBODY(hookName, CPPTYPELESSNOARG))
#define CDECLHOOK(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams, \
typedParams, __cdecl, __cdecl, CHOOKBODY(hookName, typelessParams))
#define CDECLFUNC(name, address, returnType, args) \
typedef returnType (__cdecl *name##Ptr)(args); \
name##Ptr name = (name##Ptr) address;
#define CPPFUNC(name, address, returnType, args) \
typedef returnType (__thiscall *name##Ptr)(void* thisptr, args); \
name##Ptr name = (name##Ptr) address;
#define STDFUNC(name, address, returnType, args) \
typedef returnType (__stdcall *name##Ptr)(args); \
name##Ptr name = (name##Ptr) address;
#define STDHOOK(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams) \
HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams, \
typedParams, __stdcall, __stdcall, CHOOKBODY(hookName, ARGLIST(typelessParams)))
#define HOOKIMPL(InjectHookRef, importLib, importFunc, hookName, returnType, typedParams, typelessParams, hookParams, fnPtrCall, hookCall, hookBody) \
typedef returnType (fnPtrCall *##hookName##OrigPtr )( typedParams ); \
class hookName : public IHook \
{ \
public: \
typedef hookName##OrigPtr func_type; \
private: \
static void* m_origFunction; \
static bool m_bModifyImport; \
static std::string m_lib; \
static std::string m_importFunc; \
static std::string m_sHookName; \
static returnType hookCall hookName##FnHook ( hookParams ) \
{ \
hookBody \
} \
static bool ImplIsModifyImport() { return hookName::m_bModifyImport; } \
static void ImplSetModifyImport(bool bModify) { hookName::m_bModifyImport = bModify; } \
static const std::string& ImplGetLibName() { return hookName::m_lib; } \
static const std::string& ImplGetImportFunctionName() { return hookName::m_importFunc; } \
static void ImplSetOriginalAddress(void* fn) { hookName::m_origFunction = fn; } \
static void* ImplGetOriginalAddress() { return hookName::m_origFunction; } \
static returnType On##hookName ( typedParams ); \
static void* ImplGetNewAddress() { return hookName::##hookName##FnHook; } \
static const std::string& ImplGetHookName() { return hookName::m_sHookName; } \
public: \
hookName() \
{ \
InjectHookRef.AddHook((IHook*)this); \
hookName::m_lib = importLib; \
hookName::m_importFunc = importFunc; \
hookName::m_sHookName = #hookName; \
hookName::m_origFunction = NULL; \
hookName::m_bModifyImport = true; \
} \
virtual bool IsModifyImport() const { return hookName::ImplIsModifyImport(); } \
virtual void SetModifyImport(bool bModify) { hookName::ImplSetModifyImport(bModify); } \
virtual const std::string& GetHookName() const { return hookName::ImplGetHookName(); } \
virtual const std::string& GetLibName() const { return hookName::ImplGetLibName(); } \
virtual const std::string& GetImportFunctionName() const { return hookName::ImplGetImportFunctionName(); } \
virtual void* GetOriginalAddress() const { return hookName::ImplGetOriginalAddress(); } \
virtual void* GetNewAddress() const { return hookName::ImplGetNewAddress(); } \
virtual void SetOriginalAddress(void* fn) { hookName::m_origFunction = fn; } \
static func_type GetTypedOriginalAddress() { return reinterpret_cast(hookName::m_origFunction); } \
}; \
void* hookName::m_origFunction = NULL; \
bool hookName::m_bModifyImport = false; \
std::string hookName::m_lib; \
std::string hookName::m_importFunc; \
std::string hookName::m_sHookName; \
static hookName g##hookName##Inst;
Ce qui à son tour m'a permis de faire ceci:
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>
}
Un ancien employeur a découvert qu'il n'y avait aucune implémentation de BASIC-PLUS sur les systèmes Unix modernes. Ils ont donc essayé de le réimplémenter à l'aide de macros de préprocesseur C:
#define IF if(
#define THEN ) {
#define ENDIF }
#define GOTO goto L
...etc.
Horrible.
J'en ajoute un autre qui a commencé à m'agacer avec le temps:
#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
Et c'est s'ils réussissent bien; J'ai vu des versions avec toutes les permutations possibles de parenthèses présentes ou non. Je l'ai vu défini deux fois dans le même fichier d'en-tête.
Mon argumentation s’applique principalement à Windows (bien que je suppose que les autres SDK du système d’exploitation ont quelque chose de similaire), où à peu près tout le monde semble ressentir le besoin de définir cette macro dans l’en-tête de son projet et je ne comprends pas pourquoi.
WinNT.h (qui est inclus dans Windows.h) définit une version très agréable qui utilise un modèle voodoo pour provoquer des erreurs compilation si vous passez un type de pointeur au lieu d’un tableau.
Bien sûr, cela revient exactement à ce que j'ai écrit ci-dessus si vous construisez un programme C, mais je ne redéfinirais toujours pas quelque chose que le SDK a par défaut sans raison.
Bonnes macros: (bien que personnellement je n'aime pas les doubles parenthèses requises pour utiliser cette syntaxe; je préfère soit les macros vararg (C99 uniquement) ou quelque chose comme PRINTF_0, PRINTF_1, etc., en fonction du nombre d'arguments)
#ifdef DEBUG
#define PRINTF(x) printf x
#else
#define PRINTF(x)
#endif
Réduit la taille du code et le temps d'exécution (le premier de plus que le second) pour les versions non déboguées; empêche également les fuites de chaînes de texte de débogage susceptibles de poser un risque de sécurité minime
#define "CR_LF" '\r'
Cela m'a dérouté pendant un moment!
Relative au discours de Raymond, il y a l'horrible macro (à mon avis, bien sûr):
#define CALL_AND_CHECK(func, arg) \
int result = func(arg); \
if(0 != result) \
{ \
sys.exit(-1); \
} \
J'étais assez nouveau dans l'utilisation de macros et utilisais cette macro, mais je attend la fonction que je lui transmettais échoua. Et comme je le faisais dans un fil d’arrière-plan, il m’a bouleversé pendant des jours la raison pour laquelle toute mon application "se bloquait".
En passant, si seulement std :: tr1 :: function existait lors de l'écriture de cette macro, j'aurais une semaine de ma vie!
Ce n'est pas une macro C mais ...
Il y a de nombreuses années, j’ai eu la tâche amusante de transférer le Transport Tycoon d’origine du PC sur le Mac. La version PC étant entièrement écrite en assembleur, nous avons dû parcourir tout le code source et le transférer d'abord dans le code C "PC", puis dans Mac. La plupart du code était OK, même orienté objet par endroits. Cependant, le système de rendu mondial était incroyable. Pour quiconque n'a pas joué au jeu, le monde peut être visualisé à l'un des trois niveaux de zoom. Le code pour ceci était quelque chose dans les lignes de:
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>
Nous devions utiliser une version légèrement plus ancienne de MASM, car la macro plantait l'assembleur lorsque nous essayions de l'assembler.
Skizz
Les pires abus (et je suis coupable de faire cela de temps en temps) utilise le pré-processeur comme une sorte de fichier de remplacement, à savoir:
#define FOO_RELATION \
BAR_Tuple( A, B, C) \
BAR_Tuple( X, Y, Z) \
et puis ailleurs:
#define BAR_Tuple( p1, p2, p3) if( p1 ) p2 = p3;
FOO_RELATION
#undef BAR_Tuple
qui se traduira par:
if( A ) B = C;
if( X ) Y = Z;
Ce modèle peut être utilisé pour faire toutes sortes de choses (terribles) ... générer des instructions switch ou d'énormes blocs if else, ou une interface avec du code "réel". Vous pouvez même l'utiliser pour :: cough :: générer un menu contextuel dans un menu contextuel non-oo system :: cough ::. Pas que je fasse jamais quelque chose d'aussi boiteux.
Edit: parenthèses incohérentes corrigées et exemple développé
Essayez de déboguer un gros projet qui aime vraiment les macros, et il y a beaucoup de macros qui appellent d'autres macros qui appellent d'autres macros, etc. etc. (5 à 10 niveaux de macros n'étaient pas si rares)
Et ensuite complétez avec beaucoup de #ifdef ce macrot #else cette macro, donc si vous suivez le code, c'est comme un arbre de chemins différents, ça peut aller.
La seule solution est la plupart des cas était de précompiler et de lire cela à la place ....
Une fois, j’ai vu un paquet de macros aliasant chaque mot-clé C pour vous permettre de programmer efficacement en Klingon. C'est vrai, Klingon. Heureusement, le projet a été abandonné et abandonné il y a plusieurs années.
Une autre utilisation "créative" du pré-processeur, bien que ce soit plus dans la terminologie employée que dans la mécanique (qui sont incroyablement banales):
/***********************************************************************
* 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 */
Code authentique - je pensais l'avoir supprimé, mais apparemment pas. Je dois l'avoir fait dans une branche temporaire et ne pas avoir la permission de le vérifier dans le code principal. Un élément de plus pour la liste 'à faire'.
À l’époque, cela semblait être une bonne idée de "passer" une macro en tant qu’argument dans une autre macro. (Je ne pouvais tout simplement pas supporter l'idée de définir une liste de valeurs à plusieurs endroits.) Le code ici est artificiel (et pas très motivant), mais vous donne l'idée:
#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)
{
// ...
}
Le code NFS dans les noyaux BSD utilise goto entre les macros. Il est toujours utilisé et le code fonctionne réellement. Je connais plusieurs personnes qui ont essayé de tout nettoyer, mais toutes ont abandonné au bout d'un moment - c'est trop compliqué.
Vous pouvez le voir ici: http://www.openbsd.org/cgi-bin/cvsweb/src/sys/nfs/nfsm_subs.h?rev=1.4
Le code de pilote pour un ASIC que j'ai utilisé il y a environ 10 ans comportait de nombreuses sections ressemblant à ceci:
int foo(state_t *state) {
int a, b, rval;
$
if (state->thing == whatever) {
$
do_whatever(state);
}
// more code
$
return rval;
}
Après beaucoup de casse-tête, nous avons finalement trouvé la définition:
#if DEBUG
#define $ dolog("%s %d", __FILE__, __LINE__);
#else
#define $
#endif
C'était difficile à trouver, car aucun des fichiers source qui l'utilisait n'avait aucun fichiers inclus. Il y avait un fichier appelé top.c
fichier source ressemblant à:
#include <namechanged.h>
#include <foo.c>
#include <bar.c>
#include <baz.c>
Effectivement, c’était le seul fichier référencé dans le Makefile. Chaque fois que vous changiez quelque chose, vous deviez recompiler tout. C'était "pour rendre le code plus rapide".
Je conviens que pour la plupart, les macros sont horribles à utiliser, mais j'ai trouvé quelques cas où ils ont été utiles.
Celui-ci est en fait un excellent IMHO, car vous ne pouvez obtenir que quelque chose de similaire avec sprintf, qui nécessite alors des allocations de ressources, etc.
// 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 ") : "
L'autre chose que je n'aime pas utiliser, mais que je trouve extrêmement utile, est de procéder de la sorte, ce qui me permet fondamentalement de générer rapidement des modèles comportant un nombre variable de paramètres de modèle.
#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...
Bien que cela rend la lecture de "Callback.inl" assez horrible, cela élimine complètement la réécriture du même code avec un nombre différent d'arguments. Je devrais également mentionner que "Callback.inl" #undefs toutes les macros à la fin du fichier, par conséquent, les macros elles-mêmes n'interféreront avec aucun autre code, cela rend simplement "Callback.inl" un peu plus difficile à écrire (lire et déboguer n'est pas trop difficile cependant)
Trouvé dans les déclarations, à beaucoup de confusion:
NON_ZERO_BYTE Fixed(8) Constant('79'X),
Trouvé plus tard:
IF WORK_AREA(INDEX) = ZERO_BYTE THEN /* found zero byte */
WORK_AREA(INDEX) = NON_ZERO_BYTE ; /* reset to nonzero*/
#undef near
#undef far
Quand j'étais nouveau dans la programmation de jeux, j'écrivais un truc pour un cours de caméra. C'est un jeu que j'ai écrit. J'avais des erreurs très étranges dans mon code.
Il s’avère que Microsoft avait quelques définitions pour windows.h près et de loin, ce qui a provoqué une erreur de mes variables _near et _far sur les lignes qui les contenaient. Il était très difficile de cerner le problème car (j'étais novice à l'époque) et ils n'existaient que sur quatre lignes dans tout le projet, alors je ne m'en suis pas rendu compte tout de suite.
J'ai utilisé des fichiers d'en-tête en tant que grosses macros:
// compile-time-caller.h
#define param1 ...
#define param2 ...
#include "killer-header.h"
// killer-header.h
// uses param1 and param2
J'ai également créé récursif fichiers d'en-tête.
// 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
Cela semblait être une bonne idée parfois, mais si vous en aviez besoin, vous devriez probablement simplement remplacer le fil de toute façon. Protégé est assez pervers, permettre un accès interne aux descendants n’est pas mieux que de rendre les objets publics ...
Toute macro utilisant l'opérateur de concaténation de jetons ##
.
J'en ai vu un avec lequel un de mes collègues a eu le plaisir de travailler. Ils ont essayé de faire une implémentation personnalisée de l'internat de chaîne, ils ont donc ré-implémenté des chaînes en utilisant un nombre énorme de macros qui (bien sûr) ne fonctionnaient pas correctement. J'essayais de comprendre ce qui avait provoqué l'explosion de mes yeux à cause de tous les ##
est dispersé.
Je n'aime pas trop le truc Boost Preprocessor. J'ai essayé une fois de comprendre comment l'utiliser (nous avions quand même Boost dans le projet ...), mais autant que je sache, l'utiliser rendrait mes messages d'erreur SO illisible ça n'en valait pas la peine.
J'ai aimé l'idée de l'équivalent des macros en boucle, mais c'était trop.
Soyez doux, j'ai écrit ceci comme le seul moyen auquel je puisse penser pour capturer de manière générique les exceptions.
Je l'utilise pour capturer et empêcher les exceptions de se propager hors de mes fonctions d'interface publique ...
/// 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);} \
}
Je n'aime pas la complexité et je déteste les macros, mais comment pourriez-vous "faire" un gestionnaire de captures générique? Ce n'est pas censé être la fin-tout, c'est juste mon gestionnaire de capture générique pour isoler les fonctions publiques héritées et ajouter rapidement au moins un certain nivea de protection lorsque je sais que la fonction est invoquée à travers un limite qui pourrait apparaître si une exception C++ devait être levée (bienvenue, JNI).
Alors, est-ce que ça te fait courir et te cacher, ou est-ce la seule façon de faire quelque chose comme ça?
Fondamentalement...
try{
// some block of code capable of throwing
}
CatchAll()
Je l'ai trouvé dans 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);
Le problème est que Visual Studio 2005 et peut-être d’autres fonctions ide go to definition
Et go to declaration
Ne trouvent que la déclaration #define TY_(...)
et non la déclaration DocParseStream
souhaitée.
Peut-être que c'est plus sûr de cette façon.
Je pense qu'ils devraient mettre un préfixe pour chaque fonction et ne pas appeler une macro pour faire le travail .. c'est encombrer le code .. mais peut-être que je me trompe à ce sujet. Qu'est-ce que tu penses..?
Ps: Il semble que presque toutes les fonctions internes const et autres sont préfixées par ceci .. Mon collègue vient de me dire que c’est habituel .. wtf? Peut-être que j'ai raté quelque chose.