Advertisment

Managed Extensions for C++

author-image
PCQ Bureau
New Update

Managed Extensions for C++ are a set of language extensions to C++ that provide three key features. They support calls to unmanaged C++ classes and unmanaged Library from any .NET compliant language, allow mixing of traditional C++ code (unmanaged code) with managed code in .NET and, lastly, facilitate access to .NET Framework classes from unmanaged code. Here, we’ll focus on how to make unmanaged C++ classes available for use in .NET.

Advertisment

Why C++? 



You might ask, “If .NET allows me to choose between a multitude of languages, why should I continue with C++ to write my .NET code?” C++ has always been a language that gives us immense power and flexibility to create truly innovative solutions. This ethos has been carried over to Managed Extensions, in which you not only have the complete features of the .NET runtime and class library, but also the full power of the unmanaged language. Indeed, C++ is the only language in which you can mix .NET code and unmanaged code in the same source file. The compiler allows you to seamlessly access all your unmanaged libraries: static-link libraries, template libraries, COM objects and DLLs. This easy access means that you can reuse all your existing code, and in the few cases in which the .NET Framework class library does not have suitable classes, you can use existing unmanaged libraries. Again, no other language gives you these facilities in full swing.

While contemplating about migration, the first thing to consider is whether to migrate the code at all. Consider the amount of time that an organization may have already invested in developing and stabilizing the code, familiarizing developers with it, and learning how to deploy and operate the code safely and effectively. Preserving such high investment is of paramount

importance.

Managed extensions



Managed extensions provide support for exposing your existing C++ class as a managed class. This facilitates getting the best of both worlds: take full advantage of the C++ code written already and at the same time, harness the power of .NET framework features. The result is a neat integration your existing C++ code with .NET framework; your C++ class is now ready to be consumed by any .NET-compliant language.

Advertisment

The mechanism involves writing a thin wrapper class that acts as a proxy and provides mapping between the managed and the unmanaged world. The wrapper class interoperates with its unmanaged counterpart using a pointer to the unmanaged class. 

Simple C++ class



Let us start with a simple C++ class: The ‘Employee’ class defines constructors, copy constructor and a destructor. It also overloads a few operators and implements accessors. It provides conversion operators and functionality related methods.



To start with, we create a managed C++ class and add a data member that is a pointer to the unmanaged class. 

public __gc class ManagedEmployee : public ICloneable



{


private:


Employee __nogc * m_pEmployee;


// … Wrapper Methods go here …


}



Advertisment

Observe the use of __gc keyword; this indicates that its objects are allocated on the CLR heap and automatic garbage collection is performed on this heap. Also, the ‘public’ keyword on this class declaration ensures visibility of the class outside of its assembly. Also, note the optional use of ‘__nogc’ keyword, which explicitly makes the data member type as unmanaged. The __nogc keyword is used to specify that an object be allocated on the standard C++ heap. It also indicates that the pointer data member cannot point to CLR heap. 

We need not, and should not plan a blind wrapping for every method in the original class. For example, we may have various overloading of constructors; not all of them may be appropriate to be exposed in managed environment. Also, private functions and helper functions are not to be wrapped up as they are purely for consumption from within the unmanaged class. Through selective wrapping, we get fine-grained control over the exact features we really wish to expose.

Object lifetime issues



Let us start with object lifetime issues. In the wrappers for those constructors that we plan to expose, the object of the unmanaged class is instantiated using the unmanaged new operator, calling the original constructor of the unmanaged class. In the destructor of the managed class, the unmanaged delete operator is called on the member pointer; Of course, this assumes that the managed class holds the only reference to its unmanaged counterpart. 

Advertisment

Functions with default arguments are not compliant with CLS. But, our Employee class already has a constructor with default arguments. The solution is to split this into multiple overloaded constructors in managed class. Specifically, the constructor

Employee(int EmpID, LPCTSTR pszEmpName, int nDept = 10);

has been split into 

Advertisment

ManagedEmployee(int EmpID, String * EmpName) ;



ManagedEmployee(int EmpID, String * EmpName, int DeptID);

Note the help of the helper function “Initialize” in the managed class constructors. This approach has become necessary since C++ does not allow a constructor to be called from another constructor of the same class.

When a constructor in the unmanaged class uses an unmanaged type as a parameter, the wrapper class has the responsibility to perform conversion from managed type to unmanaged type before passing the parameters to the constructor being wrapped. This technique is called Data Marshaling. Although this is being discussed in the context of constructors, this holds good for any member function in the class. The System::Runtime::InteropServices::Marshal class contains a collection of methods for handling tasks such as managed to unmanaged type conversions, unmanaged memory allocation, and copying unmanaged memory blocks.

Advertisment

Consider the second parameter in the constructor:

Employee(int EmpID, LPCTSTR pszEmployeeName, int nDept = 10);






In the “Initialize” function called by the wrapper constructor, we invoke StringToHGlobal method that allocates memory in native heap and copies the contents of a managed String object. After the C++ object has been created, the FreeHGlobal method is invoked to free this memory.

In C++, constructors may be marked ‘explicit’ to prevent implicit usage. Managed Extensions do not support ‘explicit’ keyword. However, the convert-from operator op_Explicit comes in handy. This is a static unary member function that converts an object of a class to an object of the class in which the operator is defined. Observe the usage of __gc keyword while instantiating the object of wrapper class in this method; it ensures that the object is created in the CLR heap and hence, it is accessible even when this function returns. For interoperability with .NET Framework-compliant languages that do not use op_Explicit, it is a good practice to provide the same functionality via a similar member function. In our example, this function has been named

