Thought on mixing managed and unmanaged codes in C++/CLI

With C++/CLI, MS introduced garbage collection to C++.
It is very nice to have modern runtime for C++. So, I would like to write how to mix unmanaged code and managed code here. ( It is very basic. )

pragma once
#include <vcclr.h>

using namespace System;

class CSystemTime
{
public:
	CSystemTime(void);
	~CSystemTime(void);
private:

	// declaring managed object in unmanaged class is not allowed.
	// However, creating one in a heap in a method is allowed.
	//String ^m_strHello;

	gcroot<String ^> m_strHello;
public:
	void SayHello(void);
};
#include "StdAfx.h"
#include "SystemTime.h"

using namespace System;

CSystemTime::CSystemTime(void)
	: m_strHello(NULL)
{
	m_strHello = gcnew String( "CSystemTime says..." );
}

CSystemTime::~CSystemTime(void)
{
}

void CSystemTime::SayHello(void)
{
	Console::WriteLine( m_strHello );
}
// Hour22.cpp : main project file.

#include "stdafx.h"
#include "SystemTime.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	// Because CSystemtime is a non-managed class
    CSystemTime *pSystemTime = new CSystemTime;

	pSystemTime->SayHello();

	delete pSystemTime;

    return 0;
}

Because CSystemTime is unmanaged class, new should be called to create it in main().

In CSystemTime.h, it is not possible to declare managed object directly in stack. In other words, it is not possible to declare managed object as a member variable of unmanaged class. So, gcroot is used. Although m_strHello is created in stack, it holds reference to managed class, String.

In SystemTime.cpp, creating String object is done by calling gcnew, because m_strHello is to contain reference to String.
Hmm.. Sounds natural, right?
However, there is one trick hidden there.
m_strHello itself is a non-pointer type. So, it should contain non-pointer type. However, the reference in managed environment is analogous to pointer type in unmanaged environment. So, although it looks like that it is to assign a reference to String object to m_strHello, but actually it is to store the reference to String inside of m_strHello.

To make things more apparent, please read “How to : Declare Handles in Native Types”

This is excerpt from the link.

class CppClass {
public:
   gcroot<String^> str;   // can use str as if it were String^
  ...
};

int main() {
   CppClass c;
   c.str = gcnew String("hello");
   ...
}

is same to my example. str itself is static type.

Now let’s take a look at this.

struct CppClass {
   gcroot<String ^> * str;
   CppClass() : str(new gcroot<String ^>) {}

   ~CppClass() { delete str; }

};

int main() {
   CppClass c;
   *c.str = gcnew String("hello");
   ...
}

Now, the str is a pointer to gcroot. So, in its constructor, it creates memory space for it by default and the destructor deletes the str.
And in main() function, str is deferenced first and reference is assigned to str.

So, here the sematics of = operation is somewhat different from that of C++. it is to assign to an internal space to str not str itself.

One of OOP’s main concept is data encapsulation and hiding.
It is to hide the internal implementation to outside. However, to understand the = operator, you should think about the internal implementation like I described so far.

Now, let’s compare it with that of Objective-C.
Interestingly, if you take a look at GCC source codes for Objective-C, you will know that Objective-C is really “simple” addition to C. However, should you study new keywords like gcnew and gcroot to use its garbage collector? Is there such thing to understand internal implementation of Objective-C class? ( Sometimes you should understand them, but it is for different purpose. )
So, it is interesting how elegantly and considerately designed the Objective-C is. Without learning new keywords much, it supports all the modern facility pretty well. I’m sorry to compare MS’s and GCC/Apple’s, but to learn two different things, it is helpful to compare and find differences.

Here, the example above also exposes the problem of operator overloading. Although the = in the example is not overridden by you, but it seems to be that the context is overridden. If operator overloading is done just because of someone’s convenience, it can hurt the data hiding and encapsulation paradigm.
I like operator overloading, but I also know that there are many compiler/language designers who opposed the idea, and at least I can understand why they don’t like it.

It is always good to know multiple different technologies, because it can widen the way we think! : )

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: