The Visual C++ .NET 2003 release (code-named Everett) that has been released to MSDN today is perhaps the most significant and exciting release of the product since Visual C++ 4.0! Many changes have been made, resulting in a vastly different product from its predecessor. Why should you care? Well, there are important features that have been added for both unmanaged and managed code developers that illustrate a new and serious commitment by Microsoft to the C++ language. The main enhancements come in four major areas:
Many of Microsoft's core products have been and are built with C++, and the language has been central to Microsoft. Regrettably, Standard C++ has not been. The compiler has lagged behind the standard for most of the 90s, and the impression that many developers have is that Microsoft could care less about the standard. The good news is that with the new compiler, Microsoft has finally taken the standard very seriously, leading to this version being one of the most standards-compliant C++ compilers running today on any platform. This turnaround is nothing short of amazing.
New features include Partial Template Specialization, Partial Ordering, Member Template Definitions, and much more. Andrei Alexandrescu's book Modern C++ Design and his Loki library have been the gold-plate example of using the C++ standard to its full potential. This library has consistently "broken" many compilers and, in particular, Visual C++. Visual C++ .NET fully compiles this library and runs 100% of its tests. Other popular 3rd party libraries like BOOST and BLITZ that use advanced template techniques also now build fully.
Let's look at a simple sample in which Partial Template Specialization is useful. This example is from chapter 3 of Alexandrescu's Modern C++ Design. A TypeList is a C++ tool for manipulating collections of types. It holds two types accessible through its Head and Tail inner elements, and looks like this:
template <class T, class U> struct Typelist { typedef T Head; typedef U Tail; };
One of the operations we might want to perform is, given a TypeList, obtain a compile-time constant that evaluates its length. We can first define a NullType to mark the end of a TypeList:
class NullType {};
Then, the code to compute a Typelist's length is:
template <class TList> struct Length; template <> struct Length<NullType> { enum { value = 0 }; }; template <class T, class U> struct Length< Typelist<T, U> > { enum { value = 1 + Length<U>::value }; };
The first specialization of Length is for NullType. This is a total specialization, or an Explicit Specialization. The second is a partial template specialization, that matches any TypeList<T, U>.
On Visual C++.NET 2002, this code will not compile because of the partial template specialization:
c:\Code\OReilly\LokiTypeList\LokiTypeList.h(9): error C2065: 'T' : undeclared identifier c:\Code\OReilly\LokiTypeList\LokiTypeList.h(9): error C2065: 'Typelist' : undeclared identifier c:\Code\OReilly\LokiTypeList\LokiTypeList.h(9): error C2065: 'U' : undeclared identifier c:\Code\OReilly\LokiTypeList\LokiTypeList.h(9): error C2687: cannot define a nested UDT of a template class out of line c:\Code\OReilly\LokiTypeList\LokiTypeList.h(9): fatal error C1903: unable to recover from previous error(s); stopping compilation
This is a familiar set of errors for C++ developers trying to compile their code that uses partial template specialization. Indeed, this is used throughout the standard C++ library. The good news is that this feature is completely implemented in the new compiler. If we recompile the same code with Visual C++ .NET 2003, all is fine:
Build: 1 succeeded, 0 failed, 0 skipped
Certainly, this is welcome news for the community of unmanaged C+ developers who will now be able to make full use of the C++ Standards as well as third-party libraries.
Microsoft has significantly enhanced optimization performance with two new compiler options, and has made improvements to some enhancements made to the previous version.
The first of these is the
/G7 flag. This flag instructs the
compiler to optimize code for the Intel Pentium 4 and AMD Athlon processors.
The numbers can vary when you recompile your code with
/G7 and I
don't want to quote performance numbers, but its is not unusual to see 5-10%
reduction in execution time for typical programs, and even 10-15% for programs
that contain a lot of floating-point code. Recompiling with this flag will not
produce code that only executes on the Intel Pentium 4 or AMD Athlon, but the
code produced may have a minor performance penalty on older generations of
these processors.
The second performance related option is
/arch:[argument], where
the argument is either
SSE or
SSE2. This option
allows the compiler to make use of the Streaming SIMD Extensions (SSE) and
Streaming SIMD Extensions 2 (
SSE2_) instructions, as well as other
instructions that support SSE or SSE2. Please note that the resulting code
with these flags will only execute on processors that support these options.
Again, performance improvements vary widely, but are in the range of 2-3%.
Finally, several of the optimizations introduced in previous versions of the product were enhanced in Visual C++ .NET 2003. One of these is the ability to eliminate the passing of dead parameters. Dead parameters are those that are not directly referenced in the code.
Buffer overruns are a serious problem for C and C++ programs, since unmanaged
code does not perform array bounds checking or type-safety checking. Of course,
this kind of "close to the metal" programming gives C++ an advantage for
certain classes of applications. The downside is that buffer overruns become
security holes and opportunities for several kinds of security attacks.
Recognizing this problem, Microsoft has added the
/GS compiler
option. A simple example of "vulnerable" code is the following (which sadly is
all too common):
#include <string.h> void flawed(char * str) { char buffer[10]; strcpy(buffer, str); } void main() { flawed("This string is too long"); }
This code, when run, has the wonderful effect of causing an Access Violation
(unless one has the stack check flag
/RTC1, in which case there is
an assert and then the violation) and becomes fodder for a distributed denial-of-service attack.
Without going into the details, the
/GS flag creates a new stack
layout and "catches" many of these types of buffer overruns:
Last, but certainly not least, is the welcome addition of visual designer support for creating Windows Forms in the Managed C++ language. Managed C++ programmers desiring to do this in the previous version of the product were forced to code these forms by hand without the considerable benefits of drag-and-drop placement and layout capabilities.
In the current release, the designer generates the code in the .h file and it looks something like the following:
#pragma once namespace WinFormsDem: /// <summary> /// Required designer variable. /// </summary> System::ComponentModel::Container * components; /// <summary> /// Required method for Designer support - do /// not modify the contents of this method /// with the code editor. /// </summary> void InitializeComponent(void) { this->components = new System::ComponentModel::Container(); this->Size = System::Drawing::Size(300,300); this->Text = S"Form1"; } }; }
As you can see, it looks very much like the C# code that its Form Designer generates. The notable exception is the class declaration, which may look a little funny to those who have not seen Managed C++ syntax:
public __gc class Form1
The
__gc keyword signifies that class
Form1 is a
Managed class, a garbage-collected class, and is thus controlled by the CLR.
Of course, there is much one could do from this point that is outside of the scope of this introductory article. Certainly one can drag and drop controls and all of the things that a C# or VB.NET Windows Form can do. The interesting possibilities come from combining unmanaged and managed code in this type of application. One possible scenario, for example, would be having a Managed C++ Windows Form using IJW and Managed C++ to use Direct/X without incurring the COM Interop penalty.
All in all, this is a very significant release, with features to please virtually all types of C++ developers. Microsoft has long suffered a (deserved) bad reputation for ignoring the ISO/ANSI C++ standards. Those unable to do such work with this compiler before will find welcome relief and support when using Visual C++ .NET 2003. Additionally, code recompiled for the new release will perform better, at least on new processors. Security concerns are addressed with the new option of detecting buffer overruns. Finally, the managed developer will also have welcome functionality in the form of Visual Designer support, enabling a whole new class of applications.
Sam Gentile is a well-known .NET consultant and is currently working with a large firm, using Visual C++ .NET 2003 to develop both unmanaged and managed C++ applications.
Return to ONDotnet.com