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.


 


 


Comments (4)

  1. Yves Dolce says:

    As much as I already love the new syntax, I must confess that I’m suprised by this new … syntax:

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

    Could you or someone else on the team explain what the reasoning behind it was?

    Thanks.

  2. .. says:

    ewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww