问题
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