“FromString”. 

Advertisment

Many a time, a copy constructor is commonly used in a C++ class so to prevent implicit, member-wise shallow copy; our Employee class also has one. There is no direct way to wrap this constructor since the wrapper class is a __gc type and hence, it cannot be passed by value. A workaround is to implement the ICloneable interface in the managed class and provide the “copy constructor” effect in the implementation of Clone method. Note the word ‘workaround’ since this cannot be invoked in the same way as the C++ counterpart. For example, we need to make a call like this:

ManagedEmployee *pEmployee = MyManagedEmpObj->Clone();

Let us turn our attention to member functions we wish to wrap. Functions with default arguments are not compliant with CLS. We can use the function overloading technique to achieve the same effect in managed environment. Here, we proceed the same way as discussed for constructors.

In unmanaged C++, it is common practice to make data members private and provide access through accessor member functions. This effect can be provided in Managed C++ through properties. Managed Extensions provide ‘__property’ keyword to describe a property that can have a getter function (read only), a setter function (write only), or both (read-write).

Variable arguments



What happens when a member function allows variable number of arguments? One suggestion is to use overloading to wrap functions with variable number of arguments for the required uses of the function. The preferred general pattern is to provide overloads for one, two, and three parameters of the variable parameters. Additionally, an overload should be provided that takes an array of Object *. For example, in Managed Extensions, we can declare a member function that takes a variable number of parameters as:

int foo(int i, Object * args<>);

The ParamArray attribute enables interoperability with other .NET Framework-compliant languages. The array of parameters is the last argument of the member function or constructor. The function can be called in Managed Extensions as follows:

Object * args<> = {“remaining”, “arguments”, };



foo(15, args);

Overloaded operators



How do we wrap overloaded operators? The runtime supports a number of operators as ordinary static methods that can be implemented through Managed extensions. Operators can be split across two categories: unary and binary. Both categories encompass arithmetic, logical, and comparison operators. Let us look at the basic signatures for the two overloaded operator types. For unary operators like ‘Logical NOT,’ the signature is: 

static bool op_LogicalNo(ManagedEmployee* pSource);

A binary operator (Greater Than) has signature as

static bool op_GreaterThan(ManagedEmployee* pEmp1, ManagedEmployee* pEmp2);

From an implementation standpoint, it is interesting to note that, as in C#, all operators are overloaded by implementing these functions as public static members of a managed class. This is unlike traditional C++ code, in which some have to be implemented as member functions while others are implemented as global functions.

Alternative methods



Since overloaded operators are not part of the CLS, it is a good practice to provide alternative methods that implement the same functionality as those operators. This allows them to be consumed by other languages such as VB.NET, which do not support operator overloading. 

In the Employee class, the overloaded equality operator is exposed in the managed class using the static method ‘op_Equality’. Note that the equivalent alternative is provided in ‘Equals’ function, that overrides System::Object::Equals. It is also good practice to override the GetHashCode member function of System::Object in our wrapper class. This keeps Equals and GetHashCode in agreement for the runtime.

Like Copy Constructors, there is no direct way to wrap the unmanaged assignment operators because the wrapper class is a __gc type and its objects cannot be passed by value. We can only refer to a member of such an object by dereferencing a __gc pointer to it. An equivalent workaround is provided our wrapper class in function “Assign”. 

Our “Employee” class has declared a conversion operator ‘int’ to return Employee’s id. We can use ‘op_implicit’ method to wrap this function. Of course, we could have also used ‘op_explicit’ as we did for constructors. Defining explicit operators is appropriate for conversions that potentially lose data in some way. To invoke an explicit convert-to operator, a cast must be used; this is not required for implicit conversions.

Techniques for wrapping virtual functions, and wrapping in the presence of Inheritance are not covered here. The actual techniques used will depend on the semantics of the unmanaged C++ class that is being wrapped.

An interesting question that you may have now is: Can we mix unmanaged class and managed class in the context of Inheritance? There are a few constraints when we introduce the dimension of Inheritance. A managed C++ class does not enjoy the following features: Inheritance from an unmanaged class, have an unmanaged class as a derived class. Also, declaring copy constructor, declaring friend classes or functions, overloading of ‘new’ and ‘delete’ operators, using native calling convention (__cdecl) are not permitted. RTTI is not supported since the CLR provides a much richer reflection mechanism than C++. The CLR has no representation for non-public Inheritance. The const and volatile modifiers on member functions are not supported.

Extra features



Well then, what are the extra features in managed C++ class? As we saw in our wrapper class illustration, a __gc class can have a data member that is a pointer to any unmanaged type. It can have a static class constructor and user-defined destructor. It can implement any number of __gc interfaces. It can use managed extensions to provide features like properties, events. And, last but not the least, the powerful .NET feature of Attributed programming is now available for C++ developers. 



C++ is a great language to stick to. If you have an existing unmanaged C++ application, Managed Extensions provide a smooth transition to the .NET Framework through the wrapping mechanism described in this article. Before you decide on migrating your code to .NET, consider wrapping as a practical alternative.

Meena K runs Symbiosis, a training and consultancy firm, out of Bangalore

Advertisment