Online Book Reader

Home Category

Professional C__ - Marc Gregoire [154]

By Root 1219 0
ap;

if (debug) {

va_start(ap, str);

vfprintf(stderr, str, ap);

va_end(ap);

}

}

Code snippet from VarArgs\VarArgs.cpp

First, note that the prototype for debugOut() contains one typed and named parameter str, followed by ... (ellipses). They stand for any number and types of arguments. In order to access these arguments, you must use macros defined in . You declare a variable of type va_list, and initialize it with a call to va_start. The second parameter to va_start() must be the rightmost named variable in the parameter list. All functions with variable-length argument lists require at least one named parameter. The debugOut() function simply passes this list to vfprintf() (a standard function in ). After the call to vfprintf() returns, debugOut() calls va_end() to terminate the access of the variable argument list. You must always call va_end() after calling va_start() to ensure that the function ends with the stack in a consistent state.

You can use the function in the following way:

debug = true;

debugOut("int %d\n", 5);

debugOut("String %s and int %d\n", "hello", 5);

debugOut("Many ints: %d, %d, %d, %d, %d\n", 1, 2, 3, 4, 5);

Code snippet from VarArgs\VarArgs.cpp

Accessing the Arguments

If you want to access the actual arguments yourself, you can use va_arg() to do so. Unfortunately, there is no way to know what the end of the argument list is unless you provide an explicit way of doing so. For example, you can make the first parameter a count of the number of parameters. Or, in the case where you have a set of pointers, you may require the last pointer to be nullptr. There are many ways, but they are all burdensome to the programmer.

The following example demonstrates the technique where the caller specifies in the first named parameter how many arguments are provided. The function accepts any number of ints and prints them out.

void printInts(int num, ...)

{

int temp;

va_list ap;

va_start(ap, num);

for (int i = 0; i < num; i++) {

temp = va_arg(ap, int);

cout << temp << " ";

}

va_end(ap);

cout << endl;

}

Code snippet from VarArgs\VarArgs.cpp

You can call printInts() as follows. Note that the first parameter specifies how many integers will follow.

printInts(5, 5, 4, 3, 2, 1);

Code snippet from VarArgs\VarArgs.cpp

Why You Shouldn’t Use C-Style Variable-Length Argument Lists

Accessing C-style variable-length argument lists is not very safe. As you can see from the printInts() function, there are several risks:

You don’t know the number of parameters. In the case of printInts(), you must trust the caller to pass the right number of arguments as the first argument. In the case of debugOut(), you must trust the caller to pass the same number of arguments after the character array as there are formatting codes in the character array.

You don’t know the types of the arguments. va_arg() takes a type, which it uses to interpret the value in its current spot. However, you can tell va_arg() to interpret the value as any type. There is no way for it to verify the correct type.

Avoid using C-style variable-length argument lists. It is preferable to pass in an array or vector of values, or to use initializer lists, which are described earlier in this chapter, or to switch to the type-safe C++11 variable-length argument lists using variadic templates, described in Chapter 20.

Preprocessor Macros

You can use the C++ preprocessor to write macros, which are like little functions. Here is an example:

#define SQUARE(x) ((x) * (x)) // No semicolon after the macro definition!

int main()

{

cout << SQUARE(5) << endl;

return 0;

}

Code snippet from Macros\Square.cpp

Macros are a remnant from C that are quite similar to inline functions, except that they are not type checked, and the preprocessor dumbly replaces any calls to them with their expansions. The preprocessor does not apply true function-call semantics. This behavior can cause unexpected results. For example, consider what would happen if you called the SQUARE macro with 2 + 3 instead of 5, like this:

Return Main Page Previous Page Next Page

®Online Book Reader