수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1621014
  • Today | 394
  • Yesterday | 482

4 Articles, Search for 'Preprocessor'

  1. 2008/04/26 C++ Preprocessor: Always Assert Your Code Is Right
  2. 2008/04/26 C++ Preprocessor: The Code in the Middle
  3. 2007/07/30 C++ Preprocessor: Always Assert Your Code Is Right
  4. 2007/07/30 C++ Preprocessor: The Code in the Middle
Programming/C++2008/04/26 18:20

C++ Preprocessor: Always Assert Your Code Is Right

Site : http://www.devarticles.com/c/a/cplusplu ··· right%2F

Are you looking for a way to speed up the debugging process in C++? This article explains how to use asserts to do just that, showing that not all macros are evil.

If a man begins with certainties, he shall end in doubts;

But if he will be content to begin with doubts,

 He shall end in certainties.

[Francis Bacon 1561-1626]

Assertive Programming

If there is one thing I have learned over the past few years, it is not to underestimate the power of assert(). It comes with your compiler and can be found either in cassert or assert.h.

The reason I love assert is because it looks after me and helps me find bugs I was sure weren’t there. I mean, we all write bugfree software, right? Yeah right. Well there have been many Kodak moments when an assert fired on something I was dead sure could never happen; I should be a rich man as the look on my face was priceless.

If you are not familiar with asserts, you should start using them right now. Use  assert to check that your code is working the way you expect it to or be prepared to pay the price. No not for my face, but for long nights behind the debugger, ticking away possible problem sources until you have your own Kodak moment.

When do you use assert, you ask? Simple… whenever you can use it to verify the truth of a situation: ‘this pointer can never be null’, ‘this number is never smaller than zero’, ‘there is always at least one customer stored in this list’, ‘this code won’t be used the next millennium, two digits are fine’.

Use proper error handling when you can check for things that can go wrong. Use asserts on things you are sure can’t go wrong.

Trust me… the sillier the assert… the more valuable it is. I tested this once myself, grinning, thinking ‘this is ridiculous, want to bet it will never fire?’, only to have it triggered a couple of months later! And because the assert was so preposterous and silly I immediately knew which conditions were breaking my code.

assert.

  1. To state or express positively, affirm: asserted his innocence
  2. To defend or maintain (one’s rights, for example)

The important thing to remember is that asserts are only compiled into your code when the NDEBUG macro is not defined. So in the final optimized version of your application, they are not there to bloat or to slow things down! The only investment you have to make is typing them out while you are working on those classes and functions. It will pay you back greatly when it helps you shorten the time it takes to track down bugs.

Face it… we all write code on assumptions we make about the situation the code will be running in. Assert you are in that situation, because your code base will grow beyond the picture you have of it in your mind.

Implementing Assert

Though we will be implementing our customized version of assert here, all you need to do is include assert.h and you are ready to use the assert macro that comes with it:

The __assert helper function prints an error message to stderr and the program is halted by calling abort(). It is possible that the implementation that comes with your compiler varies slightly, but you get the idea.

Lets use a simple example function:

In the unfortunate situation that the pointer to string is NULL, execution will halt and you will be offered the possibility of opening the debugger and jumping to the location in the source where the assertion failed. This can be very handy as you can examine the call stack, memory, registers, and so forth, and are likely to catch the perpetrator red handed!

Implementing a Simplified Assert

Although I am ranting about why you should use assert, this article is aimed at showing you how to implement your own version using preprocessor macros.

Here is a very basic version that doesn’t even halt execution:

