Home arrow C++ Programming arrow Inline Functions

Language Translator

Hacking Zone

Hacking Tools
Attacking

Configure Windows

Windows Configuration

Novels

Mix Novels

Human Personality

Body Language
Inline Functions PDF Print E-mail
Written by Hemanshu Patel   
Monday, 22 October 2007
Article Index
Inline Functions
Page 2
Page 3
Page 4
Page 5

More preprocessor features

Earlier, I said that you almost always want to use inline functions instead of preprocessor macros. The exceptions are when you need to use three special features in the C preprocessor (which is also the C++ preprocessor): stringizing, string concatenation, and token pasting. Stringizing, introduced earlier in the book, is performed with the # directive and allows you to take an identifier and turn it into a character array. String concatenation takes place when two adjacent character arrays have no intervening punctuation, in which case they are combined. These two features are especially useful when writing debug code. Thus,

#define DEBUG(x) cout << #x " = " << x << endl

This prints the value of any variable. You can also get a trace that prints out the statements as they execute:

#define TRACE(s) cerr << #s << endl; s

The #s stringizes the statement for output, and the second s reiterates the statement so it is executed. Of course, this kind of thing can cause problems, especially in one-line for loops:

for(int i = 0; i < 100; i++)
TRACE(f(i));

Because there are actually two statements in the TRACE( ) macro, the one-line for loop executes only the first one. The solution is to replace the semicolon with a comma in the macro.

 

Token pasting

Token pasting, implemented with the ## directive, is very useful when you are manufacturing code. It allows you to take two identifiers and paste them together to automatically create a new identifier. For example,

#define FIELD(a) char* a##_string; int a##_size
class Record {
FIELD(one);
FIELD(two);
FIELD(three);
// ...
};

Each call to the FIELD( ) macro creates an identifier to hold a character array and another to hold the length of that array. Not only is it easier to read, it can eliminate coding errors and make maintenance easier.

 

Improved error checking

The require.h functions have been used up to this point without defining them (although assert( ) has also been used to help detect programmer errors where it’s appropriate). Now it’s time to define this header file. Inline functions are convenient here because they allow everything to be placed in a header file, which simplifies the process of using the package. You just include the header file and you don’t need to worry about linking an implementation file.

You should note that exceptions (presented in detail in Volume 2 of this book) provide a much more effective way of handling many kinds of errors – especially those that you’d like to recover from – instead of just halting the program. The conditions that require.h handles, however, are ones which prevent the continuation of the program, such as if the user doesn’t provide enough command-line arguments or if a file cannot be opened. Thus, it’s acceptable that they call the Standard C Library function exit( ).

The following header file is placed in the book’s root directory so it’s easily accessed from all chapters.

//: :require.h
// Test for error conditions in programs
// Local "using namespace std" for old compilers
#ifndef REQUIRE_H
#define REQUIRE_H
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>

inline void require(bool requirement,
const std::string& msg = "Requirement failed"){
using namespace std;
if (!requirement) {
fputs(msg.c_str(), stderr);
fputs("\n", stderr);
exit(1);
}
}

inline void requireArgs(int argc, int args,
const std::string& msg =
"Must use %d arguments") {
using namespace std;
if (argc != args + 1) {
fprintf(stderr, msg.c_str(), args);
fputs("\n", stderr);
exit(1);
}
}

inline void requireMinArgs(int argc, int minArgs,
const std::string& msg =
"Must use at least %d arguments") {
using namespace std;
if(argc < minArgs + 1) {
fprintf(stderr, msg.c_str(), minArgs);
fputs("\n", stderr);
exit(1);
}
}

inline void assure(std::ifstream& in,
const std::string& filename = "") {
using namespace std;
if(!in) {
fprintf(stderr, "Could not open file %s\n",
filename.c_str());
exit(1);
}
}

inline void assure(std::ofstream& out,
const std::string& filename = "") {
using namespace std;
if(!out) {
fprintf(stderr, "Could not open file %s\n",
filename.c_str());
exit(1);
}
}
#endif // REQUIRE_H ///:~

The default values provide reasonable messages that can be changed if necessary.

You’ll notice that instead of using char* arguments, const string& arguments are used. This allows both char* and strings as arguments to these functions, and thus is more generally useful (you may want to follow this form in your own coding).

In the definitions for requireArgs( ) and requireMinArgs( ), one is added to the number of arguments you need on the command line because argc always includes the name of the program being executed as argument zero, and so always has a value that is one more than the number of actual arguments on the command line.

Note the use of local “using namespace std” declarations within each function. This is because some compilers at the time of this writing incorrectly did not include the C standard library functions in namespace std, so explicit qualification would cause a compile-time error. The local declaration allows require.h to work with both correct and incorrect libraries without opening up the namespace std for anyone who includes this header file.

Here’s a simple program to test require.h:

//: C09:ErrTest.cpp
//{T} ErrTest.cpp
// Testing require.h
#include "../require.h"
#include <fstream>
using namespace std;

int main(int argc, char* argv[]) {
int i = 1;
require(i, "value must be nonzero");
requireArgs(argc, 1);
requireMinArgs(argc, 1);
ifstream in(argv[1]);
assure(in, argv[1]); // Use the file name
ifstream nofile("nofile.xxx");
// Fails:
//! assure(nofile); // The default argument
ofstream out("tmp.txt");
assure(out);
} ///:~

You might be tempted to go one step further for opening files and add a macro to require.h:

#define IFOPEN(VAR, NAME) \
ifstream VAR(NAME); \
assure(VAR, NAME);

Which could then be used like this:

IFOPEN(in, argv[1])

At first, this might seem appealing since it means there’s less to type. It’s not terribly unsafe, but it’s a road best avoided. Note that, once again, a macro looks like a function but behaves differently; it’s actually creating an object (in) whose scope persists beyond the macro. You may understand this, but for new programmers and code maintainers it’s just one more thing they have to puzzle out. C++ is complicated enough without adding to the confusion, so try to talk yourself out of using preprocessor macros whenever you can.

 

Summary

It’s critical that you be able to hide the underlying implementation of a class because you may want to change that implementation sometime later. You’ll make these changes for efficiency, or because you get a better understanding of the problem, or because some new class becomes available that you want to use in the implementation. Anything that jeopardizes the privacy of the underlying implementation reduces the flexibility of the language. Thus, the inline function is very important because it virtually eliminates the need for preprocessor macros and their attendant problems. With inlines, member functions can be as efficient as preprocessor macros.

The inline function can be overused in class definitions, of course. The programmer is tempted to do so because it’s easier, so it will happen. However, it’s not that big of an issue because later, when looking for size reductions, you can always change the functions to non-inlines with no effect on their functionality. The development guideline should be “First make it work, then optimize it.”




Digg!Reddit!Del.icio.us!Google!Live!Facebook!Slashdot!Netscape!Technorati!StumbleUpon!Spurl!Wists!Simpy!Newsvine!Blinklist!Furl!Fark!Blogmarks!Yahoo!Smarking!Netvouz!Shadows!RawSugar!Ma.gnolia!PlugIM!Squidoo!BlogMemes!FeedMeLinks!BlinkBits!Tailrank!linkaGoGo!Free social bookmarking plugins and extensions for Joomla! websites! title=
Comments
Add NewSearch
Only registered users can write comments!

Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved.



 
< Prev   Next >
Your Ad Here

Donate us!!

Enter Amount:

RSS socialnet

Add to MyYahoo!
Subscribe in NewsGator Online
Add to Newsburst
Add to Google
Add to My AOL
Add to Pluck
Subscribe in FeedLounge
Add to Windows Live
Add to NetVibes
Subscribe in Rojo
Subscribe in Bloglines
Add to MyMSN
Add to Plusmo for your cellphone
Add to PageFlakes
Add to Technorati
Add to BlinkBits