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();
}