by Brian Ensink
3. July 2009 18:56
With Visual Studio 2002 and 2003 Microsoft released the .NET Framework 1.0 and 1.1 respectively. These releases included the new languages C# and VB.NET and also included managed extensions for C++. With Visual Studio 2005 Microsoft released the .NET Framework 2.0 and C++/CLI which is essentially an improved version of the managed extensions for C++. C++/CLI feels more natural for experienced C++ developers and the new syntax is much cleaner than the old syntax of 2002/2003. Visual Studio 2008 finally deprecates the old syntax with a compiler warning stating the old syntax will be removed in a future version.
The application I work on at the office had some old syntax managed C++. Originally this code was developed before the .NET framework existed and later converted to managed C++ using the old syntax. Recently we had time to convert it to the new syntax as part of routine maintenance. The code had a number of enumeration values that had to be converted. Under the old syntax an enumeration looks like this:
1: __value enum TestEnum
2: {
3: Foo,
4: Bar,
5: };
Under the new syntax of C++/CLI an enumeration looks like this:
1: enum class TestEnum
2: {
3: Foo,
4: Bar,
5: };
The only difference between the two is on the first line which declares the name of the enumeration.
This conversion is correct according to Microsoft’s syntax upgrade checklist but it caused a number of bugs elsewhere in the code. It has to do with how the enum values are used. Consider the managed C++ code below.
1: Console::WriteLine(String::Format("The value of TestEnum::Foo is '{0}'",
2: Convert::ToString(TestEnum::Foo)));
3:
4: Console::WriteLine(String::Format("The value of TestEnum::Bar is '{0}'",
5: Convert::ToString(TestEnum::Bar)));
This code is valid under both the old managed extensions syntax and the new C++/CLI syntax. What does it print? The answer depends on whether you compile this under old syntax or the new syntax.
|

|
|
|
Output under the old syntax.
|
Output under the new syntax.
|
Under the old syntax the compiler seems to be treating the enum value more like an integer and generates code to call one of the overloads of Convert::ToString() to convert a number to a string. Under the new syntax the compiler appears to treat the enum value like an object and calls a different version of the overloaded function Convert::ToString(). Both of these examples were created using Visual Studio 2008.
Lets use the ildasm.exe tool to take a look at the generated MSIL to see if this is really what's happening. Here is part of the MSIL generated under the old syntax. You don’t have to be an expert in MSIL to clearly see that it is in fact calling the Convert::ToString(int) overload function.
IL_000c: ldc.i4.0
IL_000d: call string [mscorlib]System.Convert::ToString(int32)
...
IL_0026: ldc.i4.1
IL_0027: call string [mscorlib]System.Convert::ToString(int32)
The MSIL generated by the same version of Visual Studio 2008 but using the new syntax looks quite different. Clearly this code is calling the Convert::ToString(object) overload function.
IL_0007: ldc.i4.0
IL_0008: box TestEnum
IL_000d: call string [mscorlib]System.Convert::ToString(object)
...
IL_0021: ldc.i4.1
IL_0022: box TestEnum
IL_0027: call string [mscorlib]System.Convert::ToString(object)
But that is not quite the end of the story. Under both the old and new syntax the enumeration is a value type despite the use of the word ‘class’ in the declaration in the new syntax. However the MSIL under the new syntax shows that it is first boxing the enum value which converts it from a value type to an object. The new behavior of implicit boxing of value types with C++/CLI was ultimately the cause of our bugs.