Archive for the ‘C++’ Category

More C++ Preprocessor Directives

Tuesday, June 5th, 2007

The #pragma directive is a compiler specific directive which compiler vendors may use for their own purposes. For instance, a #pragma is often used to allow suppression of specific error messages, manage heap and stack debugging, etc.

You can see the C# .Net equivalent here.

In Microsoft Visual C++ 8 (Visual Studio 2005), one useful use for the #pragma directive is to modify compiler warnings. This is from Microsoft:

#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...] )
#pragma warning( push[ ,n ] )
#pragma warning( pop )

The warning-specifier can be one of the following.

Warning-specifier Meaning
1, 2, 3, 4 Apply the given level to the specified warning(s). This also has the effect of turning a specified warning on that is off by default.
default Reset warning behavior to its default value. This also has the effect of turning a specified warning on that is off by default. The warning will be generated at its default, documented, level.See Compiler Warnings That Are Off by Default for more information.
disable Do not issue the specified warning message(s).
error Report the specified warnings as errors.
once Display the specified message(s) only once.
suppress Pushes the current state of the pragma on the stack, disables the specified warning for the next line, and then pops the warning stack, resetting the pragma state. You can only specify one warning for each suppress specifier, but multiple warning pragmas can operate on one line of code.suppress is only supported for C6000 warnings (code analysis warnings), which are enabled with the /analyze (Enterprise Code Analysis) compiler option.

The warning-number-list can contain any warning numbers. Multiple options can be specified in the same pragma directive as follows:

#pragma warning( disable : 4507 34; once : 4385; error : 164 )

This is functionally equivalent to:

// Disable warning messages 4507 and 4034.
#pragma warning( disable : 4507 34 )
 
// Issue warning 4385 only once.
#pragma warning( once : 4385 )
 
// Report warning 4164 as an error.
#pragma warning( error : 164 )

The compiler will add 4000 to any warning number that is between 0 and 999.

The compiler only supports up to 56 #pragma warning statements in a compiland.

For warning numbers in the range of 4700-4999, those associated with code generation, the state of the warning in effect when the compiler encounters the open curly brace of a function will be in effect for the rest of the function. Using the warning pragma inside the function to change the state of a warning greater than 4699 will only take effect after the end of the function. The following example shows the correct placement of warning pragmas to disable, and then restore, a code-generation warning message:

// pragma_warning.cpp
// compile with: /W1
#pragma warning(disable:4700)
void Test() {
   int x;
   int y = x;   // no C4700 here
   #pragma warning(default:4700)   // C4700 enabled after Test ends
}
 
int main() {
   int x;
   int y = x;   // C4700
}

Note that within a function body, the last setting of the warning pragma will be in effect for the entire function.

The warning pragma also supports the following syntax:

#pragma warning( push [ ,n ] )

#pragma warning( pop )

Where n represents a warning level (1 through 4).

The pragma warning( push ) stores the current warning state for all warnings. The pragma warning( push, n) stores the current state for all warnings and sets the global warning level to n.

The pragma warning( pop ) pops the last warning state pushed onto the stack. Any changes made to the warning state between push and pop are undone. Consider this example:

#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
// Some code
#pragma warning( pop )

At the end of this code, pop restores the state of all warnings (including 4705, 4706, and 4707) to what it was at the beginning of the code.

When you write header files, you can use push and pop to ensure that changes to warning states made by the user do not prevent your headers from compiling properly. Use push at the beginning of the header and pop at the end. Suppose, for example, you have a header that does not compile cleanly at warning level 4. The following code changes the warning level to 3 then restores the original warning level at the end of the header:

#pragma warning( push, 3 )
// Declarations/ definitions
#pragma warning( pop )

See /FI and /w for compiler options that help you suppress warnings.

C++ Preprocessor Variables

Thursday, May 17th, 2007

I went several years programming in C/C++ without really knowing about these preprocessor variables. They can be very useful for debugging and logging of custom run time errors/warnings. I have not found a definitive list of them, but here is a list of some useful ones that I know of. (The following variables can vary by compiler, but generally work)

__LINE__ Contains the current line number being processed.
__FILE__ Contains the current file name (full path) being processed.
__DATE__ Contains the compile date, This is the date that the file was compiled, not necessarily the current date.
__TIME__ Contains the compile time. This is the time that the file was compiled, not necessarily the current time.
__TIMESTAMP__ Contains the current date and time. This is the date and time that the file was compiled, not necessarily the current date and time.
__FUNCTION__ Contains the function name. (this is part of C99, the new C standard. Not all C++ compilers support it)
__cplusplus Defined when compiling a C++ program. In some older compilers, this is also called c_plusplus.
__STDC__ Defined when compiling a C program, and may also be defined when compiling C++

Instead of explaining these variables, here is an example program that demonstrates how you may use some of them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
 
#define PUKE_TRACE(X,Y) puke_trace(__FUNCTION__, __FILE__, __LINE__, __threadid(), X, Y)
void puke_trace(const char* func, const char* file, int line,
				unsigned long thread, const char* msg, std::ostream& os)
{
	os << msg << " : Line " << line << ", file " << file
		<< ", function " << func << ", thread " << thread << ".\n";
}
 
int main(int argc, char* argv[])
{
	PUKE_TRACE("Test 1.",std::cout);
	PUKE_TRACE("Test 2.",std::cerr);
	PUKE_TRACE("Test 3.",std::clog);
	return 0;
}

The above program will output something like this:

Test 1. : Line 13, file /path/to/test.cpp, function main, thread 3712.
Test 2. : Line 14, file /path/to/test.cpp, function main, thread 3712.
Test 3. : Line 15, file /path/to/test.cpp, function main, thread 3712.

__threadid() is not a preprocessor variable obviously, but it can be useful when tracing/debugging/logging multi-threaded applications, usually used in conjunction with preprocessor variables.

References:
http://www.codeguru.com/forum/showthread.php?t=231043
http://www.cppreference.com/preprocessor/preprocessor_vars.html

C++ Notes: Shallow vs Deep Copies

Monday, April 16th, 2007

A shallow copy of an object copies all of the member field values. This works well if the fields are values, but may not be what you want for fields that point to dynamically allocated memory. The pointer will be copied. but the memory it points to will not be copied — the field in both the original object and the copy will then point to the same dynamically allocated memory, which is not usually what you want. The default copy constructor and assignment operator make shallow copies.A deep copy copies all fields, and makes copies of dynamically allocated memory pointed to by the fields. To make a deep copy, you must write a copy constructor and overload the assignment operator, otherwise the copy will point to the original, with disasterous consequences.

Deep copies need …

If an object has pointers to dynamically allocated memory, and the dynamically allocated memory needs to be copied when the original object is copied, then a deep copy is required.

A class that requires deep copies generally needs:

Borrowed from this site.