C++/CLI to C++ Tips and Tricks
In my previous post, Writing Unit Tests in Visual Studio for Native C++, I described the approach I’m using to write unit tests for native C++ code by using C++/CLI, which is C++ code that runs inside the .NET runtime. Because of this “mixed” programming model, there are some techniques you may need to employ between C++ and C++/CLI code.
Class Instance Variables
The instance variables in C++/CLI classes are different from instance variables in native C++, which means you can’t mix the two blindly. For example, you can’t use native C++ classes as instance variables in your test class. The following, for example, will not compile:
public ref class MyFixture
{
MyClass m_instance;
};
You’ll get the following error message:
error C4368: cannot define 'm_instance' as a member of managed 'Tests::MyFixture': mixed types are not supported
So how do you deal with this? Instead of using a class as the type, you have to use pointers and then create/destroy the instance in the TestInitialize and TestCleanup code:
public ref class MyFixture
{
[TestInitialize]
void Initialize()
{
m_pInstance = new MyClass();
}
[TestCleanup]
void Cleanup()
{
delete m_pInstance;
}
MyClass *m_pInstance;
};
The methods marked by TestInitialize and TestCleanup run before and after each test method.
Passing a Pointer to an Instance Variable
C++/CLI instance variables have a type of interior_ptr<T> instead of the type you wrote in your code. This makes a difference if you attempt to pass either the address or a reference to this instance variable to a native C++ method or function. For example, given the class above, you might think you could write a call to one of your native methods like this:
p->SomeMethod(&m_pInstance);
Compile this and you’ll see this message:
error C2664: 'Tests::MyFixture::MyTest' : cannot convert parameter 2 from 'cli::interior_ptr<Type>' to 'MyClass **'
This error appears because .NET uses a heap—items on the heap can be moved as a result of garbage collection. In order to send a pointer to a native method/function, you need to “pin” the pointer for the duration of the call, which you can do like this:
cli::pin_ptr<MyClass *> p = &m_pInstance;
SomeMethod(static_cast<MyClass **>(p));
Once the variable p either goes out of scope, or is assigned a new value, the pointer will be free to change again.
When you’re dealing with helper methods in your test code, you can also write this type of code:
Helper(&m_pInstance);
...
void Helper(cli::interior_ptr<MyClass *> ppInstance)
{
**ppInstance = new MyClass();
}
Comments
Anonymous
March 12, 2011
A good write-up. Thanks - makes me think I should know more about cli::interior_ptr<>Anonymous
March 15, 2011
Nothing, but funny typo: naïve C++ btw: good postAnonymous
March 16, 2011
SHK, thanks. I've fixed that typo...