Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a C# double array to a C++ function using CLI

Right, what is needed sounds simple, but it is proving to be a real pain.

I have some GUI code in C# (note i've never used C# before but familiar with the syntax) and I have C++ code which interacts with it using CLI.

In C#, I want to create an array of doubles, and send it to my C++ code. I'm using the code below as the means of passing an array, and this complies in isolation.

So from C# im passing in a double[] array into that function.

public ref class KernelWrapper
{
public: 
    static void ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, array<double>^ values); 

What parameter type should I use to retrieve this array from the C++ side?

I've tried:

 MyFunction(double values[]){}
 MyFunction(double* values){}
 MyFunction(array<double>^ values){} 

but none compile, usually with the message of "array is not a template" for the last one, and

Error   1   error C2664: 'RunImageNoiseFilterKernel' : cannot convert parameter 4 from 'cli::array<Type> ^' to 'double *'

Any tips on how this achieve this will be greatly appreciated.


For readability, I'm updating with code up here

.cpp file:

void Bangor::KernelWrapper::ImageNoiseFilter(System::IntPtr imageData, int imageWidth,    int imageHeight, pin_ptr<double> pval){
    RunImageNoiseFilterKernel((Format24bppRgb*)((int)imageData), imageWidth, imageHeight); //If parameter would work, 4th argument would also be passed into this.
}

C# code:

    double[] randomValues = new double[ARRAY_LENGTH]; //Array of random numbers
    KernelWrapper.ImageNoiseFilter(ptr, image.Width, image.Height, randomValues);

The errors are:

Error   1   error C3824: 'cli::pin_ptr<Type>': this type cannot appear in this context (function parameter, return type, or a static member)
Error   3   The best overloaded method match for 'Bangor.KernelWrapper.ImageNoiseFilter(System.IntPtr, int, int, double*)' has some invalid arguments   
Error   4   Argument 4: cannot convert from 'double[]' to 'double*' 

Hope this clarifies a little.

like image 805
BlackBox Avatar asked Mar 17 '13 13:03

BlackBox


People also ask

What is passing in C?

Pass by Value, means that a copy of the data is made and stored by way of the name of the parameter. Any changes to the parameter have NO affect on data in the calling function.

What is pass by value in C?

When you use pass-by-value, the compiler copies the value of an argument in a calling function to a corresponding non-pointer or non-reference parameter in the called function definition. The parameter in the called function is initialized with the value of the passed argument.

What is pass by address in C?

If you declare a formal parameter of a function as a pointer type, you are passing that parameter by its address. The pointer is copied, but not the data it points to. So, Pass By Address offers another method of allowing us to change the original argument of a function (like with Pass By Reference).

What is pass result?

Pass by Result:This method uses out-mode semantics. Just before control is transferred back to the caller, the value of the formal parameter is transmitted back to the actual parameter. T his method is sometimes called call by result. In general, pass by result technique is implemented by copy.


2 Answers

You should use the pin_ptr<> class to convert the managed array to a double*. It pins the array so the garbage collector cannot move it while your native function is executing and accessing the array data.

#include <vcclr.h>                 // Declares cli::pin_ptr<>
using namespace cli;
#pragma managed(push, off)
#  include "unmanagedstuff.h"      // Declaration of MyFunction here
#pragma managed(pop)

...

        pin_ptr<double> pvalues = &values[0];
        MyFunction(pvalues, values->Length);

With the assumption that surely MyFunction() also needs to know the size of the array so I added an extra argument that passes the size of the array. Not doing this is lethal, your native code is liable to destroy the managed heap integrity. Note that the array will only stay pinned while pvalues is in scope so it is important that MyFunction() does not store the passed pointer. If that's the case then you must copy the array.

like image 65
Hans Passant Avatar answered Sep 22 '22 01:09

Hans Passant


Okay, so most attempts have failed for me; likely because I'm doing it wrong :P, but here is a solution which does work.

Imports:

using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Linq;

In the C# file, I create an array as so:

    float[] randomValues = new float[ARRAY_LENGTH]; //Array of random numbers
    GCHandle handle = GCHandle.Alloc(randomValues, GCHandleType.Pinned);
    var randPtr = handle.AddrOfPinnedObject();
    KernelWrapper.ImageNoiseFilter(ptr, image.Width, image.Height, randPtr);

The definition of ImageNoiseFilter is:

static void ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, System::IntPtr randValPtr);

To obtain in my C++ function which expects a float pointer, I do this:

void KernelWrapper::ImageNoiseFilter(System::IntPtr imageData, int imageWidth, int imageHeight, System::IntPtr randValues){
    RunImageNoiseFilterKernel((Format24bppRgb*)((int)imageData), imageWidth, imageHeight, (float*) ((int) randValues));
}

All this seems to work fine :), how good or safe this approach is I do not know, so anyone reading this should not assume its the "correct" approach, it just works.

like image 31
BlackBox Avatar answered Sep 25 '22 01:09

BlackBox