Welcome to MSDN Blogs Sign in | Join | Help

Some Notes about Mixed Types

Perhaps the most important reason to use C++ for managed code development is that C++ fully understands managed code and existing C++ code. This positions C++ as the ultimate language for doing managed-native interop programming. It is worth sitting back and remembering just how important that capability is. This allows you as the programmer to take advantage of any library that exists whether it is a native library or a .NET library. At the same time, every work item can be accomplished by using C++ alone. Although the CLR allows language interop between assemblies and to some degree within an assembly, it's still harder than using only one language.

C++ definitely makes interop easier than any other language on the .NET platform, but it still requires effort... and intelligence. The most fundamental thing to understand about .NET interop are differences between memory allocators. These are the two models that must be understood: the CLR garbage collector and traditional native heap memory managers.

Traditional native heap memory managers
Pick up any book on writing a memory manager, and you'll quickly learn how tricky this can be. Nevertheless, when it comes down to the final steps of squeezing out every last bit of performance, it inevitably comes down to writing a custom memory manager. What is most common about traditional memory managers is that the data stored in memory is never moved by the system. That is, from the point the memory is allocated (via new, malloc, or your favorite memory allocation function), the object stored in that memory can only be moved by the program itself. The address range of that memory allocation is given over to the program's control until the program releases that address range (via delete, free, or your favorite memory deallocation function).

Even most garbage collectors follow this same behavior. That is from the point of allocation, a memory range is under the control of the program until the memory range is no longer referenced. At this point, a garbage collector can free the memory. The contents of the data range are never stored elsewhere unless the program itself moves the data.

Performance of memory allocators are measured in several ways, including the time it takes to allocate memory, time it takes to free memory, wasted memory used for padding between allocations, and long term working set. In many cases, I've seen server applications run out of memory – not because there was a memory leak, but because the heap's memory was fragmented. Memory fragmentation means that while enough free memory exists for an allocation, not enough free space is available in a contiguous block of memory addresses.

The CLR garbage collector
The garbage collector that comes with .NET is known as a generational, compacting garbage collector. It's really quite sophisticated for the task of memory management. Every time a garbage collection happens, any free space between objects is compacted out by moving the remaining live objects to the beginning of the heap. This means that free memory is always available in a contiguous chunk in higher address range of the heap, which makes memory allocation super fast.

There are a number of other characteristics that make the .NET garbage collector world class, but they're not relevant to the eventual topic of this discussion. What is relevant is that objects on the garbage collected heap are moved by the system. To maintain consistency in the system, any memory address that referenced the object is also updated by the system. Unfortunately, the system (by which I mean the CLR) only knows about memory references that originated from managed code (managed code is not necessarily equivalent to MSIL). This means that any reference to an object on the garbage collected heap originating from unmanaged code will not be updated, and thus the program could break after the garbage collector runs.

The best way to allow unmanaged code to refer to objects on the garbage collected heap is to prevent the garbage collector from moving the object. The feature that tells the garbage collector to leave an object alone is called pinning. .NET actually provides several ways to pin an object, but there are risks associated with all of them. I'll discuss this more later.

Choosing which memory allocator to use
Choosing whether to use the .NET garbage collector for an object verses using a traditional memory allocator comes down to choosing whether to represent data with a ref class or a native class. All ref classes take memory from the garbage collected heap whenever they are instantiated with the MSIL newobj instruction. All native classes take memory from the native heap, the execution stack, or global memory.

In .NET, there is a category of types called value classes that are optimized for efficient copying. As such, an instance of a value type can occupy memory anywhere on the system with some restrictions. A value type can have a handle as a member (see my discussion of the handle design in C++). Handles have to be visible to the CLR, so a value type that contains a handle cannot be allocated on the native heap.

Now, we're at an interesting point in the discussion... with all these restrictions for what memory allocator a type can be instantiated with, how do we effectively interoperate between the two memory allocators. A typical approach to coding is to make a type a member of another type. Let's try both directions:

      struct NativePoint {
        int x, y;
      };

      ref struct ManagedPoint {
        int x, y;
      };

      class C {
        ManagedPoint mp;
      };

      ref class R {
        NativePoint np;
      };

If you try to compile this program, the Visual C++ 2005 compiler gives the following errors:

t.cpp(5) : error C3265: cannot declare a managed 'mp' in an unmanaged 'C'
may not declare a global or static variable, or a member of a native type that refers to objects in the gc heap

t.cpp(5) : error C3076: 'C::mp' : you cannot embed an instance of a reference type, 'ManagedPoint', in a native type

t.cpp(9) : error C4368: cannot define 'np' as a member of managed 'R': mixed types are not supported

As you can see, the compiler doesn't really like the code above. It doesn't like putting the ManagedPoint in the native class C that it even gives two errors. The third error brings up this notion of a mixed type.

What is a mixed type?
In the language specification, I wrote the following definition for mixed types: "A mixed type is either a native class or ref class that requires object members, either by declaration or by inheritance, to be allocated on both the garbage collected heap and the native heap." When writing a standard, I tend to be terse since every word can end up being interpreted broadly. Here, I'll try to explain the issue informally.

As I said earlier, native classes are always allocated on native heap or on the execution stack, and likewise ref classes are always allocated on the garbage collected heap. When the native class C tried to embed a ref class ManagedPoint, the compiler doesn't have any place to put the object since parts of it have to be on both the native heap and the garbage collected heap. The same is true the other way with class R. Both native class C and ref class R are mixed types.

In the old syntax for writing managed code in C++, the compiler allowed a particular kind of native class to be a member of a ref class. In C++, a class that has no constructor, destructor, or copy semantics is called a POD type, which stands for plain old data. It is equivalent to a C-structure. These types can be safely copied around with memcpy which makes them more suitable to being members of ref classes. Unfortunately, these types are used by unmanaged code frequently, meaning that if the POD is allocated on the garbage collected heap, the garbage collector cannot move that region of memory while unmanaged code is accessing the object. This is accomplished with pinning. While there are several ways to pin an object, the most accessible mechanism is a pinning pointer.

A pinning pointer works by keeping whatever it points to from moving during a garbage collection. For a pinning pointer to be effective, it must be an active local variable in a function somewhere on the call stack. As soon as a function returns, a pinning pointer no longer keeps an object for moving. Given this behavior, the C++ language design team observed this kind of code frequently.

      struct Point {
        int x, y;
      };

      ref struct R {
        Point p;
      };

      void F(Point* ptr);

      Point* G(R^ r) {
        pin_ptr<Point> pinp = &r.p;
        return pinp;
      }

      void K() {
        R^ r = gcnew R;
        F(G(r));
      }

The function G has a terrible mistake – something we call a "GC hole". Using the behavior from Visual C++ 2002 and Visual C++ 2003, Point is a POD so it can be embedded in R. Thus, the memory for the Point data is on the garbage collected heap. G pins the object where the memory for the Point stored, and uses the pinning pointer to get a native pointer to the Point. The coding error is that G returns the native pointer and allows the pinning pointer to become inactive. In effect, G returns a pointer to memory on the garbage collected heap that is not pinned.

This coding mistake was so common that we were compelled to fix it. We tried relentlessly to correct pin_ptr so that this kind of code could not exist. Unfortunately, that requires data flow analysis which is an incomputable problem without further constraints to the language. In the end, there was very little we could to change pin_ptr. It became clear that allowing PODs to be allocated on the garbage collected heap was an idea that could not survive in the new language design.

How mixed types work in the new language design
While all the code examples thus far have compiler errors with Visual C++ 2005, the C++ language design team wants the code to compile. In the beginning of 2003 we spent a few months writing a lengthy design paper that proposed "mixed types" as the solution to the problems mentioned above. The proposal suggested that whenever a mixed type occurred, the compiler would split all the "native parts" and the "managed parts" into two pieces. The "native parts" would be allocated on the native heap and the "managed parts" would be allocated on the garbage collected heap. The two parts would then be connected to each other. All of this splitting would be mostly invisible to the programmer, leaving the compiler to do all the hard work.

We were so intrigued by the possibilities here that we went ahead and designed ways to make it look like any native class could be allocated on the garbage collected heap, or even get ways to have pointers to a ref class. All of this came to be known as the C++ unified type system. The document describing how all this works is a bit over fifty pages, and although my blog postings are generally fairly lengthy, fifty pages is a bit too much even for my tastes. Provided there is interest, I may spend time writing about the unified type system over a series of articles.

Why native native classes, including PODs, should not be on the garbage collected heap
In past releases, PODs could be on the garbage collected heap. When taking their address, you get an interior pointer, which you would then have to pin in order to get an unmanaged pointer. Since mixed types say that native classes are never allocated on the garbage collected heap, we were able to assume that native types never needed to be pinned. This assumption actually goes to the heart of the type system. Consider this common implementation for the swap routine:

      template<typename T>
      void swap(T% t1, T% t2) {
        T temp = t1;
        t1 = t2;
        t2 = temp;
      }

We wanted to make it possible to replace a native reference (&) with a tracking reference (%) without any trouble. After all, they are both pointers underneath the covers and there isn't the same risk of leaking an unpinned reference to unmanaged code. Simply substituting % for & works until a conversion from an N% to an N& is necessary (in the language design, we typically use the capital letter N to represent native classes). Consider this simple code:

      struct C {
        int X;

        C(int value) : X(value) {}
        C(C& c) : X(c.X) {}
        C& operator=(C& c) {
          this->X = c.X;
          return *this;
        }
      };

      void K() {
        C c1(1);
        C c2(2);

        swap(c1, c2);
      }

Like every other native class in existing code, the copy constructor and copy assignment operator use native references for the argument. The call with the swap template could not succeed unless C either provided copy functions that took tracking references or we allowed an N% to convert to an N&. We chose to allow the conversion which was based on the assumption that native classes would never be on the garbage collected heap.

For the most part, all existing STL algorithms can be made to work for managed code by simply replacing & with %. If managed classes exposed iterators, the STL algorithms can work.

Why does Visual C++ 2005 not support mixed types?
Basically, if we had implemented everything written in the unified type system document, we would be calling this release Visual C++ 2010. Really! We designed some pretty advanced and complex stuff. The compiler team figured it would be more useful to release a product sooner that provided the core language and leave out parts that could be worked around with libraries. Just getting the fundamentals done with high quality for Visual C++ 2005 was a daunting task.

Going forward, we will be prioritizing work based on customer feedback and scenarios that are blocked. Just listing out high priority scenarios for upcoming releases could take a while, so I'll once again avoid exploring that topic here. Regarding feedback, we are already getting feedback that allowing PODs as members of ref classes is important. We also received the same feedback from teams inside Microsoft who were trying to program with the new C++ features.

Several times, we tried to narrow the set of features needed to implement some part of mixed types. Ultimately, we ended up getting a very narrow subset of the mixed type proposal that would work. Unfortunately, changes to the compiler in the past half-year needed to be low risk (meaning that changes were certain to not cause a slip to our November 7th delivery date). So far, we have been unable to mitigate the risks involved in implementing even the simplest approach to mixed types.

How to workaround absence of mixed types
Given that Visual C++ 2005 gives errors for mixed types, the next best approach is a library. First, I'll address the case where a library solution has existed for a while. As discussed earlier, the compiler returns errors C3265 and C3076 when a program tries to embed a ref class inside a native class. The gcroot template has been around for a while, which allows a native class to effectively hold onto a handle to ref class. It is used as follows:

      #include <vcclr.h>

      ref struct ManagedPoint {
        int x, y;
      };

      class C {
        gcroot<ManagedPoint^> mp;

      public:
        C() : mp(gcnew ManagedPoint) {}
        ~C() { delete mp; }
      };

The gcroot template doesn't make embedded semantics the default – the ref class object still has to be created with gcnew. It's good enough to be productive though, and there are already further derivations of gcroot included in the libraries with Visual C++, such as auto_gcroot.

So, putting a ref class inside of a native class is solved with gcroot. What about putting a native class in a ref class? Today, there is no library included with Visual C++ that solves this. So... let's make one here.

The gcroot template is a wrapper for the System::Runtime::InteropServices::GCHandle class. That's how data on the native heap can get a reference to an object on the garbage collected heap. Going the other way around is much easier – a pointer will do. Here is the first rewriting of some of earlier code:

      // First attempt at embedding a native class
      // inside a ref class
      struct NativePoint {
        int x, y;
      };

      ref class R {
        NativePoint* np;
      public:
        R() : np(new NativePoint) {}
        ~R() { delete np; }
      };

The code above will certainly work in many cases, but it subject to memory leaks when the class R is cleaned up by the garbage collector. This happens when an instance of R is not cleaned up with a destructor, but is instead lets the garbage collector call the finalizer. To fix that, we'll add a finalizer to the code before:

      // Second attempt at embedding a native class
      // inside a ref class
      struct NativePoint {
        int x, y;
      };

      ref class R {
        NativePoint* np;
      public:
        R() : np(new NativePoint) {}
        ~R() { this->!R(); }
        !R() { delete np; }
      };

Notice that I'm avoiding code duplication by calling R's finalizer from R's destructor. This pattern certainly gets us closer to working around the absence of mixed types. Nevertheless, it's clear that as R holds onto more and more resources, maintaining the destructor and finalizer, especially to be exception safe, will become unwieldy. We also need to consider the very real possibility that a class might be finalized more than once, or finalized when the constructor didn't complete execution. To solve these problems, we're going to put the resource management code in a separate template class:

      // Third attempt at embedding a native class
      // inside a ref class
      template<typename T>
      ref class Embedded {
        T* t;
 
        !Embedded() {
          if (t != nullptr) {
            delete t;
            t = nullptr;
          }
        }
 
        ~Embedded() {
          this->!Embedded();
        }
 
      public:
        Embedded() : t(new T) {}
 
        static T* operator&(Embedded% e) { return e.t; }
        static T* operator->(Embedded% e) { return e.t; }
      };


      struct NativePoint {
        int x, y;
      };

      ref class R {
        Embedded<NativePoint> np;
      };

With the Embedded template, we've finally got a library solution for embedding native classes in a ref class. Of course, the illusion isn't complete given that member access to the embedded native class must use the arrow (->) operator instead of the dot (.) operator. I think we can live with that difference until Visual C++ does introduce some support for mixed types.

You'll note that this template is different from auto pointer like templates in that Embedded completely owns the management of the native class – it never gives up that ownership. The native class is created when the Embedded class is created, and the native class is cleaned up when the Embedded class is cleaned up. Kenny Kerr wrote up some notes about this same subject in which he provided an AutoPtr template where the ownership of the resource is more in control of the programmer using the AutoPtr template. In many ways the AutoPtr template is more flexible for non-POD classes as it allows for use of a non-default constructor. AutoPtr really needs to use a finalizer though (otherwise it's likely to leak resources). J

The last area we have so far left uninvestigated is the ability to embed native arrays in a ref class. Previous versions of Visual C++ allowed this to happen if the element type of the native array was a POD type, fundamental type, or a simple value class (in other words a class that has no handles). A native array is accessed via pointers and even is synonymous with pointers in much of the C++ type system. For all the reasons listed earlier, native arrays can no longer be allocated on the garbage collected heap. This brings up an interesting problem – holding onto a pointer to a native array from a ref class adds an extra level of indirection. In some cases that can impact performance. In fact, some languages introduced the idea of a fixed array just to solve this problem. So, how can C++ get the same behavior given the constraints above?

C++ does let you put fundamental types and simple value classes as members of a value class, and the CLR provides a mechanism for explicitly stating the size of a value class. Putting that together, we have a solution – the inline_array template written by Mark Hall and improved upon by Shaun Miller:

      template<typename T, int size>
      [System::Runtime::CompilerServices::UnsafeValueType]
      [System::Runtime::InteropServices::StructLayout
        (
          System::Runtime::InteropServices::LayoutKind::Explicit,
          Size=(sizeof(T)*size)
        )
      ]
      public value struct inline_array {
      private:
        [System::Runtime::InteropServices::FieldOffset(0)]
        T dummy_item;

      public:
        T% operator[](int index) {
          return *((&dummy_item)+index);
        }

        static operator interior_ptr<T>(inline_array<T,size>% ia) {
          return &ia.dummy_item;
        }
      };

      ref class R {
        inline_array<int, 10> arr;
      };

This of course allows you to really embed an array of fundamental types. Interestingly, the generated MSIL and metadata using the inline_array template is almost exactly the same thing generated by fixed arrays in C#. It's great to see workable library solutions show up before having to extend the language.

To wrap up...
I know this subject requires a lot of knowledge to grok, but it does come up a surprising amount. Most programmers moving code from previous releases of Visual C++ to the new syntax introduced in Visual C++ 2005 are going to see these issues the most. The fastest way to get a POD in a ref class is to use the Embedded template.

We will be listening to customers to determine where to go next. Providing at least some mixed type support seems necessary, but if you have anything to say... please speak up! Sending feedback and suggestions through the MSDN Product Feedback Center is a great way of getting a permanent record of the request (and we really do spend time with feedback sent there). I'm always interested in feedback too, so don't hesitate to send notes my way.

Posted by branbray | 10 Comments

Compiler Switch Changes in Visual C++ 2005

One noticeable change in the coming release of the Visual C++ compiler is the changes to compiler switches. If you use the project system, when the IDE upgrades your project files, many of these changes will be taken care of automatically. However, if you use another build system like nmake, you might have to fix some build issues.

Why did we remove and deprecate some compiler switches?
The Visual C++ compiler has been around a long time. Long enough that many features that were once very useful are no longer needed (optimizations for the 486 probably don't get used that much these days). In some cases, new switches have been added that mean exactly the same thing as an existing switch. Furthermore, some switches existed that are essentially evil and easily led to the program errors.

Much of the investigation here began when we started thinking harder about security issues during the development of Visual C++ 2003. In particular, as soon as potential security issues became a higher priority than backwards compatibility, we had to justify more of the feature set Visual C++ exposed and of course we had to question how much testing we actually did on all the configurations the compiler could exercise. The first change was modest. Visual C++ 2003 deprecated the Gf switch. In that compiler, you would have seen the following warning:

c1xx : warning C4349: /Gf is deprecated and will not be supported in future versions of Visual C++; remove /Gf or use /GF instead

For everyone who does not immediately recall what the Gf switch does, it pooled all the string literals into a writable page of memory. Just the description screams of security issues if not simple reliability issues. The alternative GF switch pools string literals into a read-only page of memory, which will break C programs that mutate string literals. Of course, pooling is a great way to reduce the size of a binary.

Goals behind the switch reduction
The changes to the switch model for Visual C++ 2005 came to be known as switch reduction. It was designed along with another improvement, warning families, that did not make it into this release of Visual C++. Hopefully, warning families will make its way into the next release. But I'll delay discussion of that since we're considering switches right now.

When I started to write the specification for the changes in this area, I listed the following four requirements:

  1. Removing the switch would benefit testing, by reducing the test matrix for the compiler.
  2. Removing the switch would lead to a better user experience.
  3. Changing switches would make multiple versions and platforms have consistent switches.
  4. After considering the above, removing or changing the switch does not place undue burden on users.

I also noted in the specification that changes to switches needed to happen swiftly to avoid prolonged impact on users. Unfortunately, making changes to compiler switches turned out to be harder than I ever imagined. Fixing all of the files in the test harness took the majority of our effort. As a result, all of the work called for in the switch reduction specification wasn't completed.

When it came to improved user experience, the focus was on making it easy for programmers to select the correct defaults for their program or at least to easily understand the interactions switches have with each other. A good example of this are the Os and Ot switches, which mean "favor code space" and "favor code speed" respectively. Neither of these switches do anything unless the Og switch is also given to the compiler. This issue frequently comes up. We can either fix it by warning the user that he probably did something wrong, or we can make the switch model intuitive. Given the overall complexity of just getting a standard C++ program to run correctly, I favor making the compiler more intuitive and making it work by default.

The third requirement addresses the problem with the G3, G4, G5, G6, and G7 compiler switches. In a good attempt to allow application writers to make use of the latest and greatest hardware, nearly every compiler introduced a new better switch to make your code even faster. Unfortunately, compiler switches end up in make files that rarely get revised. It wasn't uncommon to see a make file specify a G4 or G5 switch even though 80486 and Pentium have long been out of mainstream production. The G-series of switches do not prevent programs from running on older hardware, which was a common misconception. Eventually, Visual C++ just ignored the G3, G4, and G5 switches and the program compiled as if G6 had been given to the compiler. I'll say more on what changed with these switches below.

Choosing what to do and getting it done
Throughout this process, I acquired a reputation for not liking switches. A running joke in the Visual C++ product team was to suggest adding a switch to me. To be honest, the reputation wasn't quite deserved. I just challenged the notion of adding a switch with its necessity. To many times, switches were added to the compiler to cover over other problems. In fact, that's par for the industry. Most compilers offer dozens of switches to disable particular optimizations because they break code. That's lazy – the programmer using the compiler has to understand the impact of an optimization and even if he does, he's likely to overlook something. The Visual C++ team has a much higher aspiration to make the compiler useful to every application and systems programmer on Windows, not just geniuses with intimate knowledge of every version of the compiler.

All that said, I started evaluating changes by doing none else than reading the source code for the compiler driver. Through that I came across obsolete, outdated, bizarre, undocumented, and useless switches. I looked at each one asking whether it was necessary for the compiler in the long term and evaluating each switch against the requirements listed above. I actually spent most of my time trying to figure out what each switch did. Even asking developers who work on the compiler, I'd sometimes get several different answers. In a few cases, no one knew what the switch did. If our own team couldn't recall a switch's purpose, it's not hard to believe nearly every programmer using Visual C++ will have the same problem.

Out of this process came the first list of changes to the switch model. Team members were given opportunity to comment along with the Visual C++ MVPs. Through much discussion, the list was narrowed to what we originally implemented. Early usage of the compiler in alphas and the first beta yielded feedback that made us reconsider some of the changes. In several cases we reverted changes due to direct feedback.

Before I go into more detail about some of the specific changes, a primary concern was ease of migration. The most noticeable impact of a change like this is breaking a build. Fortunately, additional warnings about compiler switches usually come from the compiler driver cl.exe instead of the compiler itself (one of c1.dll, c1xx.dll, or c2.dll). This is usually noticeable by the message number, Dxxxx instead of Cxxxx. As a result, the WX switch does not cause a build failure when the driver warns about an unrecognized or deprecated switch.

Now let's look at some of the specific changes...

Optimization switches
As I mentioned earlier, the G-series of switches offered two problem points. First, many customers confused the meaning of these switches, thinking that it was necessary to throw G4 to make the compiler produce code that could still run on a 486 computer. Second, the switches tended to stay in make files well past the intended period of time. In recent versions of Visual C++, the G3, G4, and G5 switches were just ignored.

Since G6 and G7 were the only two that made a difference, the natural question was what the difference between these two were. It turns out there was very little difference, and there were ways to generate code that resulted in great performance on both Pentium III and and all the new processors coming from both Intel and AMD. The switches weren't really necessary. So, we removed them and reduced the testing matrix at the same time.

So far I've taken an x86 focused view of the world. On IA64, there are significant differences between first generation processors and the following generations. IA64 compilers continue to have the G1 and G2 switch because the usability problems haven't shown up on IA64, partly because the market of developers is relatively tiny compared to x86.

On x64 platforms though, we tried at first to avoid generating different code between compilers. In the end, we were compelled to support specific processors. To avoid any confusion, rather than continue with the G-series of switches, the x64 compiler has the favor:AMD64, favor:Pentium4, favor:EM64T, and favor:blend switches. It should now be clear that throwing one of these switches still allows the program to run on other processors, but likely with weaker performance.

Certainly, I would have preferred to see the same switch set on all compilers. That hasn't happened yet, and it hasn't been necessary to drive that idea to the top of my priorities.

Regarding other optimization switches, I did a cognitive walkthrough of the optimization settings shown in the cl /? results. These were some of my thoughts:

  • Ox and O2 are almost identical. They differ only in the fact that O2 also throws GF and Gy. There is almost no reason to avoid throwing these two switches.
  • It is difficult to know that Os and Ot are ineffective.
  • Using Og means that Oy Ob2 and Oi are not thrown, which is usually only useful for debugging purposes – it is not the starting point for a release build, and it's better to use pragmas for this level of control rather than changing the build system.

At the time, I wanted to converge the optimization settings so that multiple switches resulted in the same behavior. This would allow build scripts to continue working without changes, but would both reduce the test matrix and improve default compiler switch selection. This is what I had proposed converging to:

  • Os: now turns on optimization, favoring size (also throws Oy Ob2 GF)
  • Ot: now turns on optimization, favoring speed (also throws Oi Oy Ob2 GF)
  • O1: has exactly the same meaning as Os
  • O2: has exactly the same meaning as Ot
  • Od: disables optimization (also throws Oi- Oy- Ob0 GF-)
  • Ox: has exactly the same meaning as Ot
  • Remove the Og switch

If you're using Beta 2 of Visual C++ 2005, you'll see that this convergence of optimization switches did not happen. That's mostly because we didn't have enough time in the schedule to do so. Despite known usability issues with optimization switches, we don't currently have plans to complete this part of the switch reduction specification in the next release.

Buggy features
It may come as a shock, but some of the features we've released in Visual C++ haven't had the level of quality that we wish it would have. The most notable of all is automatic precompiled header (PCH) files. The YX switch was used to tell the compiler to automatically select and create a PCH file. Developers would use this to speed up a build, but in practice it slowed the build down. Only in a few test cases did it benefit the build time. Given that information, we decided to remove the switch. If you do want to use a PCH, the Yc and Yu switch still exist and, when used correctly, they will dramatically improve build times.

There were a few other undocumented switches left over from experiments that never showed results. We took the opportunity to at least deprecate these switches.

I also listed the Wp64 switch to be deprecated and turned on by default, which doesn't appear to have happened. Overall, the Wp64 switch is no longer necessary. Visual Studio 2005 now includes 64-bit compilers. Compiling code with a 64-bit compiler yields accurate warnings and errors, whereas the Wp64 option only yielded approximations and in many cases had false positives and false negatives.

CLR switches
Visual C++ 2005 introduces at least three new switches for CLR modes. All told, we have the following CLR modes:

  • clr-: This corresponds to not using CLR functionality at all. It is the default.
  • clr: This tells the compiler to enable CLR funcationality, using the new syntax, and to produce a mixed executable image (one that can contain both machine code and MSIL). Object files generated from this mode can be linked with object files compiled with the clr- mode.
  • clr:oldSyntax: This tells the compiler to enable CLR functionality, using the old managed syntax, and to produce a mixed executable image.
  • clr:pure: This tells the compiler to enable CLR functionality and to produce a pure executable image (one that contains only MSIL).
  • clr:safe: This tells the compiler to enable CLR functionality, to produce a pure executable image, and to only allow verifiable source code through the compiler.

I'd say selecting the names for these switches was the hardest part of the process. I heard concerns that "old syntax" could be insulting to geriatric constituents. That was perhaps the highlight of the conversation. The "safe" switch had equally heated debate as concerns would be raised that Standard C++ isn't safe. The choice of "clr:safe" was for consistency with other .NET languages, which typically have an "unsafe" switch. "pure" has similar subjective concerns. Of course, it's too late to change the names now... we'll all have to live with the ones listed above.

Each of these switches override each other, so it's not possible to mix these modes (for example, old syntax and verifiable code cannot be mixed). In previous releases, there was also the clr:noAssembly switch. In reality, that should have been a linker switch since the compiler generates object files, not assemblies. Thus, in Visual C++ 2005, the LN switch replaces clr:noAssembly.

The other CLR switch, clr:initialAppDomain, was originally scheduled to be removed from Visual C++ 2005 since it was there for compatibility with CLR 1.0. It turns out that there are actually useful things one can do with this switch with CLR 2.0. We discovered that late in the feedback cycle, so the switch was left alone. Ideally, it would have been replaced with a switch starting with G since it affects code generation.

The introduction of three new modes does add testing burden as the matrix of test combinations now quadruples. In reality, we needed to focus testing on all the CLR modes to bring it up to the same level of quality as unmanaged code. One of the first things we noticed was that C code compiled with any CLR option made very little sense. C doesn't have namespaces which makes it obviously unable to access any of the .NET Frameworks. Compiling C code with the clr switch was nothing more than an interesting experiment... an experiment that unnecessarily expanded the test matrix. Thus, we made Tc and TC conflict with all of the CLR modes.

Conformance switches and default behavior
After the work in Visual C++ 2003 to support more Standard C++ features, a common complaint was that standard conforming code that should compile and run did not. It almost always came down to a particular switch was not thrown. To that effect, there were semantic differences between strict compliance mode Za and the extensions mode Ze. I'm a strong believer that different modes in a compiler should at most add and remove features from the language – they should never change the semantics of the language. Here are examples of where that principle is violated in Visual C++ 2003:

  • Compiling with and without Zc:wchar_t changes the meaning of the wchar_t type, which impacts overload resolution, name decoration, and binary compatibility.
  • Compiling with and without Zc:forScope changes the meaning of the iterator variable in a for loop.
  • Compiling with GX- verses EHs determines whether stack allocated C++ objects will have their destructors called in the event of an exception.
  • Compiling with and without J changes the meaning of char.
  • Compiling with vd0 instead of vd1 breaks binary compatibility and changes the capabilities of virtual inheritance.

Even when we were almost doing the right thing, as in the case of GR (enable dynamic type info), Visual C++ had the wrong default. In the past, the compiler defaulted to no dynamic type info. The result of all of this is that Visual C++ wasn't standards conformant out of the box. You had to set a handful of compiler switches to get top notch conformance with the C++ standard. Unfortunately, whenever you set all these switches, many libraries no longer compiled.

So, while Visual C++ 2005 hasn't been on the bleeding edge of adding dozens of new standard features, we have been getting more and more libraries to compile cleanly with a mostly conformant mode. Visual C++ 2005 now makes the following switches on by default: GR, Zc:wchar_t, Zc:forScope, fp:precise, and GS. The one switch I wish we had made a defult but we didn't get to was EHsc (enable conformant exception handling). Because some new defaults certainly break code, new switches were added to override them, including GS-, Zc:wchar_t-, and Zc:forScope-. Some of these new switches are deprecated off the bat since you are better off fixing code than just ignoring problems.

At the same time, I have problems with the Za switch. At first, it makes some sense, but very few people actually write the entire program in the restrictive subset that is Standard C++. More often, they want parts of the program like the core engine to be standard compliant. Just as using Wp64 was a bad solution to diagnose portability of code between 32-bit and 64-bit, using Za to diagnose portability between Visual C++ and other compilers is a truly bad model. If you care about portability, you'll compile your code with multiple compilers on a regular basis. The Ze switch turns out to be completely unnecessary because it is the default mode of the compiler and it is not possible to override the Za switch. Thus, the Ze switch is deprecated in Visual C++ 2005. In the long term, I hope that the default mode will be able to compile any code that Za can with exactly the same behavior, thus making Za unnecessary. For ease of diagnosing non-standard extensions, the warning families feature I spoke about earlier is a far better solution than the restrictive Za mode.

Truly evil switches
As I mentioned in the introduction, the Gf switch is particularly bad since it pools string literals into a writable section of memory. C programs are allowed to mutate string literals, so a program that pools two unrelated string literals can have unexpected results when a string is mutated. Visual C++ 2003 deprecated this option, and it is now removed from Visual C++ 2005. C++ isn't affected by this change to the same degree that C is because C++ specifies that string literals shall not be mutated, and thus the compiler always allocates them in read-only memory.

Another switch that just should never be used is H. This switch placed a maximum length on external names, which was achieved by just truncating the names. The result is that multiple entities could have the same name. Even more, undecorating the names in the debugger was impossible. Really, nothing good could come out of using the H compiler switch, so it was deprecated.

Yet another example are the Oa and Ow switches. These switches gave the compiler the freedom to make assumptions about memory aliasing that were often untrue. As a result, the optimizer made an optimization that broke programs in subtle ways. Very few programs could actually work with these options, so they were both removed from Visual C++ 2005.

Switches that had customer feedback
Originally, we deprecated the J switch. The switch changes the meaning of char to mean unsigned char instead of signed char. A program that always specifies signed or unsigned in front of char would never be affected by this compiler switch; however, nearly no program does. System headers should be vigilant and always specify whether char is signed or unsigned in source code. That again is where warning families can provide a solution. Anyways, we deprecated the switch understanding that is probably could never be removed (it can be useful in migrating source code from other operating systems). After a while, the feedback on this one compiler switch became so overwhelming that we're leaving J alone.

Another set of switches that had similar, if not thundering feedback, are the vd switches. These control the binary layout for objects to enable virtual inheritance. I personally find these switches quite distasteful because it prevents code from interoperating, but we had feedback that a particular virtual inheritance scenario didn't work. The only solution given the binary format we had was to introduce the vd2 compiler switch. So, in my opinion, the situation got even worse, but I don't see a better solution. C'est la vie.

Switches that had replacements
In several cases, switches have been replaced by a group of switches. A good example is the GX switch which ended up being replaced by EHsc when all the EH switches were added. GX stuck around for a while, but it is finally deprecated in Visual C++ 2005.

Another case of this happening are the floating-point switches. In the past, Visual C++ used the Op switch to limit the freedom the optimizer had with floating-point code generation. Now that Visual C++ 2005 has much more granular control over floating-point with the fp:precise, fp:strict, and fp:fast switches, Op was no longer necessary. Thus it was removed from Visual C++ 2005.

Switches that were obsolete
Some switches have just been around so long that they cannot possibly be useful anymore. One such switch is the G3 switch, as favoring 386 processors with the latest version of Visual C++ isn't going to happen. Another example is the QIfdiv switch... processors that don't have the Pentium division bug have long been dominant in the market. Not to mention, the GM switch is no longer necessary, since enabling MMX instructions in the compiler is no longer useful these days. A long forgotten switch from the Windows perspective was GD which enabled specific optimizations for DLLs. It turns out those optimizations weren't limited to DLLs, and the switch has been unnecessary for years.

Bizarre switches
Sometimes problems are over-designed and under-implemented. One example of this is the nologo- switch. It's meant to force the product banner to show. Different versions of the product ignore it though.

Some other bizarre switches are the TO and To switches, which tell the compiler to consider unknown files as object files. It would be better to pass these files to the linker rather than the compiler, especially if compiling with the c switch. These are deprecated in Visual C++ 2005.

Another switch that is both bizarre and wrong is one that we should have gotten rid of years ago. The switch remained undocumented for a reason – only "bozos" would use it. In fact the name of the switch meant bozo alignment, which kept the alignment of local variables the same as if it had been declared in a class; basically preventing the optimizer from reordering local variables. Fortunately, we were able to correct all the code that used bozo alignment and removed the switch from Visual C++ 2005.

The single-threaded CRT
Reducing the test matrix is really important to Visual C++. In the past, it has taken more than six weeks to complete a full test pass. In Visual C++ 2005, we haven't really made huge cuts to functionality (i.e. the test matrix hasn't gotten any smaller), but we've made a few attempts. One such attempt is removing the single-threaded CRT. As a result, the ML and MLd switches needed to be removed.

The output of cl /?
Mostly command-line users would be impacted by the results of cl /?, but they're more likely to use that to guide switch selection. Many of the changes I wanted to see happen in the help results didn't actually happen. It's too bad, but I've been busy driving the language design effort. First, I wanted to make the results shorter and leave a more complete listing for the official documentation. That would at least leave the really necessary switches (and better defaults) for command-line users.

Second, I wanted to fix some of the text. If you read the description of the GA switch, it says "optimize for Windows Application". Who wouldn't want to do that? After all, Visual C++ only targets Windows. Well, it actually impacts thread-local storage and the switch is safe for EXEs and not DLLs. I would have replaced the description with something like "generate TLS accesses for EXEs".

In the long term, many people have suggested having more in depth help available at the command-line. Now that Visual C++ has support for localizable text on the command-line, that's a more likely reality. Much will depend on the feedback customers gives us over the coming years.

Conclusion
I started writing this because the first comment I got on my last post asked about the G5, G6, and G7 switches. I certainly ended up writing much more, and the sad part is that I could write way more than I already have. The experience of investigating compiler modes and implementing the changes that actually did happen was a huge learning experience for me. I'm still learning about customer experiences due to changes like this. As I learn more and the rest of the Visual C++ team learns more, we'll make even better design decisions in upcoming releases. So, if you do have feedback, send it my way. J

Posted by branbray | 8 Comments

Four years and still going

It has been a while since I have written here. Looking at the last time I wrote an article of significance, I mentioned that I had insomnia. Indeed, I wrote everything between midnight and sunrise. Fortunately for me, I haven't had insomnia for the last year and half. This blog has suffered as a result.

So, why am I writing now? Well, today marks my fourth year anniversary of working at Microsoft as a full-time employee. It is a great moment for reflection, evaluating accomplishments, and identifying missed opportunities. No doubt, going from knowing almost nothing about C++ three-and-a-half years ago to writing the entire specification for all the new language features in Visual C++ 2005 is a huge accomplishment. It is such a monumental task that I wonder if it will ever end.

This past week has been a time of nostalgia. Reviewing my experiences at Microsoft as an intern and now, I am filled with hope and excitement. When I worked as an intern in the Outlook team, it seemed as if so many great things were about to happen in the software industry. Sure enough, great things are happening... they're just taking an incredibly long time. After four years, I've gained much more appreciation for the amount of effort it takes to change the world. I expect to take all the lessons I learned over the past four years and apply them to making an even greater difference in the next four.

Beyond the reminiscing, I regret not having said anything here for so long. It is not because I have a shortage of things to discuss. To all those who asked me to write again (there are too many to list here), I am making a commitment towards spending more of my non-insomnia induced waking hours to sharing what I know. In no particular order, I am going to write about the following:

  • Design considerations for arrays
  • How to write an inline array in a ref class
  • Design considerations for overriding
  • Design considerations for for each
  • Design considerations for tracking references (and non-const binding to temporaries)
  • Design considerations for strings
  • Design considerations for mixed types
  • Design considerations for class definitions
  • Comparing accessibility with visibility
  • Comparing hidebyname with hidebysig rules in C++
  • Design considerations for keywords
  • Design considerations for initonly and literal
  • Design considerations for delegates
  • Design considerations for properties and events
  • How to write a non-trivial event
  • Design considerations for conversions
  • Design considerations for safe cast and verifiability
  • Design considerations for delegating constructors
  • Design considerations for destructors and finalizers
  • Design considerations for boxing
  • Design considerations for enums
  • Design considerations for attributes
  • Design considerations for generics
  • XML documentation comments
  • Why we removed compiler switches in Visual C++ 2005
  • Thoughts about IntelliSense
  • Unicode support in the compiler
  • Compiler support for traits
  • Thoughts about reaching 100% conformance with the ISO C++ Standard
  • Discussion of some of the C++ breaking changes done for conformance
  • Design considerations for equality
  • Design considerations for Boolean conversions
  • Variadic macros
  • Managed calling conventions
  • How /clr:pure evolved
  • __declspec(process) variables in pure
  • [STAThread] and the main function
  • What is <MarshalCopy> and <MarshalDestroy>?
  • Design considerations for const and volatile in ref and value classes
  • Thoughts about Pre-JIT compilation verses JIT compilation
  • Thoughts about concurrent programming

If there are topics missing from this list or a particular topic should take priority, please comment to let me know. To a large degree, the list above represents subjects that I have become expert on over the last three years. With four years past, I am looking at the challenges coming in the upcoming years. I'm certain unexpected excitement and hard work lies ahead.

Posted by branbray | 4 Comments

Templates and Generics

Insomnia and being a workaholic is an interesting combination. It is amazing how much work can be accomplished in the eight hours before everyone else comes to work. J

Anyways, I spent some time working on specifying generics in C++ yesterday so I figured I'd write about that today. Perhaps the most important message regarding generics is that they are not templates. That is evident in the C++ language design as it supports both generics and templates. At the PDC, I heard comments such as "generics are templates done right". This, sadly, is a misinformed opinion that too many people share.

Generics and templates have a minor overlap in functionality. They both make it possible to create parameterized types which make it possible to create type safe collections. That's where the similarity stops. First, let's look at some features templates allow and how they are interpreted. I'll assume that you know the syntax for templates.

  • Templates are instantiated at compile-time with the source code.
  • Templates are type safe.
  • Templates allow user-defined specialization.
  • Templates allow non-type parameters.
  • Templates use "lazy structural constraints".
  • Templates support mix-ins.

Templates are instantiated at compile-time. This is huge. Anyone who really wants to understand the limitations of either generics or templates needs to know this. This means that the same template instantiated in two different assemblies actually have different types. The CLR includes the strong-name of the assembly in the type, and thus [A]vector<int> is different from [B]vector<int> even if they were instantiated from the same template. A template is always emitted to an assembly after it has been specialized. So, really a template disappears after compilation. It is not possible to instantiate a template from another assembly. Rather, to instantiate a template, the programmer needs the source code for the template.

Templates are type safe. Templates were designed to replace what many people were using macros for. Templates are fully type checked by the compiler. In no way is there any textual substitution or macro-like behavior in templates. Templates are indeed verifiable as long as the implementation of the template does not use unverifiable features.

Templates allow user-defined specialization. First, let me explain the difference between specializations and instantiations. Consider the following code:

      Collection<int> a;
      Collection<int> b;
      Collection<double> c;
      Collection<X> d;

Here, there are four instantiations of a template but only three specializations. The first two variables share the same specialization. When defining a template, every template has a "primary template". This is the most general template that the compiler will use unless there is a better explicit specialization or partial specialization of the template. The usefulness of user-defined specializations cannot be understated – it allows the programmer to choose a different implementation for different template arguments. For instance, if templates were used in a math library, this allows the programmer to create different implementations for integer and floating-point calculations.

Without user-defined specializations, the compiler creates all specializations from the primary template. These specializations can still generate fairly different code from each other. For instance, one specialization may inline all function calls involving template parameters. Another specialization may not inline the same function calls.

Templates allow non-type parameters. Non-type parameter like integers or template template parameters allow templates to have significant expressive power. Constant values in specializations are known by the optimizer and therefore can be passed into the code via a number of data flow analyses such as constant propagation and copy propagation. The resulting code is far more efficient than one that must rely on accessing variables.

The combination of specialization and non-type parameters have actually enabled an entire programming paradigm in C++ known as template meta-programming. While it is entirely possible to go overboard with the capabilities templates afford, there are numerous useful techniques.

Templates use "lazy structural constraints". What happens when a template relies on a function and it's not there? When writing the template, the developer can call member functions on the template parameter, use operators, or call functions that use the template parameter. The definition of the template is kept by the compiler until later needed for a specialization. When the compiler creates a specialization, if a function call or an operator has no meaning for a particular parameter, then a compile-time error occurs. In short, the constraint for a template parameter is that it supports a particular operation (i.e. it has a function with a suitable overload or it has a valid operator). Template constraints are enforced at specialization rather than at definition.

I use the phrase "lazy structural constraint" with much liberty. The notion I am trying to get across is that the constraint is enforced lazily because compiler diagnostics appear at specialization. They are structural constraints because they require some kind of support from a type parameter that is not necessarily dependent on a subtype relationship.

Templates support mix-ins. A class template can inherit from a type parameter. This enables a number of programming patterns, such as mix-ins and policy based programming. Generics do not support directly inheriting from a type parameter.

Now, with that brief summary of templates out of the way, let's turn to a brief summary of generics.

  • Generics are instantiated at run-time by the CLR.
  • Generics are also type safe.
  • Generics are cross-language.
  • Generics do not allow user-defined specialization.
  • Generics do not allow non-type parameters.
  • Generics use subtype constraints.

Generics are instantiated at run-time by the CLR. Unlike templates, a generic defined in source code is emitted into MSIL as a generic. The compiler does not specialize generics. A generic type thus has one assembly to which it belongs. A programmer who wants to create an instantiation of a generic must identify which assembly the generic comes from (either via importing metadata or using the current assembly). When the runtime specializes a generic, it creates one specialization for all ref classes. Each value type will have its own specialization.

Generics are also type safe. Generics were designed to be verifiable (meaning that the MSIL could be proved type safe). Like templates, generics are only unverifiable if they use unverifiable features.

Generics are cross-language. By far the biggest advantage to templates is that producers of cross-language frameworks need to use generics instead of templates. While generics are not Common Language Specification (CLS) compliant now, it is expected that they will be at some point in the future.

Generics do not allow user-defined specialization. As generics were designed as a runtime service, the designers of generics felt that specialization was tied too much to the semantics of a particular language. Thus, when writing a generic, it is only possible to write it once. This is like only being allowed to write a primary template.

Generics do not allow non-type parameters. The primary design goal for generics was creating parameterized collections. Most collections do not require non-type parameters, and thus the designers of generics did not include this feature.

Generics use subtype constraints. This is the big one. It is the mechanism by which generics are implemented on the CLR. First, look at the following code:

      generic<typename T>
      ref class R {
        void f(T t) {
          t->g();
        }
      };

How do we know that the call to g in the function f is valid? With templates, we'd check at specialization. With generics, it's up to the runtime to determine that this is valid. In order to verify that a generic class is valid, the runtime needs more information. Thus, we must fix the above definition as follows:

      generic<typename T>
      where T : IG
      ref class R {
        void f(T t) {
          t->g();
        }
      };

With generics, overload resolution is done at the point of definition. Thus, the call to g is done by looking for the name g in the constraints for T. As there is only one constraint, g must be a member of IG. The g function is called through the interface rather than directly on the variable. Consider the following example:

      interface class IMethod {
        void f();
      };
 
      ref struct R : IMethod {
        virtual void g() = IMethod::f {
          System::Console::WriteLine("R::g");
        }
 
        void f() {
          System::Console::WriteLine("R::f");
        }
      };
 
      generic<typename X>
      where X : IMethod
      void G(X x) {
        x->f();
      }
 
      template<typename X>
      void T(X x) {
        x->f();
      }
 
      int main() {
        R^ r = gcnew R;

        G(r);
        T(r);
      }

With generics, the call to f is done through the interface IMethod. With templates, the call to f is done directly on the class R. Thus, the output of this program is:

      R::g
      R::f

Explicit overrides (used for explicit interface implementation in other languages) are not the only way this difference could be surfaced. Function overloading will change too. Overloads within a generic only consider the constraints as possible arguments, whereas with templates the compiler waits until specialization so it only needs to do overload resolution with the real type.

An unfortunate consequence of subtype constraints with interfaces is that not all useful functions in a class can be contractually guaranteed through an interface. An interface only demands virtual functions. So non-virtual functions, static functions, static conversion functions, static operators, and constructors cannot be used on a generic type parameter. As the CLS requires operators and conversions to be static member functions, generics cannot make use of operator overloading on generic type parameters. This means that a type used in a sorted collection needs to implement a specific interface rather than simply provide the less-than operator. That is a significant drawback if you're used to templates, and this is why the Whidbey version of the frameworks is updating all the built-in types to implement IComparable<T>.

Now with all of that background, here is a comparison of templates and generics:

  Generics Templates
Constraint mechanism Subtype constraints Lazy structural constraints
Allows explicit specialization No Yes
Allows partial specialization No Yes
Type identity of specialization Globally unique Unique to each assembly
Cross language facility Yes No
Allowed parameters Ref class, value class only All types and non-type
Name lookup and binding At definition, to constraints At specialization, to type

Certainly, there will be evangelists for either option. The best option for C++ is to support both mechanisms. Having both templates and generics satisfies anyone who believes one is better than the other. Of course, any pragmatic programmer will realize that having both gives the programmer the ability to use the right feature to solve the problem at hand. Both generics and templates have shortcomings, but using both features together actually yields an even more expressive language.

A very compelling scenario is using templates to create highly efficient data structures, but exposing that type at the assembly boundary through a generic interface. This is similar to a factory pattern that uses private types that inherit from public interfaces. Using this pattern, a specialized C++ collection class can take advantage of frameworks APIs that use the interface and allows other languages to make use of the type through the interface.

This technique is exactly how the STL.NET collection library will be implemented. The collections will be C++ templates that employ the STL design of iterators and separate algorithms. The collections will implement generic interfaces such as IList<T>. I think that the potential for combining templates and generics is great, and we're just starting to scratch the surface. Much of uses for combining templates and generics were driven by Anson Tsao, Martyn Lovell, and Eric Niebler while they were investigating STL.NET.

Hopefully, this was a lucid (after all I am not sleeping) explanation of the fundamental differences between generics and templates. As both features are significant, I very well could write a dozen more pages. As I did with handles, I will later spend time writing about the design rationale behind both generics and templates.

Posted by branbray | 22 Comments

Behind the Design: Handles

In this writing, I plan to discuss the history and rationale of handles. This is perhaps the most noticeable addition to C++. I have heard many questions about handles. Why does C++ need handles? Why are they named handles? Why did you use the hat to declare them? And much more. The design team spent quite a bit of time getting handles right.

First, it is most useful to know what handles are. I will provide a short summary. C++ has the notion of declarators, which are ways to build up types by adding symbols. The two declarators in standard C++ are pointers (denoted with the asterisk) and references (denoted with the ampersand). In addition to the previous declarators, C++ adds handles (denoted with the caret). The caret symbol (^) is often referred to as hat, just as the asterisk is referred to as star.

A handle refers to an instance of an object that is garbage collected (note isn’t exactly true, we’ll clear that up later). How does an object become garbage collected? There are two ways this is done: (1) boxing can copy a value type and put it on the GC heap, or (2) C++ introduces the gcnew operator that creates a new instance of any type on the GC heap. For example:

      Button^ b = gcnew Button();

The new and gcnew operator are similar. Both allocate memory and invoke the constructor of the object. Whereas new allocates memory from a native heap and returns a pointer, gcnew will allocate memory from the GC heap and return a handle. A boxed value type is easy to recognize, as the type is simply a handle to value type. For example:

      int m = 42;             // integer on the stack
      int* n = new int(42);   // integer on a native heap
      int^ o = gcnew int(42); // boxed integer on the GC heap

There are other ways to create boxed value types, but I will leave that for another time when I discuss the implementation of boxed value types.

Why is it important to distinguish whether an instance of an object is on the GC heap or a native heap? The GC algorithm implemented by the CLR is a generational compacting garbage collector. This means that the memory location of the object can change upon each garbage collection. This does not happen on the native heap. A pointer refers to an instance in memory that never moves. The garbage collector ensures that a handle always points to the right instance.

To access a member of an instance referred to by a handle, use the arrow (->) operator. For example:

      Object^ o = f();
      o->ToString();

Now, at this point someone reading this might think that handles are awfully similar to pointers. Such an observation may lead to questions as to why introducing handles to the language was even necessary. To understand this, it does help to look back at the Managed Extensions syntax that shipped with Visual C++ 2002. Much of the language redesign has used the experience of implementing that language and user feedback to evolve the C++ language. Let’s look at some of the problems with what Managed Extensions tried to do.

Before we do that, here is a quick summary. In Managed Extensions, there were three kinds of pointers. The first was a native pointer (also known as a __nogc pointer) which is a traditional meaning of pointer. It points to data in memory that will not move. Another kind of pointer was a whole object pointer (known as a __gc pointer). These pointed to instances of __gc classes. The third kind of pointer was an interior pointer (also known as a __gc pointer), and these could point anywhere and in particular inside objects on the GC heap. Managed Extensions also had a feature known as defaulting rules, which allowed the compiler to choose the most logical meaning for a pointer. Consider this example:

      // System::String is a __gc class
      System::String * s;        // String __gc * (whole object pointer)
      System::String __nogc * q; // ERROR – ill-formed type
      System::String __gc * r;   // String __gc * (whole object pointer)
      
      // int is a __value class (Int32 is mostly the same as int)
      int * i;                   // int __nogc *
      int __nogc * j;            // int __nogc *
      int __gc * k;              // int __gc * (interior pointer)
      System::Int32 * l;         // int __gc * (interior pointer)
      System::Int32 __nogc m;    // int __nogc *
      System::Int32 __gc * n;    // int __gc * (interior pointer)
      
      // std::string is a __nogc class
      std::string * v;           // string __nogc *
      std::string __nogc * v;    // string __nogc *
      std::string __gc * v;      // ERROR – ill-formed type

The defaulting rules were introduced to make it easier to write code. As seen above, a pointer to a __gc class can only be a __gc pointer, and pointer to a __nogc class can only be a __nogc pointer. The only place where the defaulting rules introduced difficulty was with value classes. In every regard, int and System::Int32 are the same type except that Int32 defaults to having __gc qualification and int defaults to __nogc qualification. Because __gc qualification can be added but not taken away (much like const and volatile), trying to pass a __gc pointer to a function expecting a __nogc pointer resulted in a compile-time error. It was with this that we first saw users struggling. There are two ways to resolve this compile-time error: (1) pin the __gc pointer and convert it to a __nogc pointer, or (2) change the function to accept a __gc pointer. It was the latter option that many people chose, and they did so by placing __gc everywhere in the code until the program compiled. In particular, when dealing with a sequence of pointers (such as __gc pointer to a __nogc pointer), it became clear that most people did not understand how a pointer acquired __gc qualification in the first place.

One advantage that the defaulting rules allowed was function templates could be agnostic to __gc qualification. For the most part, this is very useful; however, recall though that the garbage collector can move memory pointed at by a __gc pointer. After each garbage collection, the value in a __gc pointer could be different. Code that comparing less-than or greater-than of two __gc pointers could return different results before and after garbage collection. Such behavior is subtle, and can easily lead to fragile code.

Also, for the code reviewer, the defaulting rules required knowledge of what kind of type was being pointed to. If the type was a __gc class or a __value class, the code reviewer would like to look for unwanted pointer tricks like conversion to int and back.

Of course, the most significant drawback of the Managed Extensions pointer qualification was the inability to overload operators on __gc classes. The Base Class Library defines a number of useful overloaded operators and C++ users clearly wanted to use this functionality with the natural operator syntax. Pointers, however, already have operators defined on them (such as equality, less-than, dereference, and arrow). While it is conceivable that overloading some operators on __gc pointers could be done, it was impossible to do so cleanly.

With all that out of the way, the design team felt very strongly that a simpler design was needed to lower the intellectual burden of using the CLR GC heap. Handles solved the problem very nicely. They are closest to the whole object pointer from Managed Extensions. Because handles were freed from the compatibility of pointers, they were designed to afford the programmer all the advantages of pointers while providing first-class support for CLR features. In fact, handles have opened new possibilities in the language. This is a sign of good language design.

First, handles have the ability to overload operators. For instance, it is possible to write the following:

      X^ operator+(X^ xl, X^ xr);
      X^ x1;
      X^ x2;
      ...
      X^ x3 = x1 + x2;  // calls operator+(X^, X^)

Making operators in the new language features work well with the CLR has mostly been relaxing rules and making operators more flexible. The operator overloading design in C++ was already quite solid. At a later time, I will talk about how operators have changed.

Another useful outcome of handles is that C++ can take advantage of conversion functions in the Base Class Library. If a class referred to be a handle contains a user-defined conversion, the compiler will now be able to find it.

During the design of handles, the design team has worked towards offering the conveniences of pointers without the pitfalls. For example, using a pointer as a Boolean expression is a useful way to guard a member access. For example:

      Y^ y = g();
      if (y) y->Execute();

This is actually a very tricky thing to get right. In C++, bool is an integral type that can convert to an int via a standard conversion. These standard conversions happen all the time. Clearly, we wanted to avoid allowing every handle converting to integers. It would make it difficult to diagnose improper arguments when many overloads to a function exist. Also relying on a conversion function to exist in an ultimate base class does not work, as System::Object is only a base class for all ref class and value class types (it is not a base for native class types). The design team solved this problem by introducing a conversion function to a special Boolean type as a special member function. C++ has a number of special member functions already. This is yet another subject that I will write about later.

Handles do not have the same meaning as pointers. They do not have built-in less-than, greater-than, increment, or decrement operators. It is not possible to reinterpret_cast a handle to an int and then back to a handle. In every regard, handles are type safe. One of the design goals for the new language was to make writing verifiable code easier. That is, code should be verifiable the first time the code was written. A program that makes use of pointers is nearly always unverifiable. The list of rules for writing verifiable code is short, and among the rules is to use handles instead of pointers.

One nice part of this new design is that defaulting rules are not necessary. In C++, a pointer always refers to memory that will not move. In large part, the defaulting rules are no longer necessary because of gcnew. In the past, new behaved differently on int and Int32. Now, new and gcnew behave exactly the same way for both int and Int32. In fact, int and Int32 are exactly the same in C++. Another useful outcome of gcnew is that the MFC debug macros do not conflict with it, which will make it easier to use CLR features in existing MFC programs.

Perhaps one of the most exciting prospects of handles and the gcnew operator is that it was possible for the design team to lift the restriction that native classes could not be garbage collected. There is a significant amount of machinery to make this work, preserve existing C++ semantics, and implement a robust solution. This is part of the feature set known as the "unified type system" of which I will have to spend much time writing about. In short, the design team is making this work:

      std::vector<int>^ vec = gcnew std::vector<int>;

This particular feature (creating handles to native class types) unfortunately will not be part of the Whidbey feature set. As with most software engineering projects, we had to make cut-off decisions so we could deliver a solid compiler earlier.

If you’re interested in learning the manner in which the CLR implements handles, look for discussion of "object references" in the CLI standard and CLR documentation.

Stan Lippman deserves the credit for looking at a new declarator. When he first started working on revising the language, he was working on the notion of a rebindable reference. He first used the % symbol as the declarator. Jeff Peil, who had also come to the conclusion that a new declarator was needed, pointed out that % was not the best choice due to C++ digraphs. When the sequence of characters, %>, is seen by the C++ lexer, it is replaced with a closing curly brace, }. If handles were to be used as template arguments (which they definitely are designed to be widely used in that regard), the digraph behavior of the C++ Standard was undesirable. Of the remaining unused symbols, the caret was the best. Nostalgic memories of Pascal pointers are shared amongst all of us on the design team. J

A curious result of the choice to use the caret is another Standard C++ feature, alternative tokens. Wherever ^ is used, it is perfectly suitable to also use the keyword, xor. For example, the following is legal:

      Button xor b = gcnew Button;

As a note to Visual C++ users, both digraphs and alternative tokens are available only when compiling with the /Za switch. The /Za switch conflicts with the /clr switch. As standards conforming behavior on the CLR is still a goal for Visual C++, we do have a strategy for finishing standards conformance features and making them the default. At some point I can write about that too.

As for the unification feature, Herb Sutter is the one to credit for driving that work. Although much of the details were figured out by all five of us on the design team, he sold this to partners, managers, and most importantly C++ developers. I think he helped push the design team to figure out all the possibilities handles enabled.

I am leaving out discussion of getting handles from an lvalue (such as address-of with & returns a pointer from an lvalue). I will discuss that after more discussion of the unified type system and deterministic finalization.

Mark Hall is the one to credit for the gcnew operator. After doing most of the design work for Managed Extensions, he has been the most qualified to recognize ways to avoid the same issues.

Lastly, why are they named "handles"? At the beginning of the language design, they were called either managed pointers or whole object pointers. At times they were also called GC pointers and tracking pointers. The problem with this is that they are not pointers. Any adjective applied to pointers misses the point that they are not a modification to the semantics of pointers, but instead they are an entirely different abstraction. We noticed that discussion tended to confuse whether the context was referring to native pointers or this new declarator. (During the course of conversation, writing, or dialog, there is a tendency to drop adjectives as more context is built up). The challenge was left to figure out a better term. Brad Van Ee mentioned "handle" in a hallway conversation. That has been the term to stick.

Throughout this and past writings, I have been promising to write about a number of other topics. If anyone is more interested to hear about one subject before another, give me feedback either via comments or via email. Hopefully, this has been interesting and I am happy to answer questions as they come up.

Posted by branbray | 8 Comments

Table-pounding Evangelism of Visual C++

Today has been a long day. The language design team is getting ready to release a draft of the language specification the the ECMA technical group. The TG5 meetings for ECMA start in just under a month. It’s very exciting, and the countdown is leading to a lot of last minute work. Look forward to seeing the early draft of the language specification on the Visual C++ dev center soon.

Anyways, someone forwarded a link around to an InfoWorld article talking about Visual C++. His takeaways from my presentation resonated with a number of other people I met at PDC. There is so much to look forward to from Whidbey, I’m just glad that the Visual C++ message is memorable enough weeks after PDC. We really do love smart developers, and we’re working hard to support them.

For tomorrow, I am working on a history and rationale for the design of handles in the language. A handle is a new declarator we are adding, the caret (^), which implies garbage collection. It is the reason the slogan on the bright yellow shirt passed out at the PDC is:

                   Visual
      Can you handle ^ C++?
Posted by branbray | 2 Comments

Security Improvements to the Whidbey Compiler

I’ve been away from an Internet connection over the last few days. After a conference in the Netherlands, I visited my sister in Germany. She’s stationed at Spangdahlem Air Base, which happened to be where the Air Force started using Xbox Live. The German countryside is amazing. I spent most of my time on the air base, but had time for a trip to Aachen.

Anyways, I was invited to speak about some Visual C++ security features at the Netherlands Unix Users Group’s autumn conference. When I’m not working on language design, one of the things I like doing is looking at security problems and doing something about them. When I started in Visual C++, Louis Lafreniere and Phil Lucido had created a feature known as “security checks” (commonly referenced as the “/GS switch”). This debuted in the Visual C++ 2002 release. I wrote an article that described how the feature worked in that release.

At the end of 2001, I had a number of discussions with Louis and Phil about how we could improve security checks. As buffer overruns were found, we saw that it was often possible to circumvent the security checks architecture. There were a few cases where the VC 2002 implementation would have prevented arbitrary code from running, but it was clearly possible to do better in general. The discussion between Louis, Phil, and I led to a number of ideas, some of which were introduced to Visual C++ 2003. The main thing that VC 2003 did was sort the local variables so that buffers were allocated in memory addresses higher than other local variables. This prevents local variables from being overwritten by a buffer overrun, thus avoiding attacks like pointer subterfuge and v-table hijacking.

Our discussions also showed that the security checks architecture was unable to prevent attacks that exploited exception handling (such as Code Red). This is because a security check that determines whether a cookie changed happens at the end of a function call in the function epilog. Exceptions allow a program to choose control flow that avoids returning from a function. What makes exception handling exploitable is that exception information is placed on the stack (this is done for historical reasons and performance). I’ll spend more time talking about exception handling later (as it will be useful for understanding /EHs and /EHa). If a buffer overrun is able to overwrite exception handling information on the stack, the EH info can be somewhere earlier in the call stack. As many system libraries make use of exception handling, nearly every program will have some exception records in the call stack.

One thing that makes exceptions unique on Windows is that the operating system provides the infrastructure to make exceptions work. For us to make code resilient to attacks against exception handling we needed support from the operating system. Bryan Tuttle, a build engineer in Windows, suggested creating tables of exception thunks that Windows could use to validate the EH record. This suggestion was developed into the feature known as “Safe Exceptions”. Many people were involved to make this work, including Richard Shupak, Dan Spalding, Louis Lafreniere, Phil Lucido, and Bryan Tuttle. This feature debuted with the improvements to /GS in VC 2003. The operating system infrastructure for safe exceptions was introduced in Windows Server 2003.

The VC 2003 release had a short development cycle, so not all of our ideas to improve /GS were implemented. The Whidbey product cycle gave us the opportunity to do more. The biggest improvement to /GS is the protection for vulnerable function parameters. To understand this, it’s helpful to see what the stack layout looks like before this change. From high memory to low memory, this is what shows up in a function activation record:

      Function arguments
      Return address
      Frame pointer
      Cookie
      EH record
      Local buffers
      Local variables
      Callee save registers

If a buffer overrun occurs, it is possible to overwrite the EH record, the cookie, the frame pointer, the return address, the function arguments, and function activation records earlier in the call stack. The EH record is protected by safe exceptions, the cookie can only be exploited by using a value that matches the value in the __security_cookie variable, the frame pointer is only useful after the function returns, and the return address is only exploitable at the function return after the security check has already taken place. Thus, during the execution of the function these parts of the function activation record are not exploitable; however, the function arguments are used by the code in the function. VC 2003 did nothing to protect the function arguments.

The Whidbey compiler will do something to address this by identifying vulnerable arguments and copying those arguments to memory addresses lower than the local buffers. This is done in the function epilog. The code of the function then makes use of the copy of the function argument rather than the original argument. We often refer to this as parameter shadowing. This yields the following stack layout:

      Function arguments
      Return address
      Frame pointer
      Cookie
      EH record
      Local buffers
      Local variables and copies of vulnerable parameters
      Callee save registers

Why only copy some parameters? Making copies of parameters has a performance impact. Just as only vulnerable functions have cookies and code injected for the security check, only vulnerable parameters in vulnerable functions will be copied. What makes an argument vulnerable? Basically, it is an inductive set: pointers and structures that contain vulnerable parameters. The actual implementation of this feature does a more in depth analysis that includes other factors to identify vulnerable parameters. Of course, there are parameters that are vulnerable but cannot be moved, such as non-POD C++ objects. Ultimately, the choice of copying vulnerable parameters is heuristic with a goal of balancing performance with real security mitigation benefits.

This improvement makes it more difficult to use out parameters and pass by reference variables to circumvent the security checks architecture. For example, in VC 2002 an out parameter that was changed by a buffer overrun to point to the __security_cookie variable would make it possible for an attacker to get a predictable cookie value thus preventing the security check in the function epilog from triggering. This then opens the possibility for easier arbitrary code exploits such as stack smashing. The Whidbey compiler will not make use of the original out parameter, so that approach to circumventing the security checks architecture will not work.

The recently announced service pack to Windows XP and Windows Server 2003 will be built with a compiler that includes these Whidbey improvements. This will help improve Windows’s resilience in the event of a buffer overrun, and will hopefully mitigate the harm a buffer overrun can incur. It is our hope that a service pack to the VC 2003 compiler will include these updates, and we are currently investigating how that may be possible.

Another change we are making in Whidbey is that /GS will be the default behavior in the compiler. This follows from the trustworthy computing pillars that software should be secure by default. All Microsoft software is building with /GS. By making this the default, turning off security checks requires an explicit action that can be found by grep of build logs. That makes audits of code bases easier. The Visual C++ team has spent an enormous amount of effort to make /GS useful for retail software, and the fact that Windows, Office, SQL Server, Visual Studio, among other products build with this proves that it is an effective feature.

I have left out a number of details. There are number of other improvements to /GS that we are doing. I’ve been saying this for a year now, but I do hope to have time to write a revision to the white paper I wrote in February 2002. In the meantime, know that /GS is improving with each release of Visual C++.

Posted by branbray | 6 Comments

There is no language lower than C++

At the PDC, I was part of a panel that answered questions on the future of programming languages. Naturally, I represented C++. I also have a passion for functional programming languages, so I'm certainly a fan of Erik Meijer who was the moderator. (We had a rather late night at the country bar near Universal Studios the night before the panel if anyone at the panel was wondering. J)

Perhaps the most talked about message from the panel was my summary that "there is no language lower than C++." (I'll have some evidence to back this up later in this writing.) Some people have interpreted my unbridled enthusiasm for claiming that C++ developers are the smartest developers as a measure to rescue C++ from C#. Oddly, some people compared C++ to Visual Basic. It is quite amusing actually. I'm sure many people would find it interesting to have a behind the scenes look at how C++, C#, and Visual Basic compare each other.

First, the C++ language design team has made a point of not being in competition with C#. In many ways, the introduction of a language in the middle has helped Visual C++. Prior to the introduction of C#, there was a number of advanced Visual Basic users that needed to go to C++ to get there job done. Obviously, that's a tough leap. The low end of C++ isn't really a lot of fun if you're not really into programming. So, with the introduction of C#, the Visual C++ team has had the opportunity to focus more on the needs to advanced developers and those who know and understand C++. One example of this happening is the effort to make templates better in Visual C++ 2003 and to bring standards conformance levels to a more respectable level.

At the PDC, I was asked whether C# thinks they're in the middle. The answer is absolutely! A few years ago, the usability team at Microsoft developed personas for programmers in each of the languages. The Visual Basic developer was the "opportunistic programmer", the C# developer was the "pragmatic programmer", and the C++ developer was kind of guy who knew the whole system. Each of the personas were given names (yeah, I know I'm not telling you what they are) so that all teams could identify with the programmer for each language. What makes C# and C++ programmers different? Well, for one C++ developers are really multilingual developers. They use the programming language best suited for a task. A C++ programmer also tends to understand the whole system, and doesn't mind reading MSIL or assembly code. C++ developers also think in much broader contexts – they think about the architecture of the whole program, rather than just single pieces. C++ developers also tend to appreciate lower level components that can be combined to create simpler components. (It's really very difficult to create components with more utility from higher level components that have less utility). C++ programmers tend to think about performance more often, and understand the dependencies in their programs. C++ programmers try to maximize tools available to get things done more efficiently (be it, sed, awk, profilers, lint tools, perl scripts, etc.) C++ programmers will also build tools to get the job done when a tool is not available.

There is much to be said about what a C++ programmer can do. Obviously, not every developer would fit the mold of persona we developed. We were trying to make sure we built a product that was best suited for a more advanced developer and make that person productive. Every language has a goal of being productive, but different kinds of programming tasks have different demands and thus different ways of achieving productivity.

And of course one of the ways a C++ programmer is productive is that he is not blocked from going to a lower level programming style when necessary. Constantly, the C++ language design team has made an effort to expose everything the CLR enables. Three examples come to mind:

  1. A class should be allowed to be abstract and sealed. The CLS disallows global functions, so a library writer targeting many languages needs to create a utility class to contain static functions. A utility class is not meant to be a data structure, and thus it is sealed. Since all the functions in a utility class are static, it is abstract to prevent instantiation. The notion of abstract and sealed are orthogonal to each other, and while other languages disallow their combination (and in some cases have even introduced alternative ways of expressing the same concept), C++ allows it simply because the CLR allows it.
  2. The ".override" directive in MSIL is used to explicitly override a virtual function in a base class. Other languages expose this feature only for explicitly implementing an interface method. C++ provides syntax for getting to this CLR feature directly, and can be used in contexts other than explicitly implementing an interface.
  3. Properties and events really are a collection of functions. This means that different accessibility levels for get and set functions is possible, and it is also possible to bind delegates to the get or set functions.

Of course, it should also be noted that C++ has the ability to express things at a much higher level, and in some cases expresses concepts in a much more consistent, correct, and succinct manner. An example of this is destruction semantics in C++ that pretty much makes the Dispose Pattern unnecessary. I'd even say that this makes C++ a less fragile programming environment for large and long lived applications. I'll certainly spend time talking about this in more depth later.

As always, I view C++ as an enabling language. It provides a significant amount of power, and number of language tools to get the job done. The C++ language design team strives to make answers to "can I do this" questions yes whenever possible.

Posted by branbray | 12 Comments

News of my death has been greatly exagerated

Good morning!

I really meant to write earlier. I've been busy preparing for a trip to the Netherlands, where I am right now. I'll talk about that tomorrow.

Anyways, I promised I'd talk about what I heard from C++ developers at PDC. After so many years of customer concern that Microsoft didn't care about Microsoft, it was great to see how excited people were to know that C++ has a vibrant future. Of course there was the introduction of the new language design, but the message that I think was best received was that rewriting applications to take advantage of WinFX is unnecessary and almost always the wrong approach. Many people didn't know that existing C++ code can run on top of the CLR without any modification.

That's right, you heard me. The CLR is rich enough to express all of the C++ semantics. In Visual C++ 7.0, we introduced the /clr switch. As a home experiment, just try it – take any program, compile it with /clr, and chances are really high that it will just work. That is where the acronym IJW (it just works) came from. I was surprised at how many people didn't know about this. We're officially calling this feature (at least for now) C++ interop.

Of course, the great thing is that in Visual C++ "Whidbey", this feature is improving. How can something that is named "It Just Works" be improved? Well, for starters we can improve on some of the performance issues. Undoubtedly, anyone who ran the experiment above would have taken notice of a noticeable slowdown. In Whidbey, we've done several things to address this. Two of them are the __clrcall calling convention, which allows a program to avoid the top performance culprit "double thunking", and the /clr:pure switch which also avoids double thunking, but enables some CLR features that did not work with C++. Double thunking is an interesting topic, and I'll write about it later.

Another way that "Whidbey" improves the IJW feature is that we're doing a lot of the ground work in the libraries to take existing programs and let them use the Frameworks. The best example is we're enabling MFC programs to use WinForms. Naturally, much of the work will allow MFC to make use of other Frameworks APIs too. That means all the MFC programs out there can start using WinFX without ripping and replacing their current code.

Another thing I heard from PDC is that running on down-level platforms that do not have WinFX is crucial. This is similar to versioning the Win32 API. It was possible for programs to test whether the operating system had a particular API, and then change behavior based on the results. This was done with delay loading a DLL and using GetProcAddress. In the WinFX world, it changes. We're currently working on creating the best practice for doing this. It is sufficient to say that this will be possible.

Many customers asked how Microsoft applications are going to take advantage of WinFX. Naturally, quite a few developers like to see how Microsoft does something first. Just as many customers are looking into WinFX, so are all the applications around Microsoft. Existing C++ applications (that's pretty much everything Microsoft currently ships) are using Visual C++ to take advantage of WinFX. A few places where other languages make sense, other languages are being used. Another feature that Visual C++ "Whidbey" introduces is MSIL linking, which allows anyone to take a .netmodule generated by any other language, and link it into a C++ program as if the .netmodule were just another object file.

I also got asked, "What is it like to work with Herb Sutter?" Of course, Herb is great person to work with, and certainly brings another point of view to discussions. I barely knew I was working with celebrity. J It was too bad the Southern California fires prevented Herb from showing up at PDC. At the same time, Herb was at the C++ Standard WG21 meeting. Quite a few things are going on there, but we also took a chance to let everyone else on WG21 know what was going on with ECMA and the new language features that bind C++ to the CLI.

Another celebrity I was asked about is Stan Lippman. People definitely missed him at the PDC. I completely forgot to mention Stan in my last posting. Stan definitely has provided a number of contributions to the new language design. As I get to discussing individual language elements in more depth, I'll certainly show where Stan deserves much of the credit. Right now Stan, one of the projects Stan is working on is a translation tool from the managed extensions syntax to the new C++ syntax. In fact, Stan has the best expertise in this area. Sooner or later, he too will have a blog. I'll mention when that happens.

In the end, the one message I heard from customers was "thank you". There are a lot of C++ developers out there who are very excited about what Visual C++ "Whidbey" will deliver. I heard comments to the effect of "Visual C++ is back" and "C++ is on its way to being the top language again." We're very excited to see customers excited about Visual C++. C++ is definitely the most enabling language I have ever worked with, and we can't wait to see what C++ developers are going to do with WinFX.

Posted by branbray | 1 Comments

Design guidelines for C++

I'm here at PDC, and it hass been great to meet so many C++ developers. Of course, the best part of this week is that Visual C++ has finally announced the new language design for C++. As I mentioned last time, I will spend a while covering rationale and design behind this language design. Before going through the design process and some of the history in the language, I'll cover highlights of what we learned from managed extensions.

  1. Keeping compatibility with existing C++ was really great. This is what we have colloquially called "It Just Works" or IJW. Giving C++ developers seamless interop between unmanaged and managed code was the killer feature. We needed to preserve this advantage and look at making it even better.
  2. People hate underscore-underscore keywords. Even though this was done to work well with the C++ Standard (