General Purpose C/C++ Macros
by Stanislav Sýkora, Extra Byte, Via R.Sanzio 22C, Castano Primo, Italy 20022
in Stan's Library, Ed.S.Sykora, Vol.I. First release April 15, 2004
Permalink via DOI:  10.3247/SL1Soft06.001
NEXT | Code snippets | Software Books and Links Programming Section of Stan' LIBRARY | Stan's HUB

Note: This is a part of a collection of macros, many of which I had been using for years in various Applications. You can copy any of them into your program header(s) using Copy and Paste. The macros had been tested on 32-bit Intel-type platforms but, unless specified otherwise, they should work on other platforms as well. In any case, however, I hereby disclaim any responsibility for whatever might happen when you use them.

For a review of macro-syntax rules and related tips and tricks, see Writing Macros.

Some of the macros assume that the types DWORD, WORD, BYTE and CHAR had been already defined.
See the Typedefs section at the end of this document for more details.

CATEGORY MACRO
EXTRACTIONS mDword, mWord, mByte, mChar, mNibble, mBit
MULTIPLE CHOICE mChoiceTF, mChoiceN0P, mChoiceLT0, mChoiceGT0
SHIFTS mShift2, mShift3, mShift4, mShift5, mShift6, mShift7
PERMUTATIONS mSwap, mRotate
mRotate2, mRotate3, mRotate4, mRotate5, mRotate6

Data Extraction

Code:

#define mDword(i,lptr) (((DWORD*)(lptr))[i])
#define mWord(i,lptr) (((WORD*)(lptr))[i])
#define mByte(i,lptr) (((BYTE*)(lptr))[i])
#define mChar(i,lptr) (((CHAR*)(lptr))[i])
#define mNibble(i,lptr) ((mByte((i)>>1,(lptr))>>(((i)&1)<<2))&0xf)
#define mBit(i,lptr) (mByte((i)>>3,(lptr))&(1<<(i)%8)?1:0)

Arguments:

lptr is a pointer to a data structure S of any type and complexity.
i is an integer-type index of the item to be extracted.

Index numbering starts at 0, corresponding to the first item in the S structure.
Possible overrun of the structure bounds is NOT tested.

All the data-extraction macros return a value (the extracted item). Most 32-bit platform compilers cast the result into DWORD. This happens because the intermediate operations are carried out in 32 bits. When the result is narrower (such as a BYTE or WORD) or signed (such as LONG or char) and the compiler signals possible loss of precision or type mismatch, you may need to cast it to proper type; e.g., (BYTE)mByte(17,lpMyStruct).

When S is of a numeric type (or contains such types), the result may depend upon its coding. In particular, it may depend upon whether little-endian or big-endian convention is used on the given platform.

To extract an item from a structure S which is known by lvalue, use its address &S.
Thus, for example, mWord(3,&S) extracts the fourth word of S.

With the exception of mNibble and mBit, the macros return an lvalue object of which it is possible to take the address.
For example, &mWord(3,&S) returns the pointer to the 3-rd WORD of the structure S.


mDword(i,lptr)
extracts and returns the i-th DWORD of the structure pointed to by lptr.
mWord(i,lptr)
extracts and returns the i-th WORD of the structure pointed to by lptr.
mByte(i,lptr)
extracts and returns the i-th BYTE of the structure pointed to by lptr.
mChar(i,lptr)
extracts and returns the i-th CHAR of the structure pointed to by lptr.
mNibble(i,lptr)
extracts the i-th nibble of the structure pointed to by lptr.
Nibbles are groups of 4 bits (a byte is composed of two nibbles). Assuming a little-endian platform,
the first nibble (i=0) is the least-significant one of the first byte of the structure.
mBit(i,lptr)
extracts the i-th bit of the structure pointed to by lptr.
Index i=0 specifies the least-significant bit of the first byte of the structure.
The return value of mBit is either 0 or 1, depending upon the specified bit's value.

Multiple choice

Code:

#define mChoiceTF(ex,caseT,caseF) ((ex)?(caseT):(caseF))
#define mChoiceN0P(ex,caseN,case0,caseP) ((ex)?((ex>0)?(caseP):(caseN)):(case0))
#define mChoiceLT0(ex,caseLT,caseGE) ((ex<0)?(caseLT):(caseGE))
#define mChoiceGT0(ex,caseGT,caseLE) ((ex>0)?(caseGT):(caseLE))

Arguments:

ex is any expression that can be interpreted as (or converted to a boolean type) and tested for true or false.
case... are return values which must be all of the same - but otherwise arbitrary - type.


mChoiceTF(ex,caseT,caseF)
returns caseT when ex is true (nonzero) and caseF if it is false (0).
mChoiceN0P(ex,caseN,case0,caseP)
returns caseN, case0 or caseF, depending upon whether ex is negative, zero, or positive, respectively.
mChoiceLT0(ex,caseLT,caseGE)
returns caseLT when ex < 0 and caseGE otherwise.
mChoiceGT0(ex,caseGT,caseLE)
returns caseGT when ex > 0 and caseLE otherwise.

Shifts

Code:

#define mShift2(A,lvB) {(lvB)=(A);}
#define mShift3(A,lvB,lvC) {(lvC)=(lvB);mShift2(A,lvB);}
#define mShift4(A,lvB,lvC,lvD) {(lvD)=(lvC);mShift3(A,lvB,lvC);}
#define mShift5(A,lvB,lvC,lvD,lvE) {(lvE)=(lvD);mShift4(A,lvB,lvC,lvD);}
#define mShift6(A,lvB,lvC,lvD,lvE,lvF) {(lvF)=(lvE);mShift5(A,lvB,lvC,lvD,lvE);}
#define mShift7(A,lvB,lvC,lvD,lvE,lvF,lvG) {(lvG)=(lvF);mShift5(A,lvB,lvC,lvD,lvE,lvF);}

The arguments A and lvB - lvG specify the objects whose contents are to be shifted. Ideally, all the arguments should be of the same type. When the type is User-defined, a proper copy operator = must be provided (either explicitly or implicitely). When the types are not all the same, the copy operator must also provide for automatic conversion between all the used types.

As indicated by the argument names, all actual arguments except A must be assignable, lvalue entities. The argument A does not need to be lvalue since it is left unchanged.

All the shift macros return the value of the A argument. However, using these macros within expressions is a bad programming practice since expressions with side effects are deprecated.


mShift2(A,lvB)
mShift3(A,lvB,lvC)
mShift4(A,lvB,lvC,lvD)
mShift5(A,lvB,lvC,lvD,lvE)
mShift6(A,lvB,lvC,lvD,lvE,lvF)
mShift7(A,lvB,lvC,lvD,lvE,lvF,lvG)
shift the contents of all the arguments (except A) by one position to the right.
For example, mShift3(a,b,c) is equivalent to c=b;b=a;.

Permutations

Code:

#define mSwap(lvA,lvB,lvAux) {(lvAux)=(lvB);(lvB)=(lvA);(lvA)=(lvAux);}
#define mRotate(lvA,lvB,lvC,lvAux) {(lvAux)=(lvC);(lvC)=(lvB);(lvB)=(lvA);(lvA)=(lvAux);}
#define mRotate2(lvA,lvB,lvAux) mSwap(lvA,lvB,lvAux)
#define mRotate3(lvA,lvB,lvC,lvAux) mRotate(lvA,lvB,lvC,lvAux)
#define mRotate4(lvA,lvB,lvC,lvD,lvAux) {mShift5(lvA,lvB,lvC,lvD,lvAux);(lvA)=(lvAux);}
#define mRotate5(lvA,lvB,lvC,lvD,lvE,lvAux) \
    {mShift6(lvA,lvB,lvC,lvD,lvE,lvAux);(lvA)=(lvAux);}
#define mRotate6(lvA,lvB,lvC,lvD,lvE,lvF,lvAux) \
    {mShift7(lvA,lvB,lvC,lvD,lvE,lvF,lvAux);(lvA)=(lvAux);}

Arguments:

lvA through lvF are the objects whose contents are to be permuted.
lvAux is an auxiliary object of the same type.

Ideally, all the arguments should be of the same type. When the type is User-defined, a proper copy operator = must be provided (either explicitly or implicitely). When the types are not all the same, the copy operator must also provide for automatic conversion between all the used types.

As indicated by the argument names, all actual arguments of the permutation macros must be assignable, lvalue entities.

All the permutation macros return the new value of the lvA argument. However, using these macros within expressions is a bad programming practice (expressions with side effects). The new value of lvA is set also into lvAux.

Notice that rotations in practice exhaust the set of macros required to permute small sets of objects. Any permutation, in fact, can be written as product of mutually independent (commutable) cyclic rotations. For example, if one wants to permute the contents of 5 objects [a0,a1,a2,a3,a4] to reflect the order [a1,a0,a4,a2,a3], it is sufficient to exchange [a0,a1] and rotate [a2,a3,a4], using the code

mSwap(a0,a1,aux);
mRotate3(a2,a3,a4,aux);

Keep in mind that the macros do not permute the objects but rather their contents.
This subtle distinction becomes important when the types of the arguments are not all the same.


mSwap(lvA,lvB,lvAux)
exchanges (swaps) lvA and lvB.
It is equivalent to mRotate2(lvA,lvB,lvAux).
mRotate(lvA,lvB,lvC,lvAux)
rotates the three values lvA, lvB, lvC to the right (lvC is replaced by lvB, lvB is replaced by lvA, and lvA is replaced by the original value of lvC). It is equivalent to mRotate3(lvA,lvB,lvC,lvAux).
mRotate2(lvA,lvB,lvAux),
mRotate3(lvA,lvB,lvC,lvAux),
mRotate4(lvA,lvB,lvC,lvD,lvAux),
mRotate5(lvA,lvB,lvC,lvD,lvE,lvAux)
mRotate6(lvA,lvB,lvC,lvD,lvE,lvF,lvAux)
rotate the contents of all the arguments (except lvAux) by one position to the right.

Note: Should you wish to rotate a larger number of items, it is likely that they would be members of an array and the task could be done better by means of a standard function. For the same reason the macros mRotate4, mRotate5 and mRotate6, though certainly very efficient when content-shuffling is really desired, need not be always the best solution.

TYPEDEFS

Some of the macros listed in this document assume that the types listed below had been already defined.

typedef unsigned char  BYTE;
typedef unsigned short WORD;
typedef unsigned long  DWORD;
typedef signed char    CHAR;
typedef signed char*   LPSTR;

You can simply copy these typedefs into your header(s).
They are also defined in windows.h and a number of other standard headers.
Likewise, you may need to define a NULL pointer, which is simply

#define NULL 0

though some compiler may require the more explicit form

#define NULL ((void*)0uL)

TOP | NEXT | Code snippets | Software Books and Links Programming Section of Stan' LIBRARY | Stan's HUB
Copyright ©2004,2006 Stanislav Sýkora    DOI: 10.3247/SL1Soft06.001 Designed by Stan Sýkora