Wednesday, March 5, 2008

The Split-Brain Language

In C++/CLI we have the merging of two nearly opposite things:
  • C++, a language designed to generate unmanaged code. It offers nearly unlimited run-time access to hardware, operating system and drivers, but almost no run-time access to the program's own type system.

  • CLI, a managed runtime. It offers only limited access to hardware, OS and drivers, but rich access to the program's type system at run-time.

C++/CLI is a language that can write both managed or unmanaged code. We'll leave aside for now whether mixing managed and unmanaged code is something we'd like to do, and just look at how well C++/CLI supports managed code. (We can force the compiler to produce a 100% managed assembly using the /clr:safe switch.)

There's no doubt that the unmanaged legacy of C++ makes some things more awkward, or at least more verbose, than they are in all-managed languages like C#. For instance, in C++/CLI you can't use the traditional C-ish syntax to declare a managed array, as you can in C#. That's because in C++ the C-style syntax is reserved for unmanaged arrays; so if you want a managed array you must explicitly invoke the managed type:

int[] someInts = {1,2,3}; // declares a managed array in C# but an unmanaged array in C++/CLI

array^ someInts = {1,2,3}; // what C++/CLI requires

Then there are time-honored C++ restrictions that, however necessary in the early 1970's, are useless today. For instance, the C++ compiler insists on reading a source file from top to bottom.

ref class A { B^ _b; }; // error: compiler can't find definition of B, even though it's on the next line
ref class B {};

And so the programmer resorts to a forward declaration, just to make the compiler shut up:

ref class B; // declares B but doesn't define it
ref class A { B^ _b; }; // compiler willing to be patient now
ref class B {}; // definition of B

In the next post I'll look at whether the unmanaged legacy of C++ can help us (or force us) to write better managed code.

No comments: