What is the worst real-world macros/pre-processor abuse you've ever come across?

Viewed 126755

What is the worst real-world macros/pre-processor abuse you've ever come across (please no contrived IOCCC answers *haha*)?

Please add a short snippet or story if it is really entertaining. The goal is to teach something instead of always telling people "never use macros".


p.s.: I've used macros before... but usually I get rid of them eventually when I have a "real" solution (even if the real solution is inlined so it becomes similar to a macro).


Bonus: Give an example where the macro was really was better than a not-macro solution.

Related question: When are C++ macros beneficial?

70 Answers

From memory, it looked something like this:

#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)

Yes that's right, no closing braces in any of the functions. Syntax highlighting was a mess, so he used vi to edit (not vim, it has syntax coloring!)

He was a Russian programmer who had mostly worked in assembly language. He was fanatical about saving as many bytes as possible because he had previously worked on systems with very limited memory. "It was for satellite. Only very few byte, so we use each byte over for many things." (bit fiddling, reusing machine instruction bytes for their numeric values) When I tried to find out what kinds of satellites, I was only able to get "Orbiting satellite. For making to orbit."

He had two other quirks: A convex mirror mounted above his monitor "For knowing who is watching", and an occasional sudden exit from his chair to do a quick ten pushups. He explained this last one as "Compiler found error in code. This is punishment".

My worst:

#define InterlockedIncrement(x) (x)++
#define InterlockedDecrement(x) (x)--

I spent two days of my life tracking down some multi-threaded COM ref-counting issue because some idiot put this in a header file. I won't mention the company I worked for at the time.

The moral of this story? If you don't understand something, read the documentation and learn about it. Don't just make it go away.

#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!");
}

Challenge: Can anyone do it with fewer defines and structs? ;-)

#define if while

It was joke played on someone, it wasn't found amusing by those affected

The hideous:

#define begin {
#define end }
/* and so on */

Seriously, if you want to code in Pascal, buy a Pascal compiler, don't destroy the beautiful C language.

An 'architect', very humble guy, you know the type, had the following:

#define retrun return

because he liked to type fast. The brain-surgeon used to like to shout at people who were smarter than him (which was pretty much everyone), and threaten to use his black-belt on them.

Real-world? MSVC has macros in minmax.h, called max and min, which cause a compiler error every time I intend to use the standard std::numeric_limits<T>::max() function.

A mix between Pascal syntax and french keywords:

#define debut {
#define fin }
#define si if(
#define alors ){
#define sinon }else{
#define finsi }

Raymond Chen has a really good rant against using flow control macros. His best example is straight from the original Bourne shell source code:

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
}

I would like to submit for the contest a gem called chaos-pp, which implements a functional language by means of the preprocessor macros.

One of the examples is calculating the 500th fibonacci number entirely by the preprocessor:

The original code before preprocessor looks as this:

int main(void) {
   printf
     ("The 500th Fibonacci number is "
      ORDER_PP(8stringize(8to_lit(8fib(8nat(5,0,0)))))
      ".\n");
   return 0;
}

preprocessing the file we get the following result (after a rather long wait):

$ 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;
}

Directly from Qt:

#define slots   /* */
#define signals /* */

Really nice to interact with other libs as boost::signals... Just an example, there are many others in Qt that create funny looking code like:

class X : public QObject {
   Q_OBJECT
private slots:
   //...
public signals:
   //...
};

And that is C++... but suddenly:

boost::signals::trackable

Is not valid C++ any more.

Windows.h has a lot of functions that abused macros.


MrValdez is annoyed by the GetObject macro found in Windows.h

The GetObject macro changes the GetObject() function into GetObjectA() or GetObjectW() (depending if the build is compiled in non-unicode and unicode, respectively)

MrValdez hates having to do before the GetObject function line

#undef GetObject

Object *GetObject()

The alternative is to change the function name to something else like GetGameObject()


jdkoftinoff in the comments have nailed it: The problem is that all windows API functions are macros.

Adam Rosenfield mentioned that that the issues can be fixed by defining NOGDI, WIN32_LEAN_AND_MEAN, NOMINMAX, etc before including windows.h to remove the issues.

A coworker and I found these two gems in some of our code for object streaming. These macros were instantiated in EVERY SINGLE class file that did streaming. Not only is this hideous code spewed all over our code base, when we approached the original author about it, he wrote a 7 page article on our internal wiki defending this as the only possible way to accomplish what he was attempting to do here.

Needless to say, it has since been refactored out and is no longer used in our code base.

Don't be thrown off by the highlighted keywords. This is ALL a 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> )

Update (December 17, 2009):

More good news regarding this hideous macro author. As of August, the employee responsible for this monstrosity was sacked.

The worst I've ever encountered was in a product containing a suite of executables where the designated technical leader hadn't figured out libraries.

Instead, he had sets of files that were shared in several Visual Source Safe folders. He then realised they needed to behave slightly differently for each application.

There's a number of refactoring steps you could apply here.

Instead, he used #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
   }

The use of the LINE preprocessor to generate unique ID for messages passed over the network:

NetworkMessages.h

#define MSG_LOGIN  __LINE__
#define MSG_LOGOUT __LINE__
#define MSG_CHAT   __LINE__

This is an example where the macro really was better than a non-macro solution:

In a non-macro solution classes, functions and variables have to be built to keep track of what ID the message is. The developer may or may not make the message ID tracking complicated whereas this is easier to read and debug.

In addition, its easier to add new messages just by adding the message into the source.

The disadvantage of this situation is that the file has to be included in all code that uses messages. Compile time would increase whenever a message is edited.

One fairly bad example:

#ifdef __cplusplus
#define class _vclass
#endif

This allows a C structure that contains a member variable called class to be handled by a C++ compiler. There are two headers with this construct in it; one of them also contains '#undef class' at the end and the other doesn't.

In one year of the International Obfuscated C Coding Contest, there was an entry where the entire program was:

P

With the proviso that you could define P in the makefile to be whatever program you wanted.

As I recall, it won in one of the categories, and the next year a rule had popped up disallowing that style of entry.

(Edit: six months later or something... I'm sure the "No IOCCC" thing wasn't in the main question when I wrote this...)

I was bored one day and was playing around with blocks in 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)

allowing "interesting" things like:

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)))));

(some function and class definitions not shown for sake of brevity)

The worst one I saw was the non-use :-)

Someone wrote a strcpy (I think that was it... over 10 years ago now) function inside of a method (because they didn't want the overhead of calling strcpy... sigh).

They clued in that it wouldn't work for Japanese characters so they added an "if" at the start to do ASCII or Unicode. At that point the code was about a screen long... likely killing cache coherency and erasing his supposed savings for the inlining of the code.

The code was identical save for the types (so should have used a macro).

Of course the strcpy that they wrote was much much much slower than the hand tuned assembler one that was in the standard library...

Of course if they had just done it all as a macro it could have been replaced with a call to strcpy...

Of course I quit the company (not directly because of that...)

The obligatory

#define FOR  for

and

#define ONE  1
#define TWO  2
...

Who knew?

I maintain code that has gotos in macros. So a function will have a label at the end but no visible goto in the function code. To make matters worse the macro is at the end of other statements usually off the screen unless you scroll horizontally.

#define CHECK_ERROR if (!SomeCondition) goto Cleanup

void SomeFunction() 
{ 
    SomeLongFunctionName(ParamOne, ParamTwo, ParamThree, ParamFour); CHECK_ERROR  
    //SomeOtherCode  
    Cleanup:    
   //Cleanup code  
}
#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

You really have to download it to appreciate it. The entire work flow is determined by macros. It is completely unreadable. As an example -

 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., etc.

And that is just setting up the options. the entire program is like that.

AI Game Programming Wisdom has a chapter where macros were used to create a scripting language for finite state machines.

Since the book and code are copyrighted material, here's a Google book link to the page detailing the macros (The resulting script language can be found on page 324.)

At Lucent, I once took a look at the source code of Steve Bourne's original Unix shell, and found he'd used the C pre-processor to make C look like Pascal or Algol. The part dealing with if statements looked like this:

#define IF   if (
#define THEN ) {
#define ELSE } else {
#define ELIF } else if (
#define FI   ; }

A friend of mine told me he'd done some maintenance on it in the mid-1990s, and it was still the same. (There's a lesson here for us in the inherent conservatism of a code base.)

Of course Steve did this as an experiment in the early days, and I'm sure would have had second thoughts if he'd written it later.

Update: According to Wikipedia's Bourne Shell article, the macros gave it an Algol 68 flavor. And, the full set of macros is here! They apparently influenced the founders of the International Obfuscated C Code Contest.

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

(names changed to protect the not so innocent)

Notice that we haven't even gotten to any code yet, this is just to get to the first actual bit of code. This actually happens (in almost, but not exactly the same way) for several functions, each of which, in the end only have 4 possible variations (which are also mostly copy/paste with slight variations and #ifdefs of their own).

#define interface struct

in some of Optima++ headers (Optima++ is/was a Watcom/Powersoft IDE I had to work with).

When I first came across macros in C they had me stumped for days. Below is what I was faced with. I imagine it makes perfect sense to C experts and is super efficient however for me to try and work out what exactly was going on meant cutting and pasting all the different macros together until the whole function could be viewed. Surely that's not good practice?! What's wrong with using a plain old function?!

#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) 

Good macros: (although personally I dislike the double parentheses required to use this syntax; I prefer either vararg macros (C99 only) or something like PRINTF_0, PRINTF_1, etc, depending on the number of arguments)

#ifdef DEBUG
#define PRINTF(x) printf x
#else
#define PRINTF(x)
#endif

Reduces code size / execution time (the first more than the second) for non-debug build; also prevents leaking debug text strings which may pose a smallish security risk

Related to Raymond's rant is the following horrible (in my opinion, of course) macro:

#define CALL_AND_CHECK(func, arg) \
    int result = func(arg);       \
    if(0 != result)               \
    {                             \
        sys.exit(-1);             \
    }                             \

I was pretty new to the practice of using macros and used this macro, but I expected the function that I passed to it to fail. And I was doing it in a background thread, so it stumped me for days why my entire app was "crashing".

As an aside, if only std::tr1::function was around when this macro was written, I would have a week of my life back!

I once put together this horrifying C++ code which used macros to help hook functions into the import table of DLLs.


#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;

Which in turn allowed me to do this:

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);

}

Anything using sendmail and its magic configuration syntax

Another piece of 'creative' use of the preprocessor, though it is more in the terminology employed than in the mechanics (which are incredibly mundane):

/***********************************************************************
 * 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 */

Genuine code - I thought I'd removed it, but apparently not. I must have done so out in some temporary branch and not gotten permission to check it back into the main code. One more item for the 'to do' list.

Found in declarations, to much confusion:

NON_ZERO_BYTE         Fixed(8)  Constant('79'X),

Found later:

IF WORK_AREA(INDEX) = ZERO_BYTE THEN  /* found zero byte */ 
   WORK_AREA(INDEX) = NON_ZERO_BYTE ; /* reset to nonzero*/

The worst abuses (and I'm guilty of doing this occasionally) is using the preprocessor as some sort of data file replacement, ie:

#define FOO_RELATION \  
BAR_TUPLE( A, B, C) \  
BAR_TUPLE( X, Y, Z) \ 

and then somewhere else:

#define BAR_TUPLE( p1, p2, p3) if( p1 ) p2 = p3;
FOO_RELATION
#undef BAR_TUPLE

which will result in:

if( A ) B = C;
if( X ) Y = Z;

This pattern can be used to do all sorts of (terrible) stuff... generate switch statements or huge if else blocks, or interface with "real" code. You could even use it to ::cough:: generate a context menu in a non-oo context menu system ::cough::. Not that I'd ever do anything so lame.

Edit: fixed mismatched parenthesis and expanded example

Try to debug big project that really loves macros, and there is a lot of macros that calls other macros that calls other macros etc etc. (5-10 levels of macros was not that uncommon)

And then top it up with a lot of #ifdef this macrot #else that macro, so if you follow the code it like a tree of different paths it can go.

The only solution is most cases was to precompile and read that instead....

I agree that for the most part, macros are horrible to use, but i have found a few instances where they have been useful.

This one is actually brilliant IMHO, as you can only get something similar with sprintf, which then requires resource allocations and whatnot, plus, all work is done entirely by the preprocessor

// 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 ") : "

The other that I loathe to use, but find it extremely useful is doing something like this, which basically allows me to quickly generate templates that have a variable number of template parameters

#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...

Although this makes it kind of horrible to read "Callback.inl", it does completely eliminate rewriting the same code with a different number of arguments. I should also mention that "Callback.inl" #undefs all of the macros at the end of the file, hence, the macros themselves won't interfere with any other code, it just makes "Callback.inl" a little harder to write (reading and debuging isn't too hard though)

I once saw a macro package that would alias every C keyword to let you effectively program in Klingon. That's right, Klingon. (Un)fortunately, the project was abandoned and taken down several years ago.

#define protected private

Seemed like a good idea sometimes, but if you need to, you should probably just string replace anyway. Protected is fairly evil, allowing internal access to descendants isn't much better than just making the items public...

Any macro that uses the token concatenation operator ##.

I saw one that a colleague of mine had the pleasure of working with. They tried to make a custom-implementation of string interning, so they re-implemented strings using a massive number of macros that (of course) didn't work properly. Trying to figure out what it did made my eyes explode because of all the ##'s scattered about.

I have used header files as big macros:

// compile-time-caller.h
#define param1 ...
#define param2 ...
#include "killer-header.h"

// killer-header.h
// uses param1 and param2

I have also created recursive header files.

// 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

Anything that uses the Token concatenation operator ##. I've seen this used to hack together psudo-template systems in C++ and other horrible things. The worst thing about using it is how impossibly cryptic your error messages become.

I have seen one good use for it however. There was a macro #MONITOR_COMPONENT(classname) that generated classes at compile time that would inherit from a predefined monitor class and classname and that would autoregister with a singleton class that was used to monitor each component.

Did it work? Yes, was it the nicest way of doing it.. probably not.

Related