Search code examples
c++-clipass-by-referencepin-ptr

C++/CLI method calls native method to modify int - need pin_ptr?


I have a C++/CLI method, ManagedMethod, with one output argument that will be modified by a native method as such:

// file: test.cpp

#pragma unmanaged
void NativeMethod(int& n)
{
   n = 123;
}
#pragma managed

void ManagedMethod([System::Runtime::InteropServices::Out] int% n)
{
   pin_ptr<int> pinned = &n;
   NativeMethod(*pinned);
}

void main()
{
   int n = 0;
   ManagedMethod(n);
   // n is now modified
}

Once ManagedMethod returns, the value of n has been modified as I would expect. So far, the only way I've been able to get this to compile is to use a pin_ptr inside ManagedMethod, so is pinning in fact the correct/only way to do this? Or is there a more elegant way of passing n to NativeMethod?


Solution

  • Yes, this is the correct way to do it. Very highly optimized inside the CLR, the variable gets the [pinned] attribute so the CLR knows that it stores an interior pointer to an object that should not be moved. Distinct from GCHandle::Alloc(), pin_ptr<> can do it without creating another handle. It is reported in the table that the jitter generates when it compiles the method, the GC uses that table to know where to look for object roots.

    Which only ever matters when a garbage collection occurs at the exact same time that NativeMethod() is running. Doesn't happen very often in practice, you'd have to use threads in the program. YMMV.

    There is another way to do it, doesn't require pinning but requires a wee bit more machine code:

    void ManagedMethod(int% n)
    {
        int copy = n;
        NativeMethod(copy);
        n = copy;
    }
    

    Which works because local variables have stack storage and thus won't be moved by the garbage collector. Does not win any elegance points for style but what I normally use myself, estimating the side-effects of pinning is not that easy. But, really, don't fear pin_ptr<>.