수안이의 컴퓨터 연구실

  • Mainpage
  • About Me
  • Tags
  • Metapage
  • Notice
  • Location
  • Keywords
  • Guestbook
  • Admin
  • Write an Article
  • Total | 1694169
  • Today | 520
  • Yesterday | 588

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.

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

«Prev  1 ... 87 88 89 90 91 92 93 94 95 ... 3009  Next»

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

Categories

전체 (3009)
Webdizen (141)
Life (6)
Diary (16)
Blog (9)
IDEA (2)
Travel (10)
Book (16)
Photo (7)
Movie (8)
Music (14)
Leisure Sports (10)
Funny (6)
Hardware (121)
Software (120)
Windows (5)
Unix & Linux (120)
Installation (5)
Kernel (10)
System (34)
Develop (22)
X-Window (0)
Applicaton (31)
Security (4)
Framework (2)
Hadoop (2)
Programming (804)
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 (2)
Development (28)
Useful Library (2)
Data Modeling (0)
Database (105)
Oracle (4)
MSSQL (41)
MySQL (2)
Data Warehouse (2)
Data Mining (4)
Network (66)
Web (79)
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

  • 디자인 패턴
  • 관찰
  • Memory
  • 발렌타인
  • 어학교육원
  • RPM
  • 집현관
  • 진로 포도주
  • Jelix
  • log
  • XMLParser
  • 실사구시관
  • 춘천캠퍼스
  • 가상 메모리 크기
  • Browser detection
  • Error
  • 키워드
  • 데이타 웨어하우스
  • 기억력
  • 바르셀로나

Recent Articles

  • 트위터(Twitter)의 시작!.
  • 청년 리더의 조건.
  • 애플의 타블렛 PC - 아이패드....
  • 미래의 인터페이스 - 육감 기....
  • 기초발성법 동영상 강좌.

Recent Comments

  • 학교 과제물중 쓰레드에 대하....
    장진혁 03/17
  • 관리자만 볼 수 있는 댓글입....
    비밀방문자 03/12
  • 상대방의 이야기를 열심히 경....
    DoNuts 03/03
  • Lots of students know techn....
    Bobbi35Shannon 02/25
  • 좋은글 잘 보고 갑니다..
    Und_hacker 01/08

Recent Trackbacks

  • printf,scanf를 이용한 형식....
    yundream의 프로그래밍 이야기 03/10
  • 파일 열기/저장하기 CFileDialog.
    은마군의 나태블록 2009
  • World IT Show 2008.
    상우 :: Oranzie's BLOG 2008
  • cvs서버 설치하기.
    3인3색 2008
  • 속속 공개되는 Google Chart....
    PHP와 Web 2.0 2007

Archive

  • 2010/02 (1)
  • 2010/01 (6)
  • 2009/12 (5)
  • 2009/09 (3)
  • 2009/08 (1)

Calendar

«   2010/03   »
일 월 화 수 목 금 토
  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 31      

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.