Notice that we don’t need a helper function to get information onto the screen. (I am sticking to printf in the following examples, but there is nothing stopping you from using fprintf and stderr instead.) The stringize macro operator (#) names the condition for us in the printf statement, adding quotes as well and the predefined __LINE__ and __FILE__ macros help to identify the location of the assert.

The do { } while ( false ) statement for the unused version of assert, I used to make sure that the user cannot forget to conclude his assert statement with a semicolon. The compiler will optimize it away, and I consider it a slightly better way to say that I am not doing anything.

We are only one step away from halting our application’s execution (don’t forget to include stdlib.h):

In case writeString is called with a NULL pointer now, we are told what went wrong, where it went wrong and the application is halted.

When you are using the Microsoft Visual C++ 7.1 compiler, you’ll be presented with a dialog screen:

After pressing Retry you are presented with more dialogs and finally... you end up in the debugger.

The problem with abort() is that it doesn’t let you resume execution after you’ve concluded that the assert was benign; or maybe you are interested in what happens immediately after the assert? You need to disable the assert (never a good thing), recompile and restart your application.

There are other ways to halt execution and you will have to look in your compiler’s documentation to discover what the correct call is, but with Visual C++ it’s an interrupt call:

When our application halts again on the writeString function, we’ll encounter the following dialog (did you recognize it? We actually came across this dialog when halting the application with the abort() function!) :

It is not a problem to continue execution now, if we think this is responsible… that is you are often recommended to implement your own assert functionality.

Wouldn’t it be nice to be able to add some hints or remarks along with the location of the assert? When the assert fires and you don’t have a debugger available, this message might still tell you what the problem is:



Refining Assert

The simplest solution is to just expand the assert macro and make it accept a message as well as a condition:



Again the stringize operator helps us to stuff the message we want into the printf statement. We could call it a day now, but I am a very lazy coder… I do not want to be forced to put messages into the assert, sometimes I just want to assert.

Of course it is possible to write an ASSERT(condition) and an ASSERTm(condition, message) macro, but did I mention I am a forgetful coder too? I’d much rather have a single ASSERT statement that can do both.

The first thing that comes to mind is the fact I could do this easily with a function:

So maybe if I declared another function:

void NoAssert(bool isOK, char const *message=””) {}

And then defined assert as:

While this seems like a quick solution, I have completely lost my extra debug information! The line information is the same now for every assert… oh… the compiler substitutes __LINE__ with the actual line number it is compiling at that moment, and since we are making a function call – all line numbers lead to the MyAssert function!

Alexandrescu demonstrates a great way around this problem [Alexandrescu]. (It is also a great article showing how you can take assertions to a higher level after this one, by making them throw exceptions!)



For some reason my Visual C++ 7.1 compiler will not accept the __LINE__ macro next to the __FILE__ macro in the code above. The strange thing is that the __FILE__ macro works fine, but with __LINE__ it complains:

It is never easy, but I do not want to give up at this point. Since the macro is expanded as a single line into the place where we are calling it, and since the compiler apparently has no objection to me assigning __LINE__ as a default parameter in a constructor, let's try again:

Now that we have our line information back, we are nearly there; as soon as we add a second assert, the compiler complains that we are redefining the struct MyAssert! If only we could keep the struct declaration local… and again Alexandrescu shows us how [Alexandrescu]:



There is a lot of fun to be had with macros and sometimes it is possible to create the wildest incantations with them [Niebler/Alexandrescu]. I hope you are convinced that despite the fact that they can be considered evil, there is something magical about them as well.

As a final example I will show you how to create personal and customizable debug streams in the next article… all with macros.

References

 [STL] – The Standard Template Library
< comes standard with your compiler but this one is very portable>
http://www.stlport.org

[BOOST] – Boost C++ Libraries
http://www.boost.org

[ACE] – The ADAPTIVE Communication Environment
http://www.cs.wustl/edu/~schmidt/ACE.html

[Niebler] – Eric Niebler
“Conditional Love: FOREACH Redux”
http://www.artima.com/cppsource/foreach.html

[Alexandrescu] – Andrei Alexandrescu
Assertions
http://www.cuj.com/documents/s=8248/cuj ··· xandr%2F

"C++" 카테고리의 다른 글
  • C++ Preprocessor: Always Assert Your Code Is Right (0)2008/04/26
  • C++ Preprocessor: The Code in the Middle (0)2008/04/26
  • C++ Programming Tips (0)2008/04/26
  • C++ Preprocessor: Always Assert Your Code Is Right (0)2007/07/30
  • C++ Preprocessor: The Code in the Middle (0)2007/07/30
2008/04/26 18:20 2008/04/26 18:20
Posted by webdizen
Tags C++Keyword C++, Preprocessor
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3208

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C++2008/04/26 17:57

C++ Preprocessor: The Code in the Middle

Site : http://www.devarticles.com/c/a/cplusplu ··· iddle%2F

In this article, we examine instructions given to the preprocessor and see how they are used in general. The preprocessor handles your code before the compiler interprets it. If you have been wondering just what the preprocessor is used for, this article explains.

Whence and what art thou, execrable shape?

[John Milton 1608-1674]

The Preprocessor and the Compiler

Before the compiler interprets your code, the preprocessor handles it. Its task is to scan through your code and to look for preprocessor instructions; these can for example be used to replace certain tokens with string or numerical values, or to blank out complete sections of the code before the compiler sees it. You can recognize these instructions from the pound (#) symbol that needs to precede every preprocessor instruction.

We are going to examine these instructions and see how they are used in general. Most probably you have been using at least one of them without giving it much thought: ‘#include’. Still, this is an instruction that is handled by the preprocessor and not by the compiler. It tells it to replace the directive with the contents of the file named after it.

One of the problems a large C++ project can face is long compilation time. This can happen when ‘#include’ is used carelessly; to compile a single source, the preprocessor might (indirectly) be pasting thousands of lines of headers in front of it!

When you use third party libraries like STL-port or Boost, you will find that macros can be very useful for building different code configurations. If you have to write portable C++ code you won’t be able to live without them… however you will most probably have grown to dislike them as well.

Don’t be misled by the fact that preprocessor macros may look deceptively simple. As I’ve said before, with C++ things can become as complex as you want them to be, and to be frank, a lot of great code I have seen involved the use of macros one way or another. Maybe this is because they provide the possibility of extending the language in such a cheeky (and sneaky) way.

Let's not get ahead of ourselves. We'll first look at what the preprocessor is used for most: string substitution and conditional compilation.

String Substitution

The preprocessor literally changes your code (following the instructions you provide of course) and the result is a new source file, a temporary file that you cannot see but which is fed to the compiler. To specify which string tokens you want to have substituted you use the ‘#define’ directive.

For example, when you want to make use of arrays, you will have to specify how big you want one to be. By defining a MAX_STR_LEN token, you can define how big this array is once, instead of having to repeat this value throughout your code.

It is usually not a good idea to use ‘#define’ as a substitute for constants; the preprocessor merely does a string substitution and doesn’t perform any typechecking. Depending on your debugger it can make it difficult to interpret values during a debug session.

In this case it would have been better to use a constant value:

There are programmers that use ‘#define’ to specify constants, so be wary when you detect these. Still I think it is better to use a ‘#define’ then it is to litter your values magically throughout the code.

Imagine that the maximum string length ise shrunk to 72,.and somewhere in the code there is a for-loop that was overlooked, and would still loop until 80. An overflow has been created and worse yet, this may only become clear when the application is run in optimized mode! The loop will modify a section of the memory, causing your application to crash in a mysterious way… oh the agony when you discover the overlooked for-loop.

Sometimes string substitution can be used to help makes things clearer as well… though some consider the following deviation to be distracting:



String Manipulation

When you use the preprocessor for string substitution it can do more than simply replace tokens with values. It is possible to quote strings that were passed as a parameter and you can concatenate two strings together into one.

The stringizing operator (#) substitutes the macro variable following it with its value and puts quotes around it.

For example:

When used like:

will be substituted into and presented to the compiler as:

With the concatenation operator (##) you can tie separate strings into a new single identifier. There are many useful things you can do with this and we’ll actually need this operator when we implement customizable streams.

It is also possible to drive things to extremes. There is a company I know of that requires in its coding standard that you use their ‘Simplified Hungarian naming convention’. This means that a function that takes a const reference to an object MyClass doesn’t look like:

But in their code looks like:

While this might save you some typing effort, I wouldn’t recommend making such drastic changes to the C++ language. Still as an example… how did it ever get this far?

Now you’ve made the following code possible:

Macro Functions

The ‘#define’ directive is very versatile and powerful, given the simple fact that it can replace one string with another string: we can create macro functions.

The MIN and MAX macros are also often quoted as examples:

You have to remember that all this does is string substitution, and any form of type checking is completely lacking!

An important note is that the opening parenthesis for the parameter list must immediately follow the macro name. There are no spaces allowed, since the preprocessor will otherwise consider it to be a regular string substitution.

In case you wonder why the parameters are all wrapped in parenthesis again, consider what would happen if we’d defined SQUARE as:

Now why does the following give a result of 11 instead of 25?

Ouch this is not what we intended:

But using parenthesis it is correct again:

Conditional Compilation

One more way to use ‘#define’ is to declare that a particular token is defined. This token can be used in combination with the precompiler commands “#if”, “#elif”, “#else” and “#endif” to test whether or not the preprocessor should include the code that follows the directive.

To see if a token is defined you use “#if defined” or “#elif defined” and to see if a token is not defined you use “#if !defined” or “#elif !defined”.  You can use the logical ‘&&’ and ‘||’ operators to concatenate instructions.

If for some reason you would like to remove a token from the table the preprocessor stores it in, you can undefine it with ‘#undef’.

A common use is to define a token DEBUG when compiling non-optimized code. If you use the Microsoft Developer Environment, a token _DEBUG is automatically defined for your project’s Debug configuration.



First the precompiler runs into the ‘#include’ instruction, so it reads the ‘stdio.h’ file (which it can find using your project’s header include path) and pastes it into the temporary source file it is going to feed to the compiler. Next it encounters the ‘#define’ instruction and it stores the MYDEBUG token in a lookup table.

Finally when it comes across the ‘#ifdef’ instruction it checks whether MYDEBUG was defined in its lookup table and when this is the case it will continue to write everything it finds until the ‘#endif’ instruction into an intermediate source file.

When it cannot find the token it will check whether the next instruction is another comparison “#elif”, tells it to follow that route otherwise “#else” or that the end of the block was reached “#endif”.

If you look at the macros used to configure STL-Port (STL), Boost (BOOST) or ACE (ACE), things can become rather complex. Handy as macros can be, they do not make the code easer to read (and thus understand). Here is a snippet from the boost thread class:

Because the macros are obfuscating which variables are needed here (because different platforms offer different facilities), it is easy to overlook that on Linux this would filter into:

When you want your code to be portable across different platforms, preprocessor macros will help you to maintain a single source tree. Unfortunately your eyes will have to deal with all possibilities, instead of the single one that your compiler might be choking on.

When there are so many trees it becomes difficult to see the forest.

Inclusion Guards

As your application grows, you will have to organize your code among multiple source and header files. All these sources are compiled into object files, which are linked together into possibly a single executable.

Now that you are using multiple classes, many header files will have to be included into each source file. Sometimes header files will include other header files, and it might be possible that one header file will be included multiple times into a single source file.

Let’s create the ubiquitous Animal class from which we’ll derive a Hippopotamus and a Crocodile. Both classes will have to include the Animal header to be able to derive from Animal. To make things easier for whoever wants to play with a Hippopotamus and/or a Crocodile, we’ll include the Animal class in both respective header files.

Now if you are going to use them together, you’ll be including the Animal header twice. Unless you make sure it is included only once, the compiler will start complaining about multiple definitions. This is why we need inclusion guards:

When the preprocessor tries to include the animal header more than once, it will find that _ANIMAL_H is already defined in its definitions table and skip the header. There is a preprocessor directive that takes care of this for the Microsoft compiler: ‘#pragma once’, but you have to remember that this makes your headers non-portable. So why not stick to inclusion guards instead?

Displaying the Intermediate Form

One of the problems with macros is that it can be very hard to deduce what the state of the preprocessor will be when it reaches a certain directive. If you’ve tried porting STL-port across different platforms, you will know what I am talking about: somewhere at the start a definition is put into the preprocessor’s table, cascading through different header files, down to the point where you are interested to see whether a piece is included in the final source that goes to the compiler or not.

Oh the hairs I would have pulled out of my head if it had not been possible to tell the compiler to show us what it sees. Start a shell and make sure you can use your compiler from the command line. If you are using the Microsoft compiler the following command will dump the output from the preprocessor :

When you are using gcc it can be as simple as:

Just invoke the help on the compiler to see which command line argument you’ll need. Remember, as your project grows more complex, you will have to provide the inclusion paths to headers that are not present in the same directory.

Predefined Macros

Most compilers come standard with a number of macros predefined: __DATE__ , __TIME__ , __LINE__ and __FILE__ .  These macros are very useful when you have to debug different builds. The date and time macros are translated to the date and moment the source was compiled. The line macro translates to the line number it appeared on in the source file and the file macro is translated to the actual filename of the source.

You will see that these macros come in handy when we start customizing some assert functionality.

Macro Troubles

Hopefully you are aware that macros are evil and can cause you a lot of headaches. Even though they have their clear benefits, they can cause a lot of trouble as well. There are four types of problems you can expect when using them.

Macros are not type-safe. When you find that typesafety might be an issue when you are implementing a macro function, you might want to consider replacing it with a templated function.

Macros have to be defined on one line. Things can become pretty complex and hard to read if your macro function extends over several lines. (You can make your macro span several lines by adding a backslash character (\) at the end of every line, except for the last).

Macros perform simple string substitution. This means that there is no such thing as a macro function call; the code defined in the macro is going to be pasted wherever that macro is used. This can cause considerable bloat in your source.

Because macros are expanded inline, not all debuggers can display the contents of the macro during a debug session. This means that you have no means to verify the way your macro function is being executed. This can lead to a lot of pain, only to discover you’ve forgotten a couple of parentheses!

As rough and coarse they may be, there are many nice tricks you can perform with preprocessor macros. Just take a look at the excellent crafted BOOST_FOREACH [Niebler] macro.

You will find a whole new dimension when you are able to combine macros and templates in meaningful ways. Soon I will be able to present to you the Active Object pattern and a nice macro that simplifies the implementation of this pattern using the ACE [ACE] library.

But first things first. Next time we’ll create a custom assert.

References

[STL] – The Standard Template Library
< comes standard with your compiler but this one is very portable>
http://www.stlport.org

[BOOST] – Boost C++ Libraries
http://www.boost.org

[ACE] – The ADAPTIVE Communication Environment
http://www.cs.wustl/edu/~schmidt/ACE.html

[Niebler] – Eric Niebler
“Conditional Love: FOREACH Redux”
http://www.artima.com/cppsource/foreach.html

[Alexandrescu] – Andrei Alexandrescu
Assertions
http://www.cuj.com/documents/s=8248/cuj ··· xandr%2F

"C++" 카테고리의 다른 글
  • C++ Preprocessor: Always Assert Your Code Is Right (0)2008/04/26
  • C++ Preprocessor: The Code in the Middle (0)2008/04/26
  • C++ Programming Tips (0)2008/04/26
  • C++ Preprocessor: Always Assert Your Code Is Right (0)2007/07/30
  • C++ Preprocessor: The Code in the Middle (0)2007/07/30
2008/04/26 17:57 2008/04/26 17:57
Posted by webdizen
Tags C++Keyword C++, Compiler, Preprocessor
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3207

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C++2007/07/30 10:34

C++ Preprocessor: Always Assert Your Code Is Right

출처 : http://www.devarticles.com/c/a/cplusplu ··· right%2F

Are you looking for a way to speed up the debugging process in C++? This article explains how to use asserts to do just that, showing that not all macros are evil.

If a man begins with certainties, he shall end in doubts;

But if he will be content to begin with doubts,

He shall end in certainties.

[Francis Bacon 1561-1626]

Assertive Programming

If there is one thing I have learned over the past few years, it is not to underestimate the power of assert(). It comes with your compiler and can be found either in cassert or assert.h.

The reason I love assert is because it looks after me and helps me find bugs I was sure weren’t there. I mean, we all write bugfree software, right? Yeah right. Well there have been many Kodak moments when an assert fired on something I was dead sure could never happen; I should be a rich man as the look on my face was priceless.

If you are not familiar with asserts, you should start using them right now. Use  assert to check that your code is working the way you expect it to or be prepared to pay the price. No not for my face, but for long nights behind the debugger, ticking away possible problem sources until you have your own Kodak moment.

When do you use assert, you ask? Simple… whenever you can use it to verify the truth of a situation: ‘this pointer can never be null’, ‘this number is never smaller than zero’, ‘there is always at least one customer stored in this list’, ‘this code won’t be used the next millennium, two digits are fine’.

Use proper error handling when you can check for things that can go wrong. Use asserts on things you are sure can’t go wrong.

Trust me… the sillier the assert… the more valuable it is. I tested this once myself, grinning, thinking ‘this is ridiculous, want to bet it will never fire?’, only to have it triggered a couple of months later! And because the assert was so preposterous and silly I immediately knew which conditions were breaking my code.

assert.

  1. To state or express positively, affirm: asserted his innocence
  2. To defend or maintain (one’s rights, for example)

The important thing to remember is that asserts are only compiled into your code when the NDEBUG macro is not defined. So in the final optimized version of your application, they are not there to bloat or to slow things down! The only investment you have to make is typing them out while you are working on those classes and functions. It will pay you back greatly when it helps you shorten the time it takes to track down bugs.

Face it… we all write code on assumptions we make about the situation the code will be running in. Assert you are in that situation, because your code base will grow beyond the picture you have of it in your mind.

Though we will be implementing our customized version of assert here, all you need to do is include assert.h and you are ready to use the assert macro that comes with it:

// assert.h
#ifndef NDEBUG
void __assert(const char *, const char *, int);
#define assert(e) \
     ((e) ? (void)0 : __assert(#e,__FILE__,__LINE__))
#else
#define assert(unused) ((void)0)
#endif

The __assert helper function prints an error message to stderr and the program is halted by calling abort(). It is possible that the implementation that comes with your compiler varies slightly, but you get the idea.

Lets use a simple example function:

void writeString(char const *string) {
assert( 0 != string );
...
}

In the unfortunate situation that the pointer to string is NULL, execution will halt and you will be offered the possibility of opening the debugger and jumping to the location in the source where the assertion failed. This can be very handy as you can examine the call stack, memory, registers, and so forth, and are likely to catch the perpetrator red handed!

Although I am ranting about why you should use assert, this article is aimed at showing you how to implement your own version using preprocessor macros.

Here is a very basic version that doesn’t even halt execution:

#ifndef NDEBUG
# define ASSERT( isOK ) \
( (isOK) ? \
     (void)0 : \
     (void)printf(“ERROR!! Assert ‘%s’ failed on line %d ” \
      “in file ‘%s’\n”, #isOK, __LINE__, __FILE__) )
#else
# define ASSERT( unused ) do {} while( false )
#endif

Notice that we don’t need a helper function to get information onto the screen. (I am sticking to printf in the following examples, but there is nothing stopping you from using fprintf and stderr instead.) The stringize macro operator (#) names the condition for us in the printf statement, adding quotes as well and the predefined __LINE__ and __FILE__ macros help to identify the location of the assert.

The do { } while ( false ) statement for the unused version of assert, I used to make sure that the user cannot forget to conclude his assert statement with a semicolon. The compiler will optimize it away, and I consider it a slightly better way to say that I am not doing anything.

We are only one step away from halting our application’s execution (don’t forget to include stdlib.h):

#ifndef NDEBUG
# define ASSERT( isOK ) \
if ( !isOK ) { \
 (void)printf(“ERROR!! Assert ‘%s’ failed on line %d “ \
  “in file ‘%s’\n”, #isOK, __LINE__, __FILE__) ); \
 abort(); \
}
#else
# define ASSERT( unused ) do {} while ( false )
#endif

In case writeString is called with a NULL pointer now, we are told what went wrong, where it went wrong and the application is halted.

When you are using the Microsoft Visual C++ 7.1 compiler, you’ll be presented with a dialog screen:


After pressing Retry you are presented with more dialogs and finally... you end up in the debugger.

The problem with abort() is that it doesn’t let you resume execution after you’ve concluded that the assert was benign; or maybe you are interested in what happens immediately after the assert? You need to disable the assert (never a good thing), recompile and restart your application.

There are other ways to halt execution and you will have to look in your compiler’s documentation to discover what the correct call is, but with Visual C++ it’s an interrupt call:

__asm { int 3 }

When our application halts again on the writeString function, we’ll encounter the following dialog (did you recognize it? We actually came across this dialog when halting the application with the abort() function!) :

It is not a problem to continue execution now, if we think this is responsible… that is you are often recommended to implement your own assert functionality.

Wouldn’t it be nice to be able to add some hints or remarks along with the location of the assert? When the assert fires and you don’t have a debugger available, this message might still tell you what the problem is:

void writeString(char const *string) {
assert(0!=string, “A null pointer was passed to writeString()!”);
...
}

The simplest solution is to just expand the assert macro and make it accept a message as well as a condition:

#ifndef NDEBUG
# define ASSERT( isOK, message ) \
if ( !(isOK) ) { \
 (void)printf(“ERROR!! Assert ‘%s’ failed on line %d “ \
  “in file ‘%s’\n%s\n”, \
     #isOK, __LINE__, __FILE__, #message); \
  __asm { int 3 } \
 }
#else
# define ASSERT( unused, message ) do {} while ( false )
#endif

Again the stringize operator helps us to stuff the message we want into the printf statement. We could call it a day now, but I am a very lazy coder… I do not want to be forced to put messages into the assert, sometimes I just want to assert.

Of course it is possible to write an ASSERT(condition) and an ASSERTm(condition, message) macro, but did I mention I am a forgetful coder too? I’d much rather have a single ASSERT statement that can do both.

The first thing that comes to mind is the fact I could do this easily with a function:

void MyAssert(bool isOK, char const *message=””) {
if ( !isOK ) {
 (void)printf(“ERROR!! Assert ‘%s’ failed on line %d “
  “in file ‘%s’\n%s\n”,
  __LINE__, __FILE__, message);
 __asm { int 3 } \
}
}

So maybe if I declared another function:

void NoAssert(bool isOK, char const *message=””) {}

And then defined assert as:

#ifndef NDEBUG
# define ASSERT MyAssert
#else
# define ASSERT NoAssert
#endif

While this seems like a quick solution, I have completely lost my extra debug information! The line information is the same now for every assert… oh… the compiler substitutes __LINE__ with the actual line number it is compiling at that moment, and since we are making a function call – all line numbers lead to the MyAssert function!

Alexandrescu demonstrates a great way around this problem [Alexandrescu]. (It is also a great article showing how you can take assertions to a higher level after this one, by making them throw exceptions!)

#ifndef NDEBUG
#define ASSERT \
struct MyAssert { \
MyAssert(bool isOK, char const *message=””) { \
 if ( !isOK ) { \
  (void)printf(“ERROR!! Assert failed in “ \
   “file ‘%s’\n%s\n”, __FILE__, message); \
   __asm { int 3 } \
  } \
 } \
} myAsserter = MyAssert
#endif

For some reason my Visual C++ 7.1 compiler will not accept the __LINE__ macro next to the __FILE__ macro in the code above. The strange thing is that the __FILE__ macro works fine, but with __LINE__ it complains:

error C2065: '__LINE__Var' : undeclared identifier

It is never easy, but I do not want to give up at this point. Since the macro is expanded as a single line into the place where we are calling it, and since the compiler apparently has no objection to me assigning __LINE__ as a default parameter in a constructor, let's try again:

#ifndef NDEBUG
#define ASSERT \
struct MyAssert { \
int mLine; \
MyAssert(int line=__LINE__) : mLine(line) {} \
MyAssert(bool isOK, char const *message=””) { \
 if ( !isOK ) { \
  (void)printf(“ERROR!! Assert failed on “ \
   “line %d in file ‘%s’\n%s\n”, \
   MyAssert().mLine, __FILE__, message); \
  __asm { int 3 } \
 } \
}\
} myAsserter = MyAssert
#endif

Now that we have our line information back, we are nearly there; as soon as we add a second assert, the compiler complains that we are redefining the struct MyAssert! If only we could keep the struct declaration local… and again Alexandrescu shows us how [Alexandrescu]:

#ifndef NDEBUG
#define ASSERT \
if ( false ) {} else struct LocalAssert { \
 int mLine; \
LocalAssert(int line=__LINE__) : mLine(line) {} \
LocalAssert(bool isOK, char const *message=””) { \
 if ( !isOK ) { \
  (void)printf(“ERROR!! Assert failed on “ \
   “line %d in file ‘%s’\n%s\n”, \
   LocalAssert().mLine, __FILE__, message); \
  __asm { int 3 } \
} \
} myAsserter = LocalAssert
#else
#define ASSERT \
if ( true ) {} else struct NoAssert { \
NoAssert(bool isOK, char const *message=””) {} \
} myAsserter = NoAssert
#endif

There is a lot of fun to be had with macros and sometimes it is possible to create the wildest incantations with them [Niebler/Alexandrescu]. I hope you are convinced that despite the fact that they can be considered evil, there is something magical about them as well.

As a final example I will show you how to create personal and customizable debug streams in the next article… all with macros.


References

[STL] – The Standard Template Library

< comes standard with your compiler but this one is very portable>

http://www.stlport.org

[BOOST] – Boost C++ Libraries

http://www.boost.org

[ACE] – The ADAPTIVE Communication Environment

http://www.cs.wustl/edu/~schmidt/ACE.html

[Niebler] – Eric Niebler

“Conditional Love: FOREACH Redux”

http://www.artima.com/cppsource/foreach.html

[Alexandrescu] – Andrei Alexandrescu

Assertions

http://www.cuj.com/documents/s=8248/cujcexp2104alexandr/


"C++" 카테고리의 다른 글
  • C++ Preprocessor: The Code in the Middle (0)2008/04/26
  • C++ Programming Tips (0)2008/04/26
  • C++ Preprocessor: Always Assert Your Code Is Right (0)2007/07/30
  • C++ Preprocessor: The Code in the Middle (0)2007/07/30
  • Multithreading in C++ (0)2007/07/30
2007/07/30 10:34 2007/07/30 10:34
Posted by webdizen
Tags C++Keyword C++, Preprocessor
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3110

Leave your greetings.

[로그인][오픈아이디란?]

Programming/C++2007/07/30 10:33

C++ Preprocessor: The Code in the Middle

출처 : http://www.devarticles.com/c/a/cplusplu ··· iddle%2F

In this article, we examine instructions given to the preprocessor and see how they are used in general. The preprocessor handles your code before the compiler interprets it. If you have been wondering just what the preprocessor is used for, this article explains.

Whence and what art thou, execrable shape?

[John Milton 1608-1674]

The Preprocessor and the Compiler

Before the compiler interprets your code, the preprocessor handles it. Its task is to scan through your code and to look for preprocessor instructions; these can for example be used to replace certain tokens with string or numerical values, or to blank out complete sections of the code before the compiler sees it. You can recognize these instructions from the pound (#) symbol that needs to precede every preprocessor instruction.

We are going to examine these instructions and see how they are used in general. Most probably you have been using at least one of them without giving it much thought: ‘#include’. Still, this is an instruction that is handled by the preprocessor and not by the compiler. It tells it to replace the directive with the contents of the file named after it.

One of the problems a large C++ project can face is long compilation time. This can happen when ‘#include’ is used carelessly; to compile a single source, the preprocessor might (indirectly) be pasting thousands of lines of headers in front of it!

When you use third party libraries like STL-port or Boost, you will find that macros can be very useful for building different code configurations. If you have to write portable C++ code you won’t be able to live without them… however you will most probably have grown to dislike them as well.

Don’t be misled by the fact that preprocessor macros may look deceptively simple. As I’ve said before, with C++ things can become as complex as you want them to be, and to be frank, a lot of great code I have seen involved the use of macros one way or another. Maybe this is because they provide the possibility of extending the language in such a cheeky (and sneaky) way.

Let's not get ahead of ourselves. We'll first look at what the preprocessor is used for most: string substitution and conditional compilation.

The preprocessor literally changes your code (following the instructions you provide of course) and the result is a new source file, a temporary file that you cannot see but which is fed to the compiler. To specify which string tokens you want to have substituted you use the ‘#define’ directive.

For example, when you want to make use of arrays, you will have to specify how big you want one to be. By defining a MAX_STR_LEN token, you can define how big this array is once, instead of having to repeat this value throughout your code.

#define MAX_STR_LEN 80
int main(int argc, char const *argv[]) {
int anArray[MAX_STR_LEN];
for ( int idx=0; idx<MAX_STR_LEN; ++idx )
 anArray[idx]=0;
return 0;
}

It is usually not a good idea to use ‘#define’ as a substitute for constants; the preprocessor merely does a string substitution and doesn’t perform any typechecking. Depending on your debugger it can make it difficult to interpret values during a debug session.

In this case it would have been better to use a constant value:

static int const MAX_STR_LEN=80;

There are programmers that use ‘#define’ to specify constants, so be wary when you detect these. Still I think it is better to use a ‘#define’ then it is to litter your values magically throughout the code.

Imagine that the maximum string length ise shrunk to 72,.and somewhere in the code there is a for-loop that was overlooked, and would still loop until 80. An overflow has been created and worse yet, this may only become clear when the application is run in optimized mode! The loop will modify a section of the memory, causing your application to crash in a mysterious way… oh the agony when you discover the overlooked for-loop.

Sometimes string substitution can be used to help makes things clearer as well… though some consider the following deviation to be distracting:

#define EVER ; ;

for ( EVER ) { …}

When you use the preprocessor for string substitution it can do more than simply replace tokens with values. It is possible to quote strings that were passed as a parameter and you can concatenate two strings together into one.

The stringizing operator (#) substitutes the macro variable following it with its value and puts quotes around it.

For example:

#define LOG(x) std::cout << #x << std::endl

When used like:

LOG(Hello World);

will be substituted into and presented to the compiler as:

std::cout << “Hello World” << std::endl;

With the concatenation operator (##) you can tie separate strings into a new single identifier. There are many useful things you can do with this and we’ll actually need this operator when we implement customizable streams.

It is also possible to drive things to extremes. There is a company I know of that requires in its coding standard that you use their ‘Simplified Hungarian naming convention’. This means that a function that takes a const reference to an object MyClass doesn’t look like:

void foo ( MyClass const &obj );

But in their code looks like:

void foo ( rcMyClass obj );

While this might save you some typing effort, I wouldn’t recommend making such drastic changes to the C++ language. Still as an example… how did it ever get this far?

#define DECLARE_HUNGARIAN( cls ) typedef cls const & rc##cls; \
      typedef cls *       p##cls;

Now you’ve made the following code possible:

DECLARE_HUNGARIAN( MyClass )
pMyObject bar();

Macro Functions

The ‘#define’ directive is very versatile and powerful, given the simple fact that it can replace one string with another string: we can create macro functions.

#define SQUARE(x) ( (x) * (x) )

The MIN and MAX macros are also often quoted as examples:

#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
#define MAX(x,y) ( (x) > (y) ? (x) : (y) )

You have to remember that all this does is string substitution, and any form of type checking is completely lacking!

An important note is that the opening parenthesis for the parameter list must immediately follow the macro name. There are no spaces allowed, since the preprocessor will otherwise consider it to be a regular string substitution.

In case you wonder why the parameters are all wrapped in parenthesis again, consider what would happen if we’d defined SQUARE as:

#define SQUARE(x) ( x * x )

Now why does the following give a result of 11 instead of 25?

int n = SQUARE( 2 + 3 );

Ouch this is not what we intended:

int n = 2 + 3 * 2 + 3;

But using parenthesis it is correct again:

int n = (2 + 3) * (2 + 3)

One more way to use ‘#define’ is to declare that a particular token is defined. This token can be used in combination with the precompiler commands “#if”, “#elif”, “#else” and “#endif” to test whether or not the preprocessor should include the code that follows the directive.

To see if a token is defined you use “#if defined” or “#elif defined” and to see if a token is not defined you use “#if !defined” or “#elif !defined”.  You can use the logical ‘&&’ and ‘||’ operators to concatenate instructions.

If for some reason you would like to remove a token from the table the preprocessor stores it in, you can undefine it with ‘#undef’.

A common use is to define a token DEBUG when compiling non-optimized code. If you use the Microsoft Developer Environment, a token _DEBUG is automatically defined for your project’s Debug configuration.

#include <stdio.h>
#define MYDEBUG
static int const MAX_STR_LEN 80;

int main(int argc, char const *argv[]) {
int anArray[MAX_STR_LEN];
for (int idx=0; idx<MAX_STR_LEN; ++idx) {
#ifdef MYDEBUG
 (void)printf(“%d “, idx);
#endif
 anArray[idx]=0;
}
}

First the precompiler runs into the ‘#include’ instruction, so it reads the ‘stdio.h’ file (which it can find using your project’s header include path) and pastes it into the temporary source file it is going to feed to the compiler. Next it encounters the ‘#define’ instruction and it stores the MYDEBUG token in a lookup table.

Finally when it comes across the ‘#ifdef’ instruction it checks whether MYDEBUG was defined in its lookup table and when this is the case it will continue to write everything it finds until the ‘#endif’ instruction into an intermediate source file.

When it cannot find the token it will check whether the next instruction is another comparison “#elif”, tells it to follow that route otherwise “#else” or that the end of the block was reached “#endif”.

If you look at the macros used to configure STL-Port (STL), Boost (BOOST) or ACE (ACE), things can become rather complex. Handy as macros can be, they do not make the code easer to read (and thus understand). Here is a snippet from the boost thread class:

class BOOST_THREAD_DECL thread : private noncopyable {
...
private:
#if defined(BOOST_HAS_WINTHREADS)
   void* m_thread;
   unsigned int m_id;
#elif defined(BOOST_HAS_PTHREADS)
private:
   pthread_t m_thread;
#elif defined(BOOST_HAS_MPTASKS)
   MPQueueID m_pJoinQueueID;
   MPTaskID m_pTaskID;
#endif
   bool m_joinable;
};

Because the macros are obfuscating which variables are needed here (because different platforms offer different facilities), it is easy to overlook that on Linux this would filter into:

private:
private:
   pthread_t m_thread;
   bool m_joinable;
};

When you want your code to be portable across different platforms, preprocessor macros will help you to maintain a single source tree. Unfortunately your eyes will have to deal with all possibilities, instead of the single one that your compiler might be choking on.

When there are so many trees it becomes difficult to see the forest.

As your application grows, you will have to organize your code among multiple source and header files. All these sources are compiled into object files, which are linked together into possibly a single executable.

Now that you are using multiple classes, many header files will have to be included into each source file. Sometimes header files will include other header files, and it might be possible that one header file will be included multiple times into a single source file.

Let’s create the ubiquitous Animal class from which we’ll derive a Hippopotamus and a Crocodile. Both classes will have to include the Animal header to be able to derive from Animal. To make things easier for whoever wants to play with a Hippopotamus and/or a Crocodile, we’ll include the Animal class in both respective header files.

Now if you are going to use them together, you’ll be including the Animal header twice. Unless you make sure it is included only once, the compiler will start complaining about multiple definitions. This is why we need inclusion guards:

#ifndef _ANIMAL_H
#define _ANIMAL_H
... // your code goes here
#endif

When the preprocessor tries to include the animal header more than once, it will find that _ANIMAL_H is already defined in its definitions table and skip the header. There is a preprocessor directive that takes care of this for the Microsoft compiler: ‘#pragma once’, but you have to remember that this makes your headers non-portable. So why not stick to inclusion guards instead?

Displaying the Intermediate Form

One of the problems with macros is that it can be very hard to deduce what the state of the preprocessor will be when it reaches a certain directive. If you’ve tried porting STL-port across different platforms, you will know what I am talking about: somewhere at the start a definition is put into the preprocessor’s table, cascading through different header files, down to the point where you are interested to see whether a piece is included in the final source that goes to the compiler or not.

Oh the hairs I would have pulled out of my head if it had not been possible to tell the compiler to show us what it sees. Start a shell and make sure you can use your compiler from the command line. If you are using the Microsoft compiler the following command will dump the output from the preprocessor :

cl /EP myfile.cpp

When you are using gcc it can be as simple as:

gcc –E myfile.cpp

Just invoke the help on the compiler to see which command line argument you’ll need. Remember, as your project grows more complex, you will have to provide the inclusion paths to headers that are not present in the same directory.

Most compilers come standard with a number of macros predefined: __DATE__ , __TIME__ , __LINE__ and __FILE__ .  These macros are very useful when you have to debug different builds. The date and time macros are translated to the date and moment the source was compiled. The line macro translates to the line number it appeared on in the source file and the file macro is translated to the actual filename of the source.

You will see that these macros come in handy when we start customizing some assert functionality.

Macro Troubles

Hopefully you are aware that macros are evil and can cause you a lot of headaches. Even though they have their clear benefits, they can cause a lot of trouble as well. There are four types of problems you can expect when using them.

Macros are not type-safe. When you find that typesafety might be an issue when you are implementing a macro function, you might want to consider replacing it with a templated function.

Macros have to be defined on one line. Things can become pretty complex and hard to read if your macro function extends over several lines. (You can make your macro span several lines by adding a backslash character (\) at the end of every line, except for the last).

Macros perform simple string substitution. This means that there is no such thing as a macro function call; the code defined in the macro is going to be pasted wherever that macro is used. This can cause considerable bloat in your source.

Because macros are expanded inline, not all debuggers can display the contents of the macro during a debug session. This means that you have no means to verify the way your macro function is being executed. This can lead to a lot of pain, only to discover you’ve forgotten a couple of parentheses!

As rough and coarse they may be, there are many nice tricks you can perform with preprocessor macros. Just take a look at the excellent crafted BOOST_FOREACH [Niebler] macro.

You will find a whole new dimension when you are able to combine macros and templates in meaningful ways. Soon I will be able to present to you the Active Object pattern and a nice macro that simplifies the implementation of this pattern using the ACE [ACE] library.

But first things first. Next time we’ll create a custom assert.


References

[STL] – The Standard Template Library

< comes standard with your compiler but this one is very portable>

http://www.stlport.org

[BOOST] – Boost C++ Libraries

http://www.boost.org

[ACE] – The ADAPTIVE Communication Environment

http://www.cs.wustl/edu/~schmidt/ACE.html

[Niebler] – Eric Niebler

“Conditional Love: FOREACH Redux”

http://www.artima.com/cppsource/foreach.html

[Alexandrescu] – Andrei Alexandrescu

Assertions

http://www.cuj.com/documents/s=8248/cujcexp2104alexandr/


"C++" 카테고리의 다른 글
  • C++ Programming Tips (0)2008/04/26
  • C++ Preprocessor: Always Assert Your Code Is Right (0)2007/07/30
  • C++ Preprocessor: The Code in the Middle (0)2007/07/30
  • Multithreading in C++ (0)2007/07/30
  • A Simple Garbage Collector for C++ (0)2007/07/30
2007/07/30 10:33 2007/07/30 10:33
Posted by webdizen
Tags C++Keyword C++, Preprocessor
No Trackback No Comment

Trackback URL : http://www.webdizen.net/blog/trackback/3109

Leave your greetings.

[로그인][오픈아이디란?]

«Prev  1  Next»

RSS HanRSS
Blog Image
webdizen
이 곳은 컴퓨터에 대해 연구하고, 공유하고, 소통하기 위한 연구실입니다. 개인적으로는 OLAP, Data Mining, Semantic Web, Data Modeling에 대해서 연구하고 있습니다.

Categories

전체 (2998)
Webdizen (134)
Life (6)
Diary (16)
Blog (9)
IDEA (1)
Travel (10)
Book (14)
Photo (7)
Movie (7)
Music (13)
Leisure Sports (10)
Funny (5)
Hardware (119)
Software (120)
Windows (5)
Unix & Linux (119)
Installation (4)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (805)
Algorithm & Data Structure (1)
Assembly (38)
UNIX/Linux C (95)
C++ (128)
STL (4)
Java (38)
Win32 API (92)
ATL/COM (44)
MFC (151)
.NET (26)
WCF/WPF (4)
C# (28)
Network Programming (17)
Database Programming (12)
OpenGL / DirectX (13)
Multimedia Programming (0)
Game Programming (21)
Parallel Distributed Progra... (0)
Reverse Engineering (0)
Debugging (9)
Python (1)
Ruby (1)
Ruby on Rails (1)
QT (4)
GTK (0)
JSP (0)
PHP (6)
ASP.NET (6)
ASP (3)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (3)
Network (66)
Web (78)
DHTML (4)
XHTML (1)
Javascript (1)
CSS (1)
AJAX (9)
XML (11)
Flex (1)
Silverlight (3)
Security (91)
DoS (1)
Kernel (10)
Scanning (3)
Sniffing (0)
Spoofing (4)
Overflow (28)
Web (11)
Shell (10)
Format String (14)
Window (2)
Embedded (70)
Multimedia (27)
Mobile (14)
Graphic (24)
Management (633)
Knowledge (581)
Hadoop (0)

Notice

  • 메타 블로그 사이트에 등록
  • 새해 맞이 블로그의 변화
  • 블로그 명칭 변경
  • 도메인(www.webdizen.net) 구...
  • TEXTCUBE 1.6.1로 업그레이드...

Tags

  • 고구마
  • 엠파스
  • 라스베이거스
  • Bitmap ImageList
  • 나동
  • Naver
  • w3m
  • 동시성
  • 실사구시관
  • 해네시 VSOP
  • XML Protocols
  • Programmer
  • Stratos Framework
  • Command
  • Browser
  • 대한민국
  • MySQL
  • 올블로그
  • ADO
  • 프레임워크

Recent Articles

  • ASCII Code의 CRLF 제거 방법.
  • Hadoop 에서 c++ API 이용시....
  • Ubuntu Linux에서 Hadoop 구....
  • 내 심장을 한껏 뛰게한 "국가....
  • 스타 스키마 데이터베이스 설....

Recent Comments

  • ■ 온라인카지노 ▶ http://L....
    asdf 11/21
  • 그리고 혹시 해외여행자보험....
    kim 11/05
  • ★★실제 바다게임장과 똑같....
    asdf 11/04
  • sbsyama.co.to← 짱5000만당....
    asdf 11/04
  • ♡KicaZ??o(???) 바카라사....
    fdsf3fass 11/03

Recent Trackbacks

  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 02/11
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007
  • 마방진을 구하는 프로그램.
    Oranzie's BLOG 3 2007

Archive

  • 2009/09 (3)
  • 2009/08 (1)
  • 2009/03 (1)
  • 2009/02 (9)
  • 2009/01 (13)

Calendar

«   2009/11   »
일 월 화 수 목 금 토
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Bookmarks

    • Administration
      • IIS.NET
      • NTFAQ
      • OS의 모든 것
      • 리눅스포털
    • Database
      • SQL Server Central
      • SQL Team
    • Development
      • .NET Heaven
      • ASP Alliance
      • ASP.NET 2.0
      • Bullog.net
      • C# Corner
      • C++ (C PlusPlus.com)
      • C++ Reference
      • CodeGuru
      • CodePlex
      • DebugLab
      • Dev Articles
      • Devpia
      • DotNet Junkies
      • DotNet Zone
      • Driver Online
      • GOSU.NET
      • HOONS 닷넷
      • Joinc 팀블로그
      • KOSR
      • MSDN Home Page
      • OSR Online
      • Sky.ph - 개발자 커뮤니...
      • TAEYO.NET
      • The Code Project
      • WindowsClient.net
      • 김상욱의 개발자 Side
      • 조인시 위키
    • Human Networks
      • belief21c's e-space
      • I think I can
      • Invisible Rover's Blog :D
      • Rodman®
      • ■ Feel So Good~! ■
      • 까만 나비
      • 나를 가꾸는 시간.
      • 나만의 즐거움~~!
      • 단녕
      • 상우 :: Oranzie's BLOG
    • Information Technology
      • Microsoft TechNet
      • 지디넷코리아 - 글로벌...
    • Security
      • FoundStone
      • milw0rm
      • NewOrder
      • OpenRCE
      • Phrack.org
      • Reverse Engineering b1...
      • Reverse Engineering Team
      • RootKit
      • SecurityFocus
      • SecurityXploded by Nag...
      • Wow Hacker
      • Zone-H
Textcube
Louice Studio Inc.
Powered by Textcube. Original designed by Tistory.