Microsoft Visual C++ Team
Microsoft Corporation
August 28, 1998
Summary: Provides a technical drill-down of the new features of Visual C++ 6.0. Familiarity with Visual C++ necessary. (11 printed pages) Discusses:
The Microsoft® Visual C++® version 6.0 development system includes many new features to enhance developer productivity, improve executable performance, and ensure the highest levels of control. However, before migrating to Visual C++ 6.0, it is prudent to carefully analyze the changes made to the product and consider their impact on your code and development cycle. This guide will assist your migration to the Microsoft Visual C++ 6.0 development system and focuses specifically on the compiler, linker, and debugger with the goal of enabling you to take the fullest advantage of Visual C++ 6.0.
Note Projects built in previous 32-bit versions of Visual C++ can be opened and saved in Visual C++ 6.0 projects automatically. For more information, see the "Porting and Upgrading" section of the Visual C++ Programmer's Guide (available in the MSDN Library that ships with Visual C++ 6.0 and online at http://msdn.microsoft.com/developer/default.htm).
Microsoft IntelliSense® Technology (also known as IntelliSense or Statement Completion) allows easy access to useful definitions while writing C or C++ source code in the editor. Function names, parameters, and comments are displayed. When you type a character that offers an opportunity for Visual C++ to automatically complete (such as '.' or '->' on a variable name, or '(' on a function name, additional information will be presented. If you don't want this to happen automatically, you can clear any of the "Statement completion options" check boxes in the Editor tab of the Options dialog box. The following text describes the different IntelliSense options and usage scenarios.
When you type the name of a class or struct and type '.' or '->', Visual C++ will display an alphabetically sorted list of member variables and functions. You can select from the list of candidates with the mouse or by typing the first characters of the item you want to select.
When displaying the list of variables/members, Visual C++ will search the header in which the class is defined for comments for that item (first next to the function definition, and then before it if there are none next to it). If comments are found, they will be displayed in a ToolTip window next to the parameter list. The standard C and C++ comment delimiters are supported.
When you have chosen a function, Visual C++ presents a ToolTip window with the function's parameters after you type the '(' character. As arguments are entered, the ToolTip window highlights the current parameter in boldface. Overloaded functions are supported by clicking in the ToolTip to switch between implementations.
Auto Type Info displays the type of a variable while you're editing the file. Simply place the pointer on the variable, pause, and the type of the variable will appear in a ToolTip.
Global completion allows you to select from system application programming interface (API) functions, available C++ classes, instance variables, and local variables. Because there is no way to automatically detect when such a case exists, you must press <CTRL><SPACE> to bring up the function/class list.
The same technology in Visual C++ 6.0 that enables IntelliSense in the editor also enables Dynamic ClassView updating. Dynamic ClassView updating is a feature that ensures the ClassView is up-to-date with class, function, and variable information immediately, without requiring a save, rebuild, or compile. As you enter a variable declaration, for example, the variable will appear in the ClassView as soon as the type name and one character of the variable name have been entered.
Similar to keystroke recording and playback, Quick Macro Recording is a menu shortcut to the more extensive macro recording functionality. You can record any action supported by the Developer Studio Object Model into a temporary VBScript macro with this feature, available from the Tools menu.
New compiler warnings have been added to help catch some common programming errors.
Unreferenced variables are now detected in both default (/Od) and optimized (/Ox) builds. The compiler will also now detect cases where a local variable is initialized but not referenced:
int test()
{
int nValue = 15;
// nValue not used in code...
}
Additionally, these errors are now reported at the line declaring the symbol rather than the end of the function.
The compiler will now catch cases such as:
if (<cond>); // note extra semi-colon
<statement>; // should execute only if <cond> is true
and issue the following warning:
4390 : empty controlled statement found; is this what was intended?
The compiler detects a variety of situations where storage-class specifiers could be incorrectly placed. For example:
__declspec(dllexport) extern "C" void foo(); // Here
class C {
int * virtual vfunc(); // And here
};
will now generate the following warning:
4518 : storage-class or type specifier(s) unexpected here; ignored
Cases where an expression statement has an operator with no side effect as the top of the expression are now detected:
int i;
i == func(); // user intended i = func();
// new warning C4553: '==' : operator has no effect;
// did you intend '='?
or:
int i, j;
i + j; // user intended ???
// new warning 4552: operator has no effect, did you
// forget something?
Detects a possible operator precedence error:
a = a << b + c; // user probably intended (a << b) + c,
// but will get a << (b + c)
will now generate the following error:
4554 : possible operator precedence error; use parentheses to avoid ambiguity
With previous versions of Visual C++, it was sometimes difficult to find the original definition that led to a redefinition warning. In Visual C++ 6.0, the compiler will now give the location of both declarations:
int i;
char i;
results in the following messages:
test.cpp(3) : error C2371: 'i' : redefinition; different basic types
test.cpp(1) : see declaration of 'i'
In previous versions of Visual C++, it was sometimes difficult to understand error messages in code that uses templates. Visual C++ 6.0 provides additional information to make this task easier. In the following example:
template <typename T> class foo {
T aT; // Can't specialize on void
};
template <typename T> class foo2 {
foo<T> myFoo; // This is OK, as long as foo<T> is OK.
};
void bar()
{
foo2<void> bad;
}
Visual C++ 5.0 would give only a first error message, shown below. Visual C++ 6.0 adds additional information to locate the instantiations involved:
test.cpp(6) : error C2182: 'aT' : illegal use of type 'void'
While compiling class template instantiation 'foo<void>' referenced at : test.cpp(10)
While compiling class template instantiation 'foo2<void>' referenced at : test.cpp(15)
The inline and __inline keywords are hints to try to inline the function, and the optimizer decides whether inlining should be performed based upon optimization switches (optimizing for size versus speed) and other heuristics.
The __forceinline keyword has been added to override the decision of the optimizer, and inline wherever possible. In some cases, the compiler will not inline because of command-line switches or other reasons. For example, the optimizer will not inline in the following cases:
#pragma(inline_depth, <n>)
where n < 256 and #pragma inline_recursion(on | off).
If the compiler cannot inline a function with __forceinline, it will generate a level 1 warning (4714).
It is important to understand the behavior of your code and the trade-offs between code size and code speed when using this keyword and measure the results, because it could result in larger and slower code.
Note The proper functioning of __forceinline cannot necessarily be verified from the map file, because taking the address of a function or exporting a function will result in an instance of the function being generated (though the function will be inlined where called by name).
An expression that takes the address of a function will force an instance of the function in the OBJ. If a function is called through a function pointer, the instance of the function in the OBJ will be called, although the function will be inlined (subject to the other restrictions) when called by name.
This is by design. Inlining is a compile-time operation and calling a function through a function pointer is a run-time operation. In all but the most trivial examples (for instance, when the compiler can optimize the 'address of a function/call through the pointer' back to the function) calling a function through a function pointer will not inline and is not expected to inline.
The __assume keyword is a hint to the optimizer that says the expression passed as the argument is true until the expression is killed (by interference with other variables, assignment to one of the variables, and so on). It can be used in conjuction with assert() during debug builds to ensure that the expression used with __assume is always true.
The format of the keyword is:
__assume(expr)
A typical usage of __assume is in assert macros. For example:
#ifdef DEBUG
# define ASSERT(e) (void) ((e) ? 0 : assert(__FILE__, __LINE__))
# define ASSERTNR(e) (void) ((e) ? 0 : assert(__FILE__, __LINE__))
#else // RELEASE build
# define ASSERT(e) (void) (((e) ? 0 : assert(__FILE__, __LINE__)), __assume(e))
# define ASSERTNR(e) (__assume(e))
#endif
#define UNREACHABLE 0
void foo(int *p)
{
// Optimizer can eliminate p==NULL checks which might follow
ASSERTNR(p!=NULL);
switch(bar()){
case 1:
blah(1);
break;
case 2:
blah(-1);
break;
default:
// default case unreachable, so the optimizer
// can assume bar() only returns 1 or 2, and
// eliminate range checks
ASSERTNR(UNREACHABLE);
}
}
The Visual C++ 6.0 compiler can embed certain run-time checks in code to detect common bugs. Enabling these checks is done using the -GZ compiler flag. Run-time checks can only be used in debug (-Od) builds.
The following checks are added.
All local variables are initialized to a specific value (0xCC). This should help catch uses of uninitialized variables.
The stack pointer (ESP) is checked to make sure it is the same before and after the call through the function pointer. This will catch cases where a function prototype has the incorrect number of parameters if the function uses the __stdcall, __fastcall, or __thiscall calling convention. __cdecl cases are not caught because the caller does both the pushes and the pops of parameters.
It will also catch cases where a __stdcall function is called through a __cdecl function pointer.
The stack pointer (ESP) is checked at the end of the function to see that the stack pointer has not been changed. This can catch cases where ESP is corrupted in inline assembly or a function's calling convention is misdeclared. For example:
void foo(int i); // misdefinition - should be void __stdcall foo(int i);
void bar() {
...
foo(4);
...
// the ESP corruption caused by foo will be caught here, as bar exits
}
In this case, the improper prototype of foo() is caught.
The placement form of operator delete is now supported. This allows the proper delete function to be called in cases when placement new is used.
The import library format has been revised to reduce the size of library files. Most files are 50-75 percent smaller.
The linker can now create code to automatically manage delay loading of dynamic-link libraries (DLLs). This can be very useful when your application links to DLLs but doesn't always need them. Simply add:
/DELAYLOAD:<dllname>
to the link command to enable delay loading.
To get a listing of exports, add:
/MAPINFO:EXPORTS
to the linker command line.
Edit and Continue can dramatically increase your productivity by allowing you to make code changes and continue execution without exiting the debugger. While some changes will require a rebuild, in most cases you can merely change the code and continue debugging.
Note Edit and Continue is enabled by default for debug projects created with Visual C++ 6.0. However, if you are importing a project from a previous version of Visual C++, you should follow these instructions.
To enable Edit and Continue, you must change the following settings:
The use of Edit and Continue is largely transparent to the user. Merely do a Step or Go after editing to initiate Edit and Continue. If the code edit is not supported, you will be prompted to rebuild, as usual.
Here is a partial list of operations Edit and Continue does not support:
Variants are automatically displayed in their correct form, so integers are displayed numerically, BSTRs as strings, and so on. Also displayed is the type of the Variant, for example:
VARIANT vt;
vt.vt = VT_I2;
vt.iVal = 123
In this case, vt will be displayed in Locals, Watch and DataTips as {123 VT_I2}. VT_BYREF objects will also be displayed automatically, as will Microsoft Foundation Classes (MFC) COleVariants.
Types based on globally unique identifiers (GUIDS), including IIDs, CLSIDs and REFIIDs, are displayed by name (if found in the registry), or, failing that, in the normal GUID hexadecimal form.
This is a new pseudo-register that displays the Thread Information Block (TIB, also confusingly known as a TEB) for the current thread. Like all registers, it may be prefixed with an @ sign. Information on the content of TIBs may be found in "Under the Hood," Microsoft Systems Journal (MSJ), May 1996.
This is a new pseudo-register called ERR that displays the last error code for the current thread. It retrieves the same value as calling the GetLastError API. Using it in conjunction with the ",hr" modifier produces very useful output. Like all registers it may be prefixed with an @ sign.
You can now display MMX® registers in the Watch and QuickWatch window using the symbols MM0-MM7. MMX registers are 64-bit integer registers and will be displayed on all x86 machines, whether they support the MMX instructions or not. MMX is a registered trademark of Intel Corporation.
Pointers to functions and v-table entries are now displayed symbolically wherever possible, including parameters, instead of just hexadecimal addresses, as in Visual C++ 5.0.
The disassembler output has been improved, particularly with regard to the inclusion of symbols in the output.
The disassembler and callstack windows now undecorate C++ names when they did not do so before, such as when displaying system callstacks from DBG files. For example, a function in rpcrt4.dll previously displayed as the somewhat meaningless:
?MTAInvoke@@YGJPAUtagRPCOLEMESSAGE@@KPAUIRpcStubBuffer@@PAUIRpcChannelBuffer@@PAK@Z
is now displayed as:
MTAInvoke
with a proper argument list.
The following table lists the new symbols for Watch variables in Visual C++ 6.0.
,hr | This displays 32-bit values (HRESULTs or Win32® error codes) in a more meaningful way, such as S_OK, or E_NOTIMPL, or as a system error message such as "File not found." Unfortunately, it is not possible to set up an automatic rule in autoexp.dat. To do this for types such as HRESULT, you have to manually add the ",hr" postfix to symbol names in the Watch window. |
,st | This displays string values either as ANSI or Unicode, depending on the Unicode Strings setting. This is now used in the default autoexp.dat file to display MFC CStrings according to the current setting. |
,mq | This displays memory as quadwords to go with the existing mb, mw, and md formatting symbols. |
,wm | This displays numeric values decoded into the names of windows message numbers (the WM_ constants), so this code
displays wMessage,wm as {WM_CLOSE}. |
,wc | This displays numeric values decoded into window class flags (the WC_ constants). |
This new option allows additional debugging information to be loaded and can be found in the Debug tab in the Options dialog box. Because this option can affect debugger performance during debuggee loading, it defaults to off.
Other debuggers use COFF symbols, but Visual C++ (which uses CodeView-format debug information) does not. However, sometimes it can be useful to read COFF information when no other information is available. For example, the Microsoft Visual Basic® 5.01 development system includes an msvbvm50.dbg file that supplies debug information in COFF format, so enabling this option allows that file to be read. To see what form of debug information is in a .dbg file, use dumpbin –headers and look at the Debug Directory for COFF.
When there is no other debugging information available (for example, Microsoft Windows® 95 operating system files), this option also converts the Export table of each loaded DLL into a symbol. To see what sort of symbols this will add for a particular DLL, use dumpbin –exports.
When your application crashes during a call to a system DLL or someone else's code, how do you determine which DLL was active when the crash occurred? The new Module List in Visual C++ 6.0 displays the address, name, path, and load order for all DLLs used by your program. You can use this dialog box to track down which DLL was active when your application crashed.
Despite the significant changes to the editor, compiler, linker, and debugger in Visual C++ 6.0, migrating projects from previous version of Visual C++ can go smoothly. However, to take full advantage of the new features in Visual C++ 6.0, developers need to understand what the new features are, how they impact existing code, and how to use them. This document makes an attempt at providing that information, but the best education comes from installing Visual C++ 6.0, building samples, and importing code. Use this document and the MSDN Library to make sure you're developing at your best with Visual C++ 6.0.