C++ Language Changes for Visual Studio 2005

The next version of Visual Studio (formerly Whidbey, now Visual Studio 2005) is rich in improvements to the libraries and the usual sort of "behind the scenes" cleanups and speedups that don't get a lot of press. It has plenty of companion tools and new functionalities that will make the lives of developers simpler and happier. But to me, all of that pales in comparison to what Visual Studio 2005 has done with C++. In this column, I highlight some of the C++ language changes awaiting you in the next Visual Studio version.

Bye, Bye Underscore

Visual Studio .NET 2002 introduced managed extensions for C++. The extensions were keywords that started with two underscores, such as __gc or __property. I've written a tremendous amount of code featuring double underscores in the years since the release, and I have to confess that I never really liked it. I understand the reasoning entirely: The double underscores flag the keywords as vendor-specific extensions, and they don't mess with the standards compliance of the compiler. In theory, you could take something full of managed extensions and compile it in some other compiler, which would ignore all the __ keywords.

That was the solution: Microsoft found a way to change the language without changing the language. But that compromise has some consequences:

  • Developers found the syntax unnatural and ugly.
  • Adoption was not all it could be.

Take, for example, the way properties are defined in managed C++:

public __gc class Foo
{
   // hundreds of lines of code
    __property String* get_Text();
   // hundreds of lines of code
   __property void set_Text(String*);
   // hundreds of lines of code
};

Sure, well-behaved programmers put the get and the set right next to each other, and put whatever underlying variables are involved nearby as well. But, the language doesn't ask that of you. It offers no surrounding brace-delimited structure that lets you say "here's a property, as a unit." So while it works, it's not natural and it's unlike the other .NET languages.

But what are you going to do about it? The only way to achieve a more natural integration of C++ into the CLR and vice versa is to really change C++. And, if you're going to do that, you gain some tremendous freedoms to make a natural and elegant language that gives you the best of both worlds. And, you can ditch most of the double underscore keywords while you're at it!

Lifetime and Scope

I just love deterministic destruction. Actually, I love garbage collection, too. Maybe I should get out more. Really, though, they both have their places and I want them both. If I'm creating objects that have only memory in them, I love not having to clean up after myself. Memory management is such a pain! But, when my objects hold a non-managed resource such as a database connection, an open file, or the like, I want to take control. I want to know it will go away as soon as possible. The Dispose pattern tries to handle this, but it's not exactly intuitive. The simplicity of a closing brace is really a much nicer approach.

Here's how things look if you have to do them yourself, working in ordinary unmanaged C++:

//this is a code fragment
{
   try
   {
      Foo* f=new Foo(/* params */);
      //all kinds of code, some of which might throw exceptions
     delete f;
   }
   catch (/* something */)
   {
      delete f;
      //whatever else, or rethrow;
   }

}

Life is so much easier if you create the object on the stack:

//this is a code fragment
{
   Foo f;
   //all kinds of code, some of which might throw exceptions
}

When f goes out of scope, whether because of an exception or not, it's cleaned up. That's natural and pleasant.

When the object is on the managed heap, you don't need to delete it. It will get cleaned up by the garbage collector. But, if it holds a managed resource, you may want to clean it up, perhaps by calling its Dispose() method. C# provides the using construct for this, but it's still not as simple as our stack example.

In the new version of the language (formally called C++/CLI), where you create something does not depend on what kind of object it is. You can create a managed object on the stack, and it can have deterministic destruction, being cleaned up when it goes out of scope. If you prefer, you can create it on the managed heap. It's your choice.

This change carries other consequences, though. One of the most far-reaching is that you can easily put any kind of object into a "templated" collection or as a member variable of another class. You can get the full C++ strength for lifetime management, rather than only "allocate on the heap and wait until the garbage collector takes care of it."

Destructors and Finalizers

What happens when you write a garbage-collected object that can be used by other languages, and you've written a destructor for it? When you're working from C++, you can create that object on the stack, and the destructor will run when it goes out of scope. What happens when a C# or VB application (which cannot create garbage-collected objects on the stack) uses that object? The runtime takes care of this in a very neat way. It exposes your destructor as a Dispose() method. So any C++/CLI object that has a destructor is disposable.

If you've written a class in C# or VB that has a Dispose() method, you've probably also written a finalizer. C++/CLI has a convenience syntax for finalizers, too. Just as the destructor for Foo is called ~Foo(), the finalizer for Foo is called !Foo(). (That's the kind of gentle humor that really works for me: ~ is a bitwise NOT, and ! is a logical NOT. Both of them remind you that they are the opposite of the constructor.)

The finalizer runs when an object is created on the managed heap and never disposed (because most Dispose implementations suppress the finalizer). It's a backstop to make sure your object cleans up the unmanaged resources it holds, even if the consumers of the object forget to dispose it.

Pointers and Handles

In managed extensions for C++, because a major restriction was that the language not change, the same punctuation and syntax was used for two very different things. The meaning of * depended on some other information from another place in your code. Try it yourself. Look at this line:

Foo* pf = new Foo();

Where is the Foo object created? Is that memory going to be cleaned up or not? Can I do some arithmetic with that pointer, like this:

pf++;

The answer depends on whether Foo was declared with the __gc keyword or not. If it's a garbage-collected object, it can be created only on the managed heap—not the native heap, not the stack. On the other hand, if it was not declared with __gc, this line is allocating the memory on the native heap, and you have to remember to clean it up with delete later.

Once the compiler writers have the freedom to change the language, as has happened with C++/CLI, you can decouple what kind of class it is from where it lives. You can also signal where it lives by using different syntax:

Foo^ hf = gcnew Foo();

This is called a handle, and most of the C++ team seem to pronounce that symbol "caret or hat" the first time, and "hat" from then on. You dereference handles with * or ->, just like pointers. The impact of this change is mostly in your head. You can see how lifetime issues are managed just from the declaration of the instance, without having to go back and look at the declaration of the class.

Speaking of the declaration of the class, __gc and __nogc are gone. In their place are some cool "spaced keywords." A spaced keyword appears to be two keywords but is actually one word that happens to contain a space. For example:

ref class R
{
private:
   int m_a;
public:
   R(int a): m_a(a) {}
};

You might think "ref" is a new keyword in C++/CLI, but it's not. "Ref class" is the keyword. You can have a variable called ref that won't cause any kind of conflict. Other spaced keywords include "value class", "interface class", and "enum class". Because almost every C++ program ever written contains a variable called "value", I'm really glad that "value" did not become a keyword.

A ref class is a managed class, a class designed to live on the managed heap and be managed by the garbage collector. You can create instances on the stack if you like, as I showed earlier. The compiler will look after that for you, essentially making an invisible smart pointer.

Properties

There's more in C++ property changes—a lot more. But, because I started the column with an illustration of the awkwardness of properties in Managed C++, let me close with the much neater version in C++/CLI:

ref class R
{
private:
   int m_Size;
public:
   property int Size
   {
      int get()  { return m_Size; }
      void set(int val){m_Size = val;}
   }
};
R r;
r.Size = 42;

Is property a keyword? Sort of. It's a positional keyword, so you can have a variable or function called property without a conflict. It carries special meaning only in a class definition, as shown here. Now, the C++/CLI language supports the idea of a property definition as a single unit. I like this a lot better than the old way, and I believe you will too.

Keep Reading

The entire language specification is on the Web at http://download.microsoft.com/download/9/9/c/99c65bcd-ac66-482e-8dc1-0e14cd1670cd/C++%20CLI%20Candidate%20Base%20Draft.pdf, if you just have to read it. I'll be coming back to more examples of the syntax changes as we move through the pre-release and beta cycle for Visual Studio 2005. A new Community Technical Preview was released at Tech Ed, so you can play with the new syntax, or at least the parts that have been implemented so far. (Put these bits on a spare computer you wouldn't mind reformatting.) Check http://msdn.microsoft.com/vs2005/preview/default.aspx for updates.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.




Comments

  • Not so much GC support in VC++ 2005 Beta1

    Posted by joshscholar on 01/21/2005 04:30am

    The finalizer syntax won't compile in VC++ 2005 express Beta 1, and (as far as I can tell) it never runs the destructor in place of a finalizer. The current version of the compiler won't allow a ref class object to be created on the stack either - which would be useful.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • This paper introduces IBM Java on the IBM PowerLinux 7R2 server and describes IBM's implementation of the Java platform, which includes IBM's Java Virtual Machine and development toolkit.

  • In the competitive marketplace that surrounds us today, customers shouldn't have to settle for legacy desktop or application delivery simply because they've relied on a certain vendor in the past. This white paper reviews how three customers decided to partner with VMware, and how they benefited from the latest VDI and app trends to improve the end-user experience, increase productivity, reliability and stability to deliver better SLAs - with lower cost and less time needed to manage end users.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds