C++ remains one of the most widely used languages in the programming environment to this day. Legacy applications and libraries use C++ in one way or the other, either by using the ISO/ANSI standard or even more recent technologies like the Microsoft Foundation Class (MFC). However, the fact remains that software developers simply cannot live without C++.The development period for a GUI application in C++, from an industrial standpoint, is unbearably long. This is compounded by the fact that Rapid Application Development (RAD) technologies have been growing exponentially in the last decade, not to mention the growing importance of web based applications. A plausible solution would be to try another language that allows developers to ship applications much faster. An important consideration is to ensure full compatibility with existing libraries and application source code (the ones written in C++). This is where Microsoft introduced the .NET framework and the C# language. The C# language is extremely similar to the Java language, although it derives a lot of its base concepts from C++.

In order to allow .NET developers to utilize existing C++, Microsoft introduced Managed Extensions for C++. This was discontinued and replaced with the current standard C++/CLI. CLI stands for ‘Common Language Infrastructure’. It was introduced in 2004 and has been standardized by Ecma (European Computer Manufacturers Association) as ECMA-372. C++/CLI is supported in Visual Studio, from the VS 2005 version onwards. C++/CLI allows the existing C++ code to interface with the .NET framework. This allows developers to incorporate legacy code into future applications, cutting development time and its associated cost. This paper explains how to use C++/CLI to interface C++ code bases with the .NET framework that can then be used by a wide variety of languages (C#, F#, ASP.NET, XML, etc.).

Data types

The fundamental data types of ISO/ANSI C++ work the same way in C++/CLI programs as well. In spite of this the data types have a different meaning and may introduce additional capabilities in certain situations. In C++/CLI, a fundamental data type is a value class type, and it is possible to behave as an ordinary value or as an object, depending on the circumstances and is defined in the System namespace. So, all data types are derived from System::ValueType, which is in turn derived from System::Object. The value type gets allocated on the stack if done statically, while dynamic allocation causes it to be allocated on the heap. This behavior is similar to that of ISO/ANSI C++ and will be covered in later sections.

The following table gives the data type, the class being invoked and the size of the data type.

 

 

For example, the developer would like to declare an integer and a character in a C++/CLI program. The way to do this would be

http://switch.slb.com/wp-content/uploads/2012/11/Code-1.png

They could also be written with the base class name, to be more precise, as

http://switch.slb.com/wp-content/uploads/2012/11/Code-2.png

Both ways work, although it is better to use the latter method since there are other data types that follow a similar syntax. These data types would be delved into in later sections.

Input/Output using CLI

There are syntactical differences between native C++ and C++/CLI for input and output on a command prompt level. The syntax for C++/CLI is the same as that of C#, except for the data type that holds the variable.

Let us print out the ubiquitous introductory example “Hello World”. This is done using the line

http://switch.slb.com/wp-content/uploads/2012/11/Code-3.png

For example, let us assume that we have the same integer and character declared as in the above example.

http://switch.slb.com/wp-content/uploads/2012/11/Code-4.png

The way to output the integer data type is as follows,

http://switch.slb.com/wp-content/uploads/2012/11/Code-5.png

However, please note that this method would not work as is for the ‘char’ data type. If we attempt the following syntax to output the character, it would not work.

http://switch.slb.com/wp-content/uploads/2012/11/Code-6.png

This would output the ASCII code for the alphabet ‘A’ i.e. 65. To avoid this and to output the alphabet ‘A’, the data type needs to be changed to that of a string. This is a new data type, one that would be covered briefly right now.

The string data type would be declared as follows,

http://switch.slb.com/wp-content/uploads/2012/11/Code-7.png

The output does not require any special formatting, the usual way works just fine to output the letter ‘A’.

http://switch.slb.com/wp-content/uploads/2012/11/Code-8.png

C++/CLI also enables developers to output in-scope values into an output stream, as does most other languages. The way to do this is given below.

Suppose we have 3 integers that have been declared as below,

http://switch.slb.com/wp-content/uploads/2012/11/Code-9.png

In native C++, they can be output with strings using the cout operator:

http://switch.slb.com/wp-content/uploads/2012/11/Code-10.png

In C++/CLI, the syntax is different. The entire string is passed as a whole, and the places where a value needs to be inserted is marked specially using an index. The variable that needs to be printed is placed as an argument to the print function at the corresponding index. This would be clear with an example that achieves the same result as the previous example. In C++/CLI, the way to go about this is:

http://switch.slb.com/wp-content/uploads/2012/11/Code-11.png

The {0}, {1} and {2} represent the indices for the variables that need to be substituted; namely number1, number2 and number3 respectively.

Another quick time saver is the ‘endl’ manipulator function that ISO/ANSI C++ uses to move to the next line. This is used most commonly while outputting formatted text like,

http://switch.slb.com/wp-content/uploads/2012/11/Code-12.png

In C++/CLI, this is avoided by using the WriteLine function, as was seen in the previous function. There are 2 ways to output streams of text – Console::WriteLine and Console::Write. Console::WriteLine outputs the text given to it and then transfers control after moving the cursor to the next horizontal line. On the other hand, Console::Write does not move to the next horizontal line. It transfers control at the present cursor position. Therefore, to output “Hello World” in a single line using 2 different write functions, the developer needs to go about as mentioned below:

http://switch.slb.com/wp-content/uploads/2012/11/Code-13.png

There are a wide variety of reading elements that are supported by C++/CLI. The developer has the opportunity to read by the line and by the character, as well as the ability to detect keystrokes. (The last ability is really helpful since it may be needed to invoke event handlers)

To read by the line, the Console::ReadLine() function is used. This function would read a complete line of input text and would terminate only upon the pressing of the Return key. An example to show its usage is

http://switch.slb.com/wp-content/uploads/2012/11/Code-14.png

To read by the character, the Console::Read() function is used. This function reads input data by each character, and then analyzes the characters read and converts the input to a corresponding numeric value. An example to show its usage is

http://switch.slb.com/wp-content/uploads/2012/11/Code-15.png

C++/CLI also has a function that can return the key that was pressed as an object. This is a nifty feature for developers as it has some good applications. The function that achieves this is ReadKey() function, and it returns the key pressed as an object of the ConsoleKeyInfo type, a value class declared in the System namespace. The ReadKey() function needs a Boolean value that needs to be passed as an argument. An argument of true does not result in an echo of the keystroke on the screen, while a value of false will cause the character of the key press to be displayed. This choice is given to the developer based on what he/she perceives to be the right user interface for the scenario. An example to show this function in action is given below.

http://switch.slb.com/wp-content/uploads/2012/11/Code-16.png

Once this function is executed, the result would be stored in ConsoleKeyInfo. To identify the character corresponding to the keystroke, the expression keypress.KeyChar is to be used. This can be used for passing messages on keystrokes or any other event handler. For example, once the ReadKey() function has been executed, the developer might think it is a good idea to let the user know which key was recorded. One way to do this is given below,

http://switch.slb.com/wp-content/uploads/2012/11/Code-17.png

Loops and Decision Control

All the loops and decision control statements like, if-else, for, while, do-while, logical operators, etc. are the same for C++/CLI as they are for native C++. There is a useful loop command for going through lists or arrays. As an example, to print out all the elements in an integer array in native C++, one must first find the total length of the array and then iterate through every element until the last one with a print statement. In C++/CLI and in C#, this is made much simpler by using the ‘for each’ statement. Assume an array named test has been declared with unknown size. The developer would like to print out every element of the array. The code to do this is given below as,

http://switch.slb.com/wp-content/uploads/2012/11/Code-18.png

Arrays and Array Initialization

The arrays in C++/CLI are extremely powerful and come with a wide variety of functions already built-in, making the life of developers much easier. The built-in functions are examined in the next section.

Firstly, arrays are declared on the CLR heap. The CLR heap is a different entity in comparison to the native C+ heap. An important point to note is that the CLR heap deletes memory that has been allocated when it is no longer required. Because of this dynamic garbage collection, the engineer does not need to worry about memory leaks. The heap could also compact memory to avoid fragmentation and this really helps the engineer when it comes to dealing with memory management.

Since the garbage collection is dynamic, a tracking handle is required to ensure the compiler knows when to free up a memory location. The tracking handle symbol is ‘^’ in C++/CLI. An example that has already been covered is String^. The right way to initialize a String object is given below,

http://switch.slb.com/wp-content/uploads/2012/11/Code-19.png

When it comes to CLR arrays, the generic syntax to initialize an array is

http://switch.slb.com/wp-content/uploads/2012/11/Code-20.png

Examples –

http://switch.slb.com/wp-content/uploads/2012/11/Code-21.png

Note that declaring an array of strings involves 2 track handlers, one for the string data type and the other for the array itself.

Array indexing is the same as that of native C++, i.e. it begins from index 0 and goes until the last element. Also, every element can be traversed by using the ‘for each’ keyword that was discussed previously. To initialize an array, the keyword ‘gcnew’ needs to be used. This is shown in several examples below.

http://switch.slb.com/wp-content/uploads/2012/11/Code-22.png

This creates an array named data of type int (System::Int32) that can store 100 elements.

http://switch.slb.com/wp-content/uploads/2012/11/Code-23.png

This creates an array of strings named names that contains 3 elements.

Likewise, the software engineer can create any array of any type as the situation demands it.

Array Functions

a) Length function

The determination of the length of an array is extremely straightforward. Since every array is declared with a tracking handle, the elements can be considered as pointers. Thus, the ‘->’ operator can be used on the array.

From the previous example, if a developer would like to determine the length of the array data, all that is needed is the following statement:

http://switch.slb.com/wp-content/uploads/2012/11/Code-24.png

The Length operator returns the size of the array as a 32-bit integer. If the developer is in need of a 64-bit integer, the LongLength property can be used instead.

b) Sort function

The Sort() function makes sure that the elements of the array are in ascending order. It is a very straightforward function that needs just the array name as the input parameter. For example, assume the array data to be filled with 100 numbers, each of which has been inserted randomly. If the developer would like to sort this array, the syntax would be

http://switch.slb.com/wp-content/uploads/2012/11/Code-25.png

c) BinarySearch() function

The BinarySearch() function is a function that returns the index position of a given element in the array or within the specified range of a sub-array. For the required element to be found, the element must be stored in a variable whose name is ‘toBeFound’. The variable type is that of the element that needs to be found. The BinarySearch() function takes 2 arguments, the array that needs to be searched and the values that are being searched for.

This might seem slightly confusing at first but hopefully, the following example would make it clearer.

http://switch.slb.com/wp-content/uploads/2012/11/Code-26.png

This example would yield the numeral ‘2’, which is the position of the element ‘789’ in the array test. Note that the position is ‘2’ since the index begins from 0.

d) Clear function

The Clear() function can set all elements to 0. It also offers the added flexibility of choosing the starting and ending points, so even a sub-set of the array can be set to 0. The syntax of the Clear() function is

http://switch.slb.com/wp-content/uploads/2012/11/Code-27B.png

As an example, take the previous integer array data. To clear the entire array, the syntax is

http://switch.slb.com/wp-content/uploads/2012/11/Code-27.png

References

In native C++, the developer has the opportunity to pass arguments by value or by reference. To pass by reference, the ‘&’ symbol is used to signal to the compiler that the argument is to be passed by reference. In C++/CLI, the corresponding symbol is ‘%’ and they are called tracking references. They can be created for value types on the stack and reference types on the heap. The references are automatically updated if the object is moved around by the garbage collector, and the references themselves are stored on the heap. For example,

http://switch.slb.com/wp-content/uploads/2012/11/Code-28.png

This example would output 20, since ‘tracktest’ is simply another name for test. You can alter test using tracktest.

Pointers

There is no direct mapping of the concept of pointers from ISO/ANSI C++ to C++/CLI. C++/CLI does not provide a direct equivalent of pointers with which the engineer would be able to perform pointer arithmetic. It does, however, provide alternatives that would help the developer work with memory addresses. The first of these alternatives is called Interior Pointers. An interior pointer is an automatic variable that is local to a function. It is declared by interior_ptr<data_type>. The address that is stored in an interior pointer would be updated every time the garbage collector moves any object around. As an example, a developer might need to point to the first element in an array and then move around for whatever reason. The way to go about doing this is as follows

http://switch.slb.com/wp-content/uploads/2012/11/Code-29.png

If the value of ptr is output right now using the syntax

http://switch.slb.com/wp-content/uploads/2012/11/Code-30.png

The value outputted would be ‘TRUE’. This is quite odd since the value expected is ‘1’. This would be outputted if we use the right syntax, namely

http://switch.slb.com/wp-content/uploads/2012/11/Code-31.png

The ‘*’ symbol is the same symbol that developers need to use when outputting the value stored by a pointer in native C++. The Boolean value being returned is simply letting the user know that the pointer contains a value. If the pointer is initialized to a nullptr, it would output ‘FALSE’.

The other alternative that is available in C++/CLI is pinning pointers. A pinning pointer is an interior pointer that prevents objects being pointed to from going into the garbage-collection heap. Think of it as an anchor that retains the object and its value. The value of a pinning pointer will not be changed by the CLR. This becomes a necessity when dealing with a mixture of managed and unmanaged objects. The reason is because the address wouldn’t change unexpectedly during the resolution of an unmanaged function call.

Both of these alternatives are the ones provided by C++/CLI in lieu of direct memory management. A concept that cannot be covered in detail in this paper is that of marshalling. The developer might have to marshal data to a particular type so that the data can be used as is intended across managed and unmanaged versions of C++. In the example below, a String^ object had to be marshaled into a char* type: http://switch.slb.com/wp-content/uploads/2012/11/Code-32.png

This example demonstrates the correct usage of marshaling.

Functions

In C++/CLI, functions are passed the same way as they are in native C++ for the most part. The function is declared by

http://switch.slb.com/wp-content/uploads/2012/11/Code-33B.png

For a simple example,

http://switch.slb.com/wp-content/uploads/2012/11/Code-33.png

However, since C++/CLI deals with tracking handles, there are certain variations in passing arguments.

It is possible to pass a variable number of arguments into a function. This is achieved by using the ellipsis ( … ) operator in the argument list followed by an array declaration of the type of data being passed. The example would make things clear:

http://switch.slb.com/wp-content/uploads/2012/11/Code-34.png

The ‘for each’ loop would be perfect to iterate through all the elements of the array in this case.

There are certain points for argument passing that are worthy of mention:

  • Address arithmetic is not allowed in C++/CLI. Always use array indexing.
  • Returning handles to objects that have been placed on the CLR heap is not an issue. The garbage collector would take care of releasing all unused memory.

Also, the main() function accepts command-line arguments differently than the native C++ version. Developers are used to seeing main function as shown below:

http://switch.slb.com/wp-content/uploads/2012/11/Code-35.png

However, in C++/CLI, to use a command-line argument, the main function needs to be passed an argument of an array of strings, i.e.

http://switch.slb.com/wp-content/uploads/2012/11/Code-36.png

An example demonstrating the usage of the array would hopefully make things crystal clear for engineers.

http://switch.slb.com/wp-content/uploads/2012/11/Code-37.png

Whatever the user inputs into the command-line would be broken up into different sub-strings and would be outputted in a similar format as that of native C++.

Classes

Figure 1

Figure 1 -Types of struct and class

As shown in figure 1, C++/CLI supports both classes and structures. A point to be noted is that all the 2-word combinations are keywords i.e. value class, value struct, ref class andref struct. The difference between value and ref types is that value classes and structs must contain their own data. Ref classes and structs, on the other hand, must be accessed by variables that are handles and so have to contain addresses.

A reference class does not have a default copy constructor or a default assignment operator. If the situation demands that the class have these features supported, the developer would have to manually incorporate them in the class. Also, assignment operators must not be overloaded in value classes. This is because the process of assignment of one value class object to another is defined to be member-wise copying. This cannot be altered by the developer and attempting to do would result in an error. Other than these differences, operator overloading is similar to that in native C++. In ref class, the operator overloading contains parameters and values that are typically handles.

Class Inheritance

All classes in C++/CLI are derived from a standard class, System::Object. As a result, all classes are derived classes by default. Since System::Object is similar to the role of void* in native C++, the handle in native C++, the handle System::Object^ can reference any type of object.

When a value class is declared, the developer is not allowed to specify a base class, since all value classes are derived from System::Object. This means that polymorphism is allowed only for those functions that are defined as virtual in the System::Object class.

A reference class can be derived from an existing reference class in the same way as deriving a class in native C++. The following example would provide the syntax for actually doing so.

http://switch.slb.com/wp-content/uploads/2012/11/Code-38.png

A point to note in the example above is the use of the ‘abstract’ keyword. If a CLI class contains a pure virtual function (according to native C++), the class must be specified as abstract. This example demonstrates the usage of inheritance with classes.

Comments

Great article

Hey Vishnu,
This article has been a great help! Thanks!!