Recode any one of the ch_stack class constructors so they throw exceptions for as many conditions that you think are reasonable. An
exception is an unexpected condition that a program encounters and cannot cope with. An example is floating-point division by zero. Usually the system aborts the running program. Bad dynamic_cast and typeid operations can be made to throw the exceptions bad_cast and bad_typeid, so the user can choose between dealing with the NULL pointer or catching an exception. C++ code is allowed to directly raise an exception in a try block by using the throw expression. The exception is handled by invoking the appropriate handler selected from a list found in the routine containing the try block. A simple example of this is:
vect::vect(int n){
//fault tolerant constructor
try {
if (n < 1)
throw(n);
p = new int[n];
if (p==0)
throw ("FREE STORE EXHAUSTED");
}
catch (int n) {
cerr << "Incorrect vector size: "
<< n << endl;
exit(1);
} //catches an incorrect size
catch (const char* error) {
cerr << error << endl;
delete p [];
exit (1);
}// catches free store exhaustion
}
C systems have stdin, stout, and stderr as standard files. The C++ stream input/output ties these standard files to cin, cout, and cerr respectively.
The standard file cerr is the output stream ostream corresponding to stderr. It is the standard stream for error output. cerr is an object of class ostream that represents the standard error stream. It is associated with the cstdio stream stderr. By default, most systems have their standard error output set to the console, where text messages are shown, although this can generally be redirected. Because cerr is an object of class ostream, we can write characters to it either as formatted data using for example the insertion operator (ostream::operator<<) or as unformatted data using the write member function, among others (see ostream).
More preprocessor Features:
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 in C++ is the process of converting a macro argument into a string literal. It is performed using the **`#` directive** in preprocessor macros. Stringizing allows you to take an argument passed to a macro and transform it into a string literal in your code. This can be useful for debugging, logging, or generating human-readable messages based on macro arguments.
How Stringizing Works
When the `#`operator is used in a macro, it converts the macro's argument into a string literal, preserving the exact text passed to the macro.
Syntax
#define MACRO_NAME(arg) #arg
Example
Here is an example to illustrate stringizing:
#include <iostream>
#define STRINGIZE(x) #x
int main() {
std::cout << STRINGIZE(Hello, World!) << std::endl;
return 0;
}
Output:
Hello, World!
Key Points:
-
Literal Conversion: The
#
operator converts the macro argument into a string literal exactly as written in the code.
-
No Evaluation: The argument is not evaluated; it is treated as plain text.
-
Debugging and Messages: Commonly used for generating readable debug messages or creating symbolic names for arguments.
Use Case: Combining with Macros
Stringizing is often used in combination with other macros to provide more dynamic functionality:
#include <iostream>
#define LOG_VARIABLE(var) std::cout << "Value of " #var " is: " << var << std::endl;
int main() {
int x = 42;
LOG_VARIABLE(x); // Expands to: std::cout << "Value of x is: " << x << std::endl;
return 0;
}
Output:
Value of x is: 42
Stringizing is a simple yet powerful tool in C++ preprocessor macros for enhancing readability and debugging capabilities in code.
Stringizing 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.