Detailed description of pre-processing commands and macro definitions in VC

Source: Internet
Author: User
Tags array definition

People who are new to MFC programming are often intimidated by various macro definitions and preprocessing commands generated by the MFC wizard. However, preprocessing and macro definition are a powerful tool in C language. They can be used for simple source code control, version control, warning, or some special functions.

  A classic example

The most classic example of using preprocessing and macro definition is to add a header file to avoid the header file being compiled twice. In this case, there is a file headerfile. h. It is included in headerfile1.h and also included in headerfile2.h. Now there is a CPP file, implement. CPP contains headerfile1.h and headerfile2.h:

# Include "headerfile1.h"
# Include "headerfile2.h"


Assume that headerfile. h defines a global variable iglobal.

Int iglobal;


During compilation, the compiler compiles the headerfile twice and finds that iglobal is defined twice. In this case, a variable redefinition compilation error occurs.

The traditional solution is to use # ifdef and # endif to avoid repeated compilation of header files. In the preceding example, you only need to add the following lines:

# Ifndef smartnose_2002_6_21_headerfile_h
# Define smartnose_2002_6_21_headerfile_h

Int iglobal;

# Endif


After careful consideration of the macro definition above, we will find that when the compiler compiles the headerfile once. H and later, the macro smartnose_2002_6_21_headerfile_h is defined, and headerfile will be used later. the Int iglobal line will be skipped during compilation of H. Of course, the macro smartnose_2002_6_21_headerfile_h can be defined at will, but the macro itself cannot be the same as the macro defined in other files, therefore, MFC always uses a random macro with a very long length in an automatically generated file, but I don't think this is necessary. I suggest you add some meaningful information to this macro, for example, the author, the file name, the file creation time, and so on, because we sometimes forget to add this information to the comment.

In VC. net, we will not see these macro definitions any more, because a preprocessing command is generally used here:

# Pragma once


You only need to add this command at the beginning of the header file to ensure that the header file is compiled once. This command is actually available in vc6, but it is not widely used in consideration of compatibility.

  Source code version control

When we develop multiple versions for many platforms, pre-compilation commands and macro definitions can also help us. Suppose we have developed a set of software for Windows and Linux. Because of the differences between the two systems, we have to control the source code version in the program. For example, for memory allocation, we can use the standard C malloc function on Linux, but we want to use the heapalloc API on Windows. The following code demonstrates this situation:

Main ()
{
....................
# Ifdef _ windows_platform
Heapalloc (5 );
# Else
Malloc (5 );
# Endif
....................
}


When we compile this program on the Windows platform, we only need to define the _ windows_platform macro, so the heapalloc statement can work. In this way, we can implement different versions of code for different platforms in the same file, while maintaining a good program structure. In many cases, we can also use different algorithms for a method, and then use macro definitions to select one of them for compilation based on different situations. This is the most widely used in MFC applications. The most obvious is the frequent

# Ifdef _ debug

......................... Some code ...........

# Endif


Such code plays a role in debugging of the application.

  # Pragma command

Among all the pre-processing commands, the # pragma command may be the most complex. It is used to set the compiler status or to instruct the compiler to complete some specific actions. The format is generally

# Pragma para

Here, para is a parameter. Below are some common parameters.

Message parameter. The message parameter is one of my favorite parameters. It can output relevant information in the compilation information output window, which is very important for controlling source code information. The usage is as follows:

# Pragma message ("message text ")

When the compiler encounters this instruction, it prints the message text in the compilation output window.

When we define many Macros in the program to control the source code version, we may forget whether these macros are correctly set, in this case, we can use this command to check it during compilation. Suppose we want to determine whether we have defined the _ x86 macro in the source code. The following method can be used:

# Ifdef _ x86

# Pragma message ("_ x86 macro activated !")

# Endif


After we define the _ x86 macro, the application will display "_ x86 macro activated!" in the compilation output window during compilation !". We won't be scratching our heads because we don't remember some specific macros we defined.

The other Pragma parameter that is used more frequently is code_seg. Format:

# Pragma code_seg (["section-name" [, "section-class"])


It can set the code segment where function code is stored in the program. It is used when we develop the driver.

The last commonly used command is the # pragma once Command mentioned above.

  Pre-defined macros of VC

In VC, a type of macros are not defined by the # define statement, but can be recognized by the compiler itself. These macros are also very useful. Let's take a look at the first one, which is the most frequently used in MFC: __file __.

When the compiler encounters this macro, it expands it into the file name of the currently compiled file. Now, we can immediately think of what we can do with it. When an application error occurs, we can report the file in which the program code of this error occurs, for example, in the file test. CPP has the following code:

Try
{
Char * P = new (char [10]);
}
Catch (cexception * E)
{
Trace ("There is an error in file: % s/n" ,__ file __);
}


When the program runs, if an error occurs in memory allocation, there is an error in file: test. CPP, of course, we can also display this error message elsewhere.

If we can record the row in which the error occurs, we are lucky that, like the _ file _ macro definition, there is also a macro that records the number of rows of the current Code, this macro is _ line __. Using the two macros above, we can write an Assert statement similar to that provided by VC. The method is as follows:

# Define myassert (X )/
If (! (X ))/
MessageBox (_ file __,__ line __, null, mb_ OK );


In an application, we can use it like an Assert statement. When an error occurs, a dialog box is displayed. Its title and content tell us the file and code line number of the error, this facilitates debugging. This is very useful for projects that cannot use the assert statement.

In addition to these two macros, the _ time __, _ date __, and _ timestamp _ macro that records the Compilation Time.

With these predefined macros, we can generate almost the same complete source code information report as that generated by VC.

  Conclusion

Open the source code of MFC and Linux, and macro definition occupies almost half of the sky. Message ing, queue operations, platform porting, version management, and even kernel module disassembly and installation are all completed with macro definition. It is no exaggeration to say that some files can only see macro definitions. Therefore, learning macro definition and skillful use of macro definition are critical to learning C language and even VC.

I have demonstrated several commonly used macro definition and preprocessing commands above, but it can be said that these are quite common techniques. The macro definition and pre-processing commands described below are also very important techniques used in ATL, MFC, and Linux.

  # Connector and # Connector

# The Concatenation symbol is composed of two pound signs. Its function is to connect two substrings (tokens) in the macro definition with parameters to form a new substring. But it cannot be the first or last substring. The so-called token refers to the minimum syntax unit that the compiler can recognize. The specific definition has a detailed explanation in the compilation principle, but it does not matter if you do not know. At the same time, it is worth noting that the # operator replaces the passed parameters as strings. Let's take a look at how they work. This is an example on msdn.

Suppose the program has defined such a macro with parameters:

# Define Paster (n) printf ("token" # N "= % d", Token # N)

At the same time, an integer variable is defined:

Int token9 = 9;

Call this macro in the following way in the main program:

Paster (9 );

During compilation, the above sentence is extended:

Printf ("token" "9" "= % d", token9 );

Note that in this example, the "9" in Paster (9); is regarded as a string and connected with the "token", thus becoming the token9. # N is also replaced by "9.

As you can imagine, the result of running the program above is to print token9 = 9 on the screen.

In ATL programming, we often see the following section when viewing its source code:

# Define implements_interface (ITF )/
{& IID _ # ITF, entry_is_offset, base_offset (_ itcls, ITF )},

We often use it like this without thinking:

......
Implements_interface (ICAT)
......

In fact, iid_icat is defined by the ATL wizard elsewhere. When there is no wizard, you only need to follow the rules that add IID _ before your interface name to define the guid, you can also use this macro. This kind of technique may be rarely used in the actual development process, but ATL is widely used, and many of them have such source code, so it is very important to understand what it is. One of my friends is busy for a whole day because I don't know how the implements_interface macro is defined, and I accidentally changed the iid_icat definition.

  Linux

I was puzzled by a small Macro when I first started reading Linux:

# Define wait_event (WQ, condition )/
Do {/
If (condition )/
Break ;/
_ Wait_event (WQ, condition );/
} While (0)

This is a strange loop. It only runs once at all. Why not remove the do {...} while structure from the outside? I once called it a "Strange Circle" in my mind ". It turns out to be a very clever technique. It may often cause troubles in the project, and the above definition can ensure that these troubles do not occur. The following is an explanation:

Suppose there is such a macro definition.

# Define macro (condition )/

If (condition) dosomething ();

Use this macro in the program as follows:

If (temp)
Macro (I );
Else
Doanotherthing ();

Everything looks normal, but think about it. This macro was launched as follows:

If (temp)
If (condition) dosomething ();
Else
Doanotherthing ();

In this case, else does not match the first if statement, but matches the second if statement incorrectly. The compilation is successful, but the running result must be incorrect.

To avoid this error, do {...} is used {....} While (0) wraps it into an independent syntax unit, so it is not confused with the context. At the same time, because most compilers can recognize do {...} While (0) is useless loop and optimized, so using this method will not lead to lower program performance.

  A few small warnings

As Microsoft claims, macro definition and pre-compiler commands are powerful, but they make the program difficult to debug. Therefore, when defining a macro, do not save your strings. You must strive to fully describe the macro function. At the same time, do {…} should be used if necessary when defining macros (for example, if statements are used {...} While (0) closes it. When defining a macro, you must pay attention to the dependency between various macros to avoid such dependency. The following is an example.

An integer queue composed of a static array is set. The Int array [] = {5, 6, 7, 8} is used in the definition };

We also need to traverse this array in the program. The common practice is to use a macro definition.

# Define ele_num 4
................................
...................................

For (INT I = 0; I <ele_num; I ++)
{
Cout <array [I];
}

For some accidental reason, we deleted an element in the definition and changed it:

Array [] = {5, 6, 7}

But forget to modify the ele_num value. In the above Code, an access exception occurs immediately and the program crashes. Then we went to sleep for debugging and finally found the problem lies in the macro definition. To solve this problem, do not use

Array [] = {....} This definition, while explicitly declaring the size of the array:

Array [ele_num] = {....}

In this way, when changing the array definition, we will not remember to change the macro definition. In short, the macro definition can be used in all aspects.

Another interesting phenomenon I found is:

Suppose there is a course management system, and the number of students is defined:

# Define stu_num 50

The number of teachers is also 50, so many people use stu_num to cover all the areas involving the number of teachers. One of the students was fired in the past semester and the system needs to change. What should we do? Simple use # define stu_num 49? If so, a teacher will be fired. We have to manually find the stu_num macro in the program and then determine whether it represents the number of students. If so, change it to 49. God, this macro definition makes it much easier to create than to use it. The correct method should be to define another macro for the number of instructors:

# Define tea_num 50

When the number of students changes, the system changes as long as stu_num is defined as 49. Therefore, when there is no inevitable relationship between the two quantities in the program, do not use one macro to replace the other, it will only make your program unable to be modified.

Finally, we recommend that beginners in C/C ++ use macro definition and pre-compiled commands in your program as much as possible. Let's take a look at the source code of MFC, ATL, or Linux. You will find the reason for the powerful C language.

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.