The odd sex Technique of C -- the wonderful use of macros

Source: Internet
Author: User


I. macro list


When this problem occurs:

There is a tag variable, where each bit represents the corresponding meaning. We need to provide a set of functions to access and set these bits, but the operation functions for each tag bit are similar. If there are 32 bits, do we need to implement 32 similar operation functions?

You may say that you can use a set of operation functions to determine which bit to operate based on input parameters. This is feasible,

① Not intuitive enough. For example, to access the Movable flag, is Movable () is a natural method for users, and we can only provide this interface isFlag (Movable)

② Poor scalability. If you want to add a delete/modify flag, you need to change the code of functions such as isFlag.

We want to have this design:

Add or delete a tag macro in the macro definition of the header file. The operation function name designed for each tag is automatically changed, and an operation function is automatically added for the added tag, the deleted tag space is automatically subtracted from a set of operation functions.

This design is so cool!


But how to implement it?

First, the macro name of each tag bit is changed, and our operation function name needs to be changed accordingly. At this time, we can think of using a macro with a parameter and using the # character of the macro, combine two strings. (So that they can be replaced by macros)

# Define FLAG_ACCESSOR (flag )\

Bool is # flag () const {\

Return hasFlags (1 <flag );\

}\

Void set # flag (){\

JS_ASSERT (! HasFlags (1 <flag ));\

SetFlags (1 <flag );\

}\

Void setNot # flag (){\

JS_ASSERT (hasFlags (1 <flag ));\

RemoveFlags (1 <flag );\

}

[This step is something that most people can think of.]

In this way, FLAG_ACCESSOR (Movable) can be used to obtain the three functions that operate the Movable flag bit: is Movable (), set Movable (), setNot Movable ()

But how many flag spaces do we need to write FLAG_ACCESSOR (flag?

How to use a formula to expand to multiple FLAG_ACCESSOR (flag),Extract commonalitiesBecause the FLAG_ACCESSOR (flag) and flag are different, the macro function names are the same. Therefore, the macro list is used:

# Define FLAG_LIST (_)\

_ (InWorklist )\

_ (EmittedAtUses )\

_ (LoopInvariant )\

_ (Commutative )\

_ (Movable )\

_ (Lowered )\

_ (Guard)

The following formula is used: FLAG_LIST (FLAG_ACCESSOR.

However, there is another problem. We have not defined InWorklist, EmittedAtUses, LoopInvariant, and so on. We need to use macros to define the names of these markup bits.

For example:

# Define InWorklist 1

# Define EmittedAtUses 2

......

In this way, if we add or modify the name of a tag in the future, we need to modify the macro list and the macro definition of the tag name.

The best design we want is to change everything and change everything..

 

[Yang] if a new tag bit is added, we only need to add one in # define FLAG_LIST. For example, _ (Visited) automatically adds # define Visited 8.

If it is difficult to automatically add a macro definition, we should consider whether there is an alternative solution. We can see that this macro definition is a defined number, and enumeration also has the same function.

In this way,We put these expanded bits in the enum enumeration to automatically assign 1, 2, 3 ...... Instead of defining them one by one using a macro.

 

Now the question becomes: how can we add an item in # defineFLAG_LIST (_), and the corresponding item will be automatically added in the enum enumeration?

We only need to put FLAG_LIST (_) into the enum enumeration, so that we can increase the value at the same time.

 

If the macro list is:

# Define FLAG_LIST (_)\

_ (InWorklist )\

_ (EmittedAtUses )\

_ (LoopInvariant)

Can be changed:

InWorklist

EmittedAtUses

LoopInvariant

That's all.

In this way, we add a _ (Visited) item in # defineFLAG_LIST ). Visited is automatically added to enum.

 

That is, how to expand _ (InWorklist) into InWorklist. This is simple: # define DEFINE_FLAG (flag) flag,

 

The specific implementation method is as follows:

 

# Define FLAG_LIST (_)\

_ (InWorklist )\

_ (EmittedAtUses )\

_ (LoopInvariant )\

_ (Commutative )\

_ (Movable )\

_ (Lowered )\

_ (Guard)

It defines a FLAG_LIST macro. This macro has a parameter called _, which is itself a macro and can call every parameter in the list. A practical example may be used to illustrate the problem more intuitively. Suppose we have defined a macro DEFINE_FLAG, for example:

 

# Define DEFINE_FLAG (flag) flag, // note that the flag has a comma

Enum Flag {

None = 0,

FLAG_LIST (DEFINE_FLAG)

Total

};

# Undef DEFINE_FLAG

The following code can be obtained by extending FLAG_LIST (DEFINE_FLAG:

 

Enum Flag {

None = 0,

DEFINE_FLAG (InWorklist)

DEFINE_FLAG (EmittedAtUses)

DEFINE_FLAG (LoopInvariant)

DEFINE_FLAG (Commutative)

DEFINE_FLAG (Movable)

DEFINE_FLAG (Lowered)

DEFINE_FLAG (Guard)

Total

};

Then, extend the DEFINE_FLAG macro for each parameter, so that we can get the enum as follows:

 

Enum Flag {

None = 0,

InWorklist,

EmittedAtUses,

LoopInvariant,

Commutative,

Movable,

Lowered,

Guard,

Total

};

Next, we may need to define some access functions to better use the flag list:

 

# Define FLAG_ACCESSOR (flag )\

Bool is # flag () const {\

Return hasFlags (1 <flag );\

}\

Void set # flag (){\

JS_ASSERT (! HasFlags (1 <flag ));\

SetFlags (1 <flag );\

}\

Void setNot # flag (){\

JS_ASSERT (hasFlags (1 <flag ));\

RemoveFlags (1 <flag );\

}

 

FLAG_LIST (FLAG_ACCESSOR)

# Undef FLAG_ACCESSOR

 

(In this way, we only need to change the Add/delete bits in the macro list .)

 

[CONCLUSION: yang]



 

It is very enlightening to demonstrate the process step by step. If you are still confused about its use, you can spend some time on gcc-E.

 

[The advantages of the macro list are: You can expand a sub-statement into multiple sub-statements, and it is easy to expand, as long as you add a list item .]

 

II,Specified Initialization


Many people know that static array Initialization is like this:

Int fibs [] = {1, 2, 3, 4, 5 };

The C99 standard actually supports a more intuitive and simple way to initialize various collection class data (such as struct, Consortium, and array ).

 

Array Initialization


We canSpecify the elements of the array for initialization.. This is very useful,Especially when we need to update a ing relationship synchronously based on a set of # define. Let's take a look at the definition of a group of error codes, such:

/* Entries may not correspond to actualnumbers. Some entries omitted .*/

# Define EINVAL 1

# Define ENOMEM 2

# Define EFAULT 3

/*...*/

# Define E2BIG 7

# Define EBUSY 8

/*...*/

# Define ECHILD 12

/*...*/

 

Now, suppose we want to provide an error description string for each error code.To ensure the latest definition of the array, no matter whether the header file is modified or supplemented, we can use the syntax specified by this array..

 

Char * err_strings [] = {

[0] = "Success ",

[EINVAL] = "Invalid argument ",

[ENOMEM] = "Not enough memory ",

[EFAULT] = "Bad address ",

/*...*/

[E2BIG] = "Argument list too long ",

[EBUSY] = "Device or resource busy ",

/*...*/

[ECHILD] = "No child processes"

/*...*/

};

In this way, sufficient space can be allocated statically, and the maximum index is valid. At the same time, the special index is initialized to the specified value, and the remaining index is initialized to 0.

 

 

Struct and Consortium


Use the field names of struct and consortium to initialize data.Is very useful. Suppose we define:

Struct point {

Int x;

Int y;

Int z;

};

Then we initialize the structpoint as follows:

Struct point p = {. x = 1,. z = 3}; // x is 1, y is 0, z is 3

 

When we do not want to initialize all fields to 0, this method can easily generate struct during compilation without calling an initialization function.

 

For a consortium, we can use the same method, but we only need to initialize one field.

 



Iii. Assertions during compilation


This is actually a very "creative" function implemented using the macro of C language. In some cases, especially during kernel programming, conditional checks can be performed during compilation, rather than during runtime, which is very useful. Unfortunately, the C99 standard does not support any assertions during compilation.

However, we can use preprocessing to generate code, which is compiled only when certain conditions are established (preferably a command that does not actually function ).There are a variety of different ways to do this, usually create an array or struct with negative size. The most common method is as follows:

# Define STATIC_ZERO_ASSERT (condition) (sizeof (struct {int :-! (Condition );}))

# Define STATIC_NULL_ASSERT (condition) (void *) STATIC_ZERO_ASSERT (condition ))

 

/* Force a compilation error if conditionis false */

# Define STATIC_ASSERT (condition) (void) STATIC_ZERO_ASSERT (condition ))

If (condition) is calculated as a non-zero value (that is, the true value in C), that is! (Condition) is zero, so the code can be compiled smoothly and generate a zero-size struct. If the (condition) result is 0 (true or false in C), a compilation error occurs when you try to generate a negative struct.

 

It is very simple to use. If any assumption condition can be statically checked, it can be asserted during compilation. For example, in the flag list mentioned above, the type of the Flag Set is uint32_t. Therefore, we can make the following assertions:

STATIC_ASSERT (Total <= 32)

It is extended:

(Void) sizeof (struct {int :-! (Total <= 32 )})

Now, assume Total <= 32. So -! (Total <= 32) equals 0, so this line of code is equivalent:

(Void) sizeof (struct {int: 0 })

This is a valid C code. Now we assume there are more than 32 logos, So -! (Total <= 32) is equal to-1, so the code is equivalent:

(Void) sizeof (struct {int:-1 })

Because the bit width is negative, it can be determined that if the number of flags exceeds the space we have assigned, the compilation will fail.

 

/* --------- The preceding content is adapted from timeline. ------------*/



 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.