C++/CLI revision of the Managed Extension Reference Array Syntax

The declaration of a managed array object in the original language design was a slightly non-intuitive extension of the standard array declaration in which a __gc keyword was placed between the name of the array object and its possibly comma-filled dimension, as in the following pair of examples,

using namespace System;

void PrintValues( Object* myArr __gc[]);

void PrintValues( int myArr __gc[,,]);

This has been simplified in the revised language design, in which we use a template-like declaration to mirror the STL vector declaration. The first parameter indicates the element type. The second parameter specifies the array dimension [defaults to 1, of course]. The array object itself is a reference type and so must be given a hat. If the element type is also a reference type, then that, too, must be so marked. For example, the above example, when expressed in C++/CLI, looks as follows:

using namespace System;

void PrintValues( array<Object^>^myArr );

void PrintValues( array<int, 3>^myArr );

Because a reference type is a tracking handle rather than an object, it is possible to specify it as the return type of a function. The syntax for doing this was criticized in the original language design again as being somewhat non-intuitive. For example,

 

using namespace System;

Int32 f() [];

int GetArray() __gc[];

int GetArray() __gc[]

{

      int a1 __gc[];

      return a1;

}

 

This has also benefit from the revised declarative language syntax. The example, above, when translated into C++/CLI, looks as follows:

 

using namespace stdcli::language;

using namespace System;

array<Int32>^ f();

array<int>^ GetArray();

array<int>^ GetArray()

{

      array<int>^a1;

      return a1;

}

 

The actual definition of the array type is contained within the stdcli::language namespace, although the final identifier for this namespace, and whether it will be autogenerated by the compiler [this was auto-generated by a translation tool] or require user specification is currently under discussion in the ECMA standardization process.

 

Of course, the definition of GetArray is a dismal failure since it is returning a null tracking handle! One of the pitfalls of using the dynamic programming paradigm is adjusting to the CLR reference type semantics which, from an ISO C++ point of view, are topsy-turvy. The correct implementation is to return the object by gcnew, and not to fret over who owns the handle – the CLR garbage collector owns it. [The reason the revised language provides a deterministic finalization mechanism through a steroid destructor mechanism is not for the automation of memory management – Give GC a Change, as the late John Lennon once wrote, if memory serves me – but to handle the automatic reclamation of other system resources that are not recognized by the underlying CLR.]

 

The explicit initialization of a managed array supported within the original language is maintained in the revised syntax. For example, the following two initialized declarations,

 

int myIntArray __gc[] = { 1, 2, 3, 4, 5 };

      Object* myObjArray __gc[] =

          {

__box(26)

,

__box(27)

,

__box(28)

,

__box(29)

,

__box(30)

};

     

looks as follows in C++/CLI:

using namespace System;

array<int>^myIntArray = {1,2,3,4,5};

array<Object^>^myObjArray = {26,27,28,29,30};

 

The allocation of an array object on the heap, in the original language design, looks as follows:

 

            using namespace System;

Object* myArray[] = new Object*[2];

String* myMat[,] = new String*[4,4];

In C++/CLI, the new expression is replaced with gcnew, and the dimension sizes are passed as parameters to the gcnew expression, as follows:

 

            using namespace System;

array<Object^>^myArray = gcnew array<Object^>(2);

array<String^,2>^myMat = gcnew array<String^,2>(4,4);

I believe it is permitted in the new language to provide an explicit initialization list following the gcnew expression [but I am not going to unfathomably confirm that – this is, after all, a blog, and new language features generally fall outside my charter – see Herb Sutter or Brandon Bray’s blogs for details].

 

In the original language design, there was no explicit support for the param array that C# supports. Instead, one flags an ordinary array with an attribute, as follows:

 

void Trace1( String* format, [ParamArray]Object* args[] );

void Trace2( String* format, Object* args[] );

While these both look the same, the ParamArray attribute tags this for C# or other .NET languages as an array taking a variable number of elements with each invocation. In C++/CLI, the design for directly supporting this looked as follows:

void Trace1( String^ format, ... array<Object^>^ args );

void Trace2( String^ format, array<Object^>^ args );

in which the ellipsis […] preceding the array declaration tags it as a param array. Unfortunately, this has recently failed to make the list of features to be implemented for the inaugural release of C++/CLI. However, I can’t bear to [at least as yet] remove it from the translation tool.