How do I pass an array from .Net to C?

六月ゝ 毕业季﹏ 提交于 2019-12-11 09:02:45

问题


I am trying to make use of an open source library, written in C, by wrapping it and exposing it to .Net. I am not, by any stretch, a C expert.

So far, I have managed to call the demo code, written in C, from F#. I managed to get this far by following this guide then filling in the blanks. I have adapted the C code to expect an int passed to it, so I can at least take in a scalar value from F# into C. No return value though.

But working with arrays is much trickier.

My C code is

extern "C"
{
    __declspec(dllexport) void DisplayHelloFromDLL(int i)
    {
        //printf("Hello from DLL !\n");
        cout << "You gave me ... an int: " << i << endl;

        // Load problem data
        c_float P_x[4] = { 4., 1., 1., 2., }; //covariance matrix
        c_int   P_nnz = 4; //number of non-zero elements in covar
        c_int   P_i[4] = { 0, 1, 0, 1, }; //row indices?
        c_int   P_p[3] = { 0, 2, 4}; //?
        c_float q[2] = { 1., 1., }; //linear terms
        c_float A_x[4] = { 1., 1., 1., 1., }; //constraint coefficients matrix
        c_int   A_nnz = 4; //number of non zero elements in constraints matrix
        c_int   A_i[4] = { 0, 1, 0, 2, }; //row indices?
        c_int   A_p[3] = { 0, 2, 4}; //?
        c_float l[3] = { 1., 0., 0., }; //lower bounds
        c_float u[3] = { 1., 0.7, 0.7, }; //upper bounds
        c_int n = 2; //number of variables (x)
        c_int m = 3; //number of constraints


        // Problem settings
        OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));

        // Structures
        OSQPWorkspace *work; // Workspace
        OSQPData *data;      // OSQPData

        // Populate data
        data = (OSQPData *)c_malloc(sizeof(OSQPData));
        data->n = n;
        data->m = m;
        data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
        data->q = q;
        data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
        data->l = l;
        data->u = u;


        // Define Solver settings as default
        osqp_set_default_settings(settings);

        // Setup workspace
        work = osqp_setup(data, settings);

        // Solve Problem
        osqp_solve(work);

        // Clean workspace
        osqp_cleanup(work);
        c_free(data->A);
        c_free(data->P);
        c_free(data);
        c_free(settings);
    }
}

and my C# code is

class Program
    {
        [DllImport("TestLibCpp.dll")]
        public static extern void DisplayHelloFromDLL(int i);
        static void Main(string[] args)
        {
            Console.WriteLine("This is F# program");
            DisplayHelloFromDLL(52);
        }
    }

To keep the focus of this question narrow: how do I make P_x a parameter, to be passed in from F#?


回答1:


After reading AlexF's link and getting something to work, I thought I'd post an answer here for anyone with a similar problem. I may get some of the concepts wrong, but the code works. If I get any details wrong, let me know and I will edit.

Concepts to be aware of: Blittable types: These are types that have the same internal representation in .Net (managed) and in C (unmanaged). Blittable types are 'pinned' by the marshaler which seems to mean that a pointer is passed from .Net to C, rather than the values being copied. The memory location of the Blittable type is locked until the unmanaged function returns. Not sure how this affects garbage collection.

In/Out attributes: Blittable arrays are passed as In paramters. You must explicitly mark them as Out if you wish to use them as a return value.

Anyway, here is the code that works.

Unmanaged:

extern "C"
{
    __declspec(dllexport) void DisplayHelloFromDLL(c_float* P_x)
    {
        //printf("Hello from DLL !\n");
        //cout << "You gave me ... an int: " << i << endl;

        // Load problem data
        //c_float P_x[4] = { 4., 1., 1., 2., }; //covariance matrix
        c_int   P_nnz = 4; //number of non-zero elements in covar
        c_int   P_i[4] = { 0, 1, 0, 1, }; //row indices?
        c_int   P_p[3] = { 0, 2, 4}; //?
        c_float q[2] = { 1., 1., }; //linear terms
        c_float A_x[4] = { 1., 1., 1., 1., }; //constraint coefficients matrix
        c_int   A_nnz = 4; //number of non zero elements in constraints matrix
        c_int   A_i[4] = { 0, 1, 0, 2, }; //row indices?
        c_int   A_p[3] = { 0, 2, 4}; //?
        c_float l[3] = { 1., 0., 0., }; //lower bounds
        c_float u[3] = { 1., 0.7, 0.7, }; //upper bounds
        c_int n = 2; //number of variables (x)
        c_int m = 3; //number of constraints


        // Problem settings
        OSQPSettings *settings = (OSQPSettings *)c_malloc(sizeof(OSQPSettings));

        // Structures
        OSQPWorkspace *work; // Workspace
        OSQPData *data;      // OSQPData

        // Populate data
        data = (OSQPData *)c_malloc(sizeof(OSQPData));
        data->n = n;
        data->m = m;
        data->P = csc_matrix(data->n, data->n, P_nnz, P_x, P_i, P_p);
        data->q = q;
        data->A = csc_matrix(data->m, data->n, A_nnz, A_x, A_i, A_p);
        data->l = l;
        data->u = u;


        // Define Solver settings as default
        osqp_set_default_settings(settings);

        // Setup workspace
        work = osqp_setup(data, settings);

        // Solve Problem
        osqp_solve(work);

        // Clean workspace
        osqp_cleanup(work);
        c_free(data->A);
        c_free(data->P);
        c_free(data);
        c_free(settings);
    }
}

Managed (F#)

open System.Runtime.InteropServices

module ExternalFunctions =
    [<DllImport("TestLibCpp.dll")>]
    extern void DisplayHelloFromDLL(float[] i)

[<EntryPoint>]
let main argv = 
    let P_x = [|4.; 1.; 1.; 2.|]
    ExternalFunctions.DisplayHelloFromDLL(P_x);
    0 // return an integer exit code


来源:https://stackoverflow.com/questions/52354173/how-do-i-pass-an-array-from-net-to-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!