Visual Studio sucks

I quite like C++. The template and operator overloading features let me write concise, maintainable code without adding run-time overhead. Namespaces let me structure my program in an obvious way without worrying about names from different things colliding. Smart pointers and destructors mean I can decide once what my object lifetimes should be and then forget about it, without having to worry throughout the program that objects might go away too soon or too late.

That is, until I start using MS Visual Studio 2005 to write Windows applications. I spent much of this afternoon trying to work around two known bugs in its C++ compiler, which Microsoft does not intend to fix because the fixes are in MSVS 2008. (Upgrading is not an option, because I am using it to compile a binary plug-in for a program compiled with 2005.) In both cases, the workaround involved me replacing some concise and safe code with verbose code.

The first is with namespaces. In C++, you can add using namespace Foo to a scope to specify that when resolving unqualified names to full names in this scope, the compiler will try the namespace Foo as well as the lexically enclosing namespace and its ancestors. So, for example:

namespace Foo {
  class A {
    A() { std::cout << "Foo::A" << std::endl; }
  };
}

namespace Bar {
  class A {
    A() { std::cout << "Bar::A" << std::endl; }
  };
}

namespace Baz {
  using namespace Foo;
  A a; // resolves to Foo::A
}

A a; // XXX


As you can see, using namespace Foo makes unqualified references to A mean Foo::A (unless there is a class A already in scope, in which case the reference is ambiguous). But what of the line marked XXX? According to the standard, it should fail at compile-time, because there is no class A in scope here. But under Microsoft's C++ compiler, you actually get Foo::A (or a complaint about ambiguous names, if there is a class A already in scope). The using namespace 'leaks' out of its enclosing lexical scope, polluting other scopes. If this happens inside a header file, then when the header is later #included, names in other files start becoming ambiguous in completely unexpected ways. The two ways of getting around this are to using Foo::A each class individually, or to spell out each name in full every time it is used. If your namespaces contain lots of types, neither of these is terribly convenient. As the file I was working on was essentially a cross-bar for converting between types in one namespace and types in another, it made the file about twice as long.

It's quite lucky, really, that I encountered this bug. If I hadn't been primed to look out for compiler bugs, I might have failed to spot the second one, which is with std::auto_ptr. I covered smart pointers recently, in this post: http://ego.istic.org/articles/Shallow%20copy%20constructors%20are%20broken.pod, but I didn't mention std::auto_ptr. This class is just a pointer whose destructor deletes the pointed-to object, and with a copy constructor that does a move instead of a copy (that is, it clears the old pointer to prevent double-freeing). Use it when you want to heap-allocate an object and keep exactly one pointer to it, which can be passed around. When the pointer to it goes out of scope, the object is freed. If you just want the auto-delete functionality without passing the pointer around, then it is clearer to use boost::scoped_ptr instead, which is similar but disallows copying. std::auto_ptr can't be used in STL containers because of its unusual copy semantics.

The MSVS attempt at std::auto_ptr can't really be called an implementation because it has bugs that break standard-compliance, in the area of implicit conversions. Imagine you have a class hierarchy, and want to convert between pointers to derived and base classes.

class A { ... };
class B { ... };
class C : public A, public B { ... };
class D : public C { ... };

C *c = new C();
D *d = new D();

B *b = c;  // valid and equivalent to static_cast<B*>(c);
A *a = d; // valid and equivalent to static_cast<A*>(d);

This is all fine and dandy, apart from the fact that after all this pointer duplication I have no idea which ones I should deallocate. There is a bit of fiddling around behind the scenes when I convert a C* to a B*, because the compiler has to "jump over" the A data in *c to get at the B data, but it all works. std::auto_ptr and other smart pointers declare extra implicit conversion operators so that they work in the same way:

std::auto_ptr<C> c(new C());
std::auto_ptr<D> d(new D());

std::auto_ptr<B> b(c); // works as above, but now c is a null pointer
std::auto_ptr<A> a(d); // works as above, but now d is a null pointer

// When b and a go out of scope, they will be deleted. I hope the author of A
// and B remembered to make their destructors virtual!

Now I don't have to remember to delete the objects by hand, and if there is some exception, they will be deleted anyway, without needing a catch(...) block and rethrow. That is, until I use Microsoft's C++ standard library. It erroneously uses void * internally, so the first conversion above generates an invalid pointer to B (because it fails to do the pointer-jiggling I mentioned in the previous paragraph), and the second conversion fails at compile-time (it fails to notice that A and D are related). It also allows several implicit conversions from pointers to unrelated things to std::auto_ptr, conversions disallowed by ISO because attempting to do them is almost certainly a sign of driver error.

In the same area of cross-bar code, I was allocating objects of various types, sticking the pointer in a std::auto_ptr, fiddling with the fields of these objects (in ways that might give rise to exceptions), and each time passing the std::auto_ptr to a common function which wanted a pointer to their common ancestor class (corresponding to A in the example), and which would either call release() on it, sticking the resulting pointer into a large data structure, or would let it go out of scope and be deallocated. Because it wouldn't let me do the conversion, I couldn't use std::auto_ptrs in this case, which meant I had to call delete myself, and worry about adding catch(...) blocks (with rethrows) in exactly the right places to avoid memory leaks in the case of exceptions. Again, the code-for-the-standards-impaired is about twice as long as the code-for-actual-C++-compilers, and is error-prone and less maintainable.

As I said, both of these bugs are known and Microsoft is treating them as solved because they are not present in Visual Studio 2008. My employer pays quite a fee for MSDN licenses for Visual Studio, and we (and other people bitten by this bug) have been left in the lurch by Microsoft's premature abandonment of a product we can't migrate away from (for compatibility reasons). This is one of many reasons I won't use Visual Studio for my recreational programming, and it's yet another reminder why letting third-party, closed software vendors lock your users and customers into their product is bad for you and bad for your users.