Gotw # 63 mad code
Original article See: www.gotw.ca/gotw/063.htm
X] xuan XIE.
Difficult: 4/10
Sometimes you will encounter some seemingly usually incredible debugging situations. Continue to try to solve this problem and see if you can explain the cause of the problem.
problem:
1. A programmer wrote the following code:
// --- File Biology.h
//
// ... Appropriately include files and other materials ...
Class Animal
{
PUBLIC:
// Based on this type of object:
//
Virtual Int Eat (int) {/*...*/}
Virtual Int Excrete (int) {/*...*/}
Virtual Int Sleep (int) {/*...*/}
Virtual int wake (int) {/*...*/}
// For animals already have spouse,
// Sometimes they don't like their spouses, we provide:
INT Eatex (Animal * a) {/*...*/};
INT Excreteex (Animal * a) {/*...*/};
INT SLEEPEX (Animal * a) {/*...*/};
INT WAKEEX (Animal * a) {/*...*/};
// ...
}
// Specific class.
//
Class cat: public animal {/*...*/};
Class dog: public animal {/*...*/};
Class weevil: public animal {/*...*/};
// ... more cute creatures ...
// Although the redundancy is very convenient to overcharge function.
//
Int Eat (Animal * a) {Return A-> Eat (1);
INT Excrete (Animal * a) {Return A-> Excrete (1);
INT SLEEP (Animal * a) {Return A-> Sleep (1);
INT Wake (Animal * a) {return A-> wake (1);
Unfortunately, the code cannot be compiled. The compiler rejects the definition of one or more ... EX () functions and gives an error message indicating that the function has been defined.
In order to wind the compiler error, this programmer comes out of the ... EX () function, so that the program is compiled and starts testing the Sleeping function. Unfortunately, animal :: sleep () member function seems to have not run correctly; when he tries to call animal :: sleep (), everything is normal. But when he tried to call animal :: Sleep () when he tried to use Sleep () this as a free function - in fact, this package function did not do anything in addition to calling member function versions - sometimes Not doing ... Not every time, this happens to some time. Finally, when the programmer is in the debugger or enters the symbol table generated by the connector to try to find the crucings, he found that the code of Animal :: Sleep () is found.
Did the compiler have a problem? Is this programmer to give a fierce email to the provider of the compiler and submit an angry letter to the New York Times? Is it a millennium? Or just because of the naughty virus from the Internet?
What happened? ?
answer:
Briefly, there are several situations that may result in the above symptoms, but there is a significant possible situation to explain all observed behaviors. Yes, you guessed it, it is a macro and unexpected overloaded heavy load.
Some popular C programming environments provide you with a macro of the change function name. Usually they are working on "good" or "innocent" reasons, that is, to be compatible forward and backward API versions; for example, if a SLEEP () function is replaced in a version of the operating system Sleepex (), then in the header file of the declaration function provided by the supplier, it may contain such a "useful" macro that automatically converts Sleep into Sleepex.
This is not a good idea. Macro is an enemy packaged because it is unable to control their practical scope, even the author of the macro is not powerful.
Because of some reasons, macro is considered "inert-free, stench, chaos-hogging bedfellows, and most of the reasons are as a kind of" The essence of a long-term prestigious text replacement tool. The macro starts role in the pre-treatment period, while any C syntax and semantic rules have not been applied. The following is some of the macros of some lack of attractive features: 1. Macro will be renamed - more time you hurt innocentrs instead of protect them. Conservative, this renaming will disturb the inclusion work to a certain extent. The renaming of this macro means that your function is actually not as you wish. For example, consider our non-member function Sleep (): INT SLEEP (Animal * a) {Return A-> Sleep (1); You can't find Sleep () anywhere in the target code or link table file, because there is no SLEEP () function at all. At first, when you want to know your sleep (), you may feel, "Ah, perhaps the compiler will automatically help me with Sleep () inline (." Because of that It can be explained why short functions looks not to exist - although it is clear that the compiler will not be in the case where there is no direct indication. You immediately concluded, so the fire gave your compiler to your compiler. . Some people in you may have already met unfortunate, and it is really bad. If you are like me, it is easy to be irritated by the compiler, and is not satisfied with a simple explanation, then you can be able to defeat you strongly :) Next, you may open the debugger, deliberately single step tracking Function ... Just being brought into the source code line (in the source code, it looks still in the source code), continue to simply track the phantom function, you will find it is active, Calling, however, all other calls it does not know its existence. Usually, it is only a small step between "What should I do" and "just whisper complaining stupid macro". Wait, the situation is better: 1 (b). C already has the characteristics of solving the name problem. This leads to possible "unhealthy interactions". You may think that change the function name is not a big problem. Ok, very good, usually true. But if you change the name of the function, it is renamed with another existing function ... if the two functions are renowned, what should C ? It will overload them. If you don't realize that you quietly, it is not very good. Ah, this is just like a SLEEP () case. The reason is that the library supplier provides a "useful" SLEEP macro to automatically rename the function to Sleepex, causing a function of renowning in the supplier's library. If the different functions have different signatures? Then we write our own SLEEP () function, we can find the overloaded overload in the library, carefully avoid uncertainty and other errors that lead to problems. We have even rely on overloading because we might deliberately provide a similar behavior with Sleep () in the library. In other words, once our function is quietly overloaded, it is not only the overloaded function behavior than we expect, and if we originally design overload, then it will not do anything, At least it does not work as we think. In the context of our problem, such a rename macro can only explain why the final call is completely different in different environments. Which function is called to rely on a specific type determined by overload resolutions in different call points. Sometimes our function is sometimes library function. It is just dependence, perhaps in an unaptimistic way. This is not enough if this story ends with an unpleasant impact on non-member functions. Unfortunately, some dangerous things are flying in other directions like a SHRAPNEL: 2. Macro Ignore type The original intent of the Sleep macro described above is to change the name of a global non-member function. However, this macro changed all the places that appeared in Sleep; if we just have a global variable Sleep, then its name will be quietly changed. This is definitely a bad thing. 3. Macro 域 域 范围 Worse, a macro that changes global non-member functions will be happy to change all the names of all matching functions (or other things), maybe these functions are members of the class or have been packed in your own name space. bingo. In this example, we wrote classes that contain sleep () and sleepex (); many of the problems are related to the Sleep reamer, at least partially. This macro is implicitly overloaded with each other. It is this invisible overload that explains why it is called in the first point in the first point. This is definitely a bad thing. This is like a japanese, unlicensed doctor (not smarter) with dirty hands ("" Unaviable "macro) cut your body (class or name space), then in your The body cavity is rearranged (class members and other code) ... and all this is actually finished in dreaming (nothing to do at what they are doing). In short, macro is not satisfied with anything :) Strategy: Avoid macros. You should be like this, "Macro! Hey, I hate!" Unless they are forced to use them in some special circumstances, and to ensure they do not destroy. The macro is not type security (type-safen) ... They are not safe. If you must write a macro, you should avoid them in the header file, and to try to give them a name that is long enough, this is not intentionally hurt to something else. Strategy: Try to use the namespace.