Click here to Skip to main content
15,879,490 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Can anyone explain what is happening in this macro as I am new to c and macros

#define FIELD_OFFSET(type, field) ((LONG)(LONG_PTR)&(((type *)0)->field))
Posted

I would explain some more general points to take into account dealing with macros.
Macros in C are just text replacements, in the simplest form a macro is a text to be replaced by another text:
C++
#define OldText NewText

A more advanced feature of macros is the possibility of passing parameters. In this case the text to search and replace is macro name, while the text in the parameters is replaced in the positions where the parameters names appears in the macro body:
C++
#define macro1(par1, par2) par1 = par2;

In the last example if we write:
C++
macro1(a, 4)

After file preprocessing we will get:
C++
a = 4;

The number of parameters can even be variable, this declared using the elipsis and the special symbol __VA_ARGS__ that translates to the sequence of variable parameters:
C++
#define PRINTMACRO(format, ...)  printf(format, __VA_ARGS__);

In this case the line:
C++
PRINTMACRO("string %s and int %d\n", string, a);

Afte preprocessor will lead to:
C++
printf("string %s and int %d\n", string, a);


Now after this clarification, going back to your question the macro that made you perplex is not complicate as a 'macro', but is how it is write that made you perplex.
If this is the case let have a closer look to it. First of all get rid of teh macro capsule and let analyze the content:
C++
((LONG)(LONG_PTR)&(((type *)0)->field))

Exam from the inside: ((type *)0) here we cast the value 0 (zero) as the address of a type variable (a pointer to variable). Up to now it can be any kind of variable, but will understand that is not so.
Infact extending: (((type *)0)->field) we understand that we are getting the member 'field' from a pointer to a struct of type 'type', which address is 0.
Further on: &(((type *)0)->field) we are getting the address of the member 'field' of a structure of type 'type' at address 0.
Just a consideration is required now: if a structure is considered at address 0, the address of each field is coincident with the field offset, in bytes, from the start of the structure. But an offset is a number, and here we got an address. This can create warnings on types compatibility during compilation. We need to cast the result to a number.
This is what we do now, we cast our address to a LONG, but before we cast the result to a LONG_PTR.
This is an MS macro, and it is crafted to work for 32 and 64 bits assemblies. That's why we cast it first to LONG_PTR: (LONG_PTR)&(((type *)0)->field). LONG_PTR is a type that guarantee to accept longs and pointers for any processor bitness so avoid compiler warnings.
Then the last step is to cast value to what an offset must be: a number.
C++
((LONG)(LONG_PTR)&(((type *)0)->field))

The morale is don't worry about the macro it is C rules you have to follow to un derstand the code ;).
It is a good habit to always enclose each parameter and then the whole macro in braces to avoid any side effect from the expression (don't do this and soon or later you'll find why you have to :D).

Last, why all that braces?
Consider:
C++
struct _tagstr
{
    int a;
    float b;
};

void *pStr;

...

//We are pretending that at the location pointed by pStr there is a a structure
//like _tagstr to which we assign the float value 0.1
// (Yes an obscure feature of C :-) )
*((float *)((char *)pStr + FIELD_OFFSET(struct _tagstr, b))) = 0.1;

It's alway better to strictly delimit parameters of macros.
I hope this will give some more lights on the issue.

P.S. consider my last example as an exercise ;)
 
Share this answer
 
v8
Comments
Richard MacCutchan 1-May-15 13:01pm    
+5, very comprehensive and clear explanation.
Frankie-C 1-May-15 13:12pm    
Thanks Richard
It gives the offset, in bytes, of item field in the structure type. So if you have s tructure as follows:
C++
struct foo
{
    int fooInt;     // 4 bytes wide, starting at offset 0
    char fooChar;   // 1 byte wide, starting at offset 4
};
FIELD_OFFSET(foo, fooChar) = 4;

There are very few cases where you would need to use it, allthough I have done myself, in the dim and distant past.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900