pinvoke маршаллинг 2d многомерного массива типа double в качестве входа и выхода между c # и c ++ - PullRequest
0 голосов
/ 21 ноября 2018

У меня есть следующее c # и c ++ pinvoke marshalling 2d многомерного массива типа double вещество, которое я пытаюсь решить.

Я просмотрел следующий запрос, чтобы получить то, что у меня есть в настоящее время P / Invoke с массивами данных с двойной сортировкой между C # и C ++ .

Я просмотрел Marshalling C # Jagged Array для C ++ , который имеет очень хорошее совпадение сценариев, но не ясно, как перейти от ответа ко всем аспектам реализации.

Моя проблема, думаю, если я нахожусь наПравильный путь до сих пор, это то, как я раскручиваю c ++ *outputArray = new double[*outputArrayRows, *outputArrayCols];, который успешно передается обратно из вызова с поддержкой DllImport в переменную c # IntPtr outputArrayPtr в переменную var outputArray = new double[outputArrayRows, outputArrayCols];, необходимую для продолжения.

Вопрос= Любое понимание того, является ли цикл for правильным следующим шагом и какой синтаксис я использую внутри него?

c ++ сторона вещей

extern "C" __declspec(dllexport) void SomeFunction(double** inputArray, int inputArrayRows, int inputArrayCols,
    double** outputArray, int* outputArrayRows, int* outputArrayCols)
{
    // just initialize the output results for testing purposes no value assignment as of yet
    *outputArrayRows = 10;
    *outputArrayCols = 2;
    *outputArray = new double[*outputArrayRows, *outputArrayCols];
    return;
}

extern "C" __declspec(dllexport)DllExport void FreeArray(double** allocatedArrayPtr)
{
    delete[] allocatedArrayPtr;
}

c # сторона вещей

[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void SomeFunction(double[,] inputArray, int inputArrayRows, int inputArrayCols, 
    out IntPtr outputArray, out int outputArrayRows, out int outputArrayCols);
[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
static extern void FreeArray(IntPtr allocatedArrayPtr);

[TestMethod]
public void DllImport_SomeFunction_ShouldNotThrowException()
{
    var inputArray = new double[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

    IntPtr outputArrayPtr; int outputArrayRows, outputArrayCols;
    DllImportUnitTests.SomeFunction(inputArray, inputArray.GetLength(0), inputArray.GetLength(1), 
        out outputArrayPtr, out outputArrayRows, out outputArrayCols);
    var outputArray = new double[outputArrayRows, outputArrayCols];
    IntPtr[] outputArrayPtrArray = new IntPtr[outputArrayRows];
    //Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayRows); // overload for double[] but not for double[,]
    Marshal.Copy(outputArrayPtr, outputArrayPtrArray, 0, outputArrayRows);
    FreeArray(outputArrayPtr);
    for (var i = 0; i < outputArrayPtrArray.Length; i++)
    {
        Marshal.Copy(outputArrayPtrArray[i], outputArray[i ???], 0, outputArrayCols);
    }

    Assert.IsNotNull(outputArray);
}


обновление, содержащее ответ [/ что сработало для меня]

На основе комментариев я обновил заголовок, чтобы обозначить эту проблему, связанную с попыткой передать и получить2d [/ многомерный] массив nне зубчатый массив.Тем не менее, в моих тестах стало очевидным, что среда проекта dll vs17 c ++ windows desktop делает только неровные массивы [например, c ++ DllExport double** SomeFunction(double** inputArray, . . . и double** returnArray = new double*[numberOfRows] и c # double[][] dogLegValues = new double[numberOfRows][/* numberOfCols not specified */];].Ниже я добавляю сигнатуры функций C # pinvoke DllImport и c ++, с которыми мне удалось поработать, и некоторый интересный код маршаллинга для подготовки 2d-массива для передачи в виде зубчатого массива и для обработки возвращенного зубчатого массива, в конечном итоге преобразующего его в2d массив, который ожидал вызывающий объект, если это помогает другим.

c # Оператор DllImport и комментарии, фиксирующие результаты

[DllImport(dllName /*, CallingConvention = CallingConvention.Cdecl */)]
//static extern /* double[] */ IntPtr SomeFunction(double[] inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke can marshal double[] 1d array input but not output
static extern /* double[,] */ IntPtr SomeFunction(/* double[,] */ IntPtr[] inputArray, int inputArrayRows, out int outputArrayRows); // pinvoke cannot marshal double[,] 2d array input or output

сигнатура функции c ++

#define DllExport extern "C" __declspec(dllexport)
//DllExport double* SomeFunction(double* inputArray, int inputArrayRows, int* outputArrayRows) // using flattened 2d array input and output
DllExport double** SomeFunction(double** inputArray, int inputArrayRows, int* outputArraysRows) // using 2d converted to jagged array [ of arrays ] input and output

c # код маршалинга для 2dмассив сведен в массив 1d

int outputArrayRows; const int outputArrayCols = 2;
double[] inputArrayFlattened = new double[inputArray.Length];
//var index = 0; foreach (var value in inputArray) { inputArrayFlattened[index] = value; index++; } // more concise flattening but adds a stack frame variable 
for (var i = 0; i < inputArray.GetLength(0); i++) { for (var j = 0; j < inputArray.GetLength(1); j++) inputArrayFlattened[i * inputArray.GetLength(1) + j] = (double)inputArray.GetValue(i, j); }
IntPtr outputArrayPtr = MyUnitTests.SomeFunction(inputArrayFlattened, inputArray.Length, out dogLegValuesRows);
double[] outputArray = new double[outputArrayCols]; Marshal.Copy(outputArrayPtr, outputArray, 0, outputArrayCols);

c # код маршалинга для массива 2d

IntPtr[] inputArrayPtr = new IntPtr[inputArray.GetLength(0)];
var inputArrayJagged = inputArray.ToJaggedArray();
for (var i = 0; i < inputArrayJagged.Length; i++)
{
    IntPtr inputArrayJaggedRowPtr = Marshal.AllocCoTaskMem(sizeof(double) * inputArrayJagged[i].Length);
    Marshal.Copy(inputArrayJagged[i], 0, inputArrayJaggedRowPtr, inputArrayJagged[i].Length);
    inputArrayPtr[i] = inputArrayJaggedRowPtr;
}
IntPtr outputArrayJaggedPtr = MyUnitTests.SomeFunction(inputArrayPtr, inputArray.GetLength(0), out outputArrayRows);
IntPtr[] outputArrayJaggedPtrArray = new IntPtr[outputArrayRows];
Marshal.Copy(outputArrayJaggedPtr, outputArrayJaggedPtrArray, 0, outputArrayRows);
//FreeArray(outputArrayJaggedPtr); // doesn't appear we need this given passing result back as return value and no issue when returning 1 row but crashes when returning 2 rows

double[][] outputArray = new double[outputArrayRows][/* outputArrayCols not specified */];
for (var i = 0; i < outputArrayJaggedPtrArray.Length; i++)
{
    outputArray[i] = new double[outputArrayCols]; // can't do this with a double[,] 2d array or can you ???
    double[] outputArrayJaggedRow = new double[outputArrayCols]; 
    Marshal.Copy(outputArrayJaggedPtrArray[i], outputArrayJaggedRow, 0, outputArrayCols);
    outputArray[i] = outputArrayJaggedRow;
}

var results = outputArray.ToTwoDimensionalArray();

c ++ примеры инициализации и назначения зубчатых массивов

// hard coded test return values used to get pinvoke marshalling worked out using flattened 2d array input and output
double* returnArray = new double[2]; // or new double[outputDataCols] 
returnArray[0] = 1234.56; returnArray[1] = 98.76; dogLegValuesRows = 1;

// hard coded test return values used to get pinvoke marshalling worked out using 2d converted to jagged array [ of arrays ] input and output
double** returnArray = new double*[2]; // or new double*[*outputDataRows]
returnArray[0] = new double[2]; // or new double[*outputDataCols]
returnArray[0][0] = 1234.56; returnArray[0][1] = 98.76; //*outputDataRows = 1;
returnArray[1] = new double[2]; // or new double[*outputDataCols]
returnArray[1][0] = 7890.12; returnArray[1][1] = 34.56; *outputDataRows = 2;

1 Ответ

0 голосов
/ 21 ноября 2018

Этот код:

*values = new double[*valuesOuterLen, *valuesInnerLen];

не делает то, что вы думаете, он делает.(Я сократил имена переменных, потому что они просто усложняют вещи.)

C ++ наследует от C отсутствие многомерных массивов.То, что у него есть, это массивы массивов или массивы указателей на массивы.В обоих случаях вы индексируете их как array[firstIndex][secondIndex].Вы не можете new массив таких указателей, вы должны написать что-то вроде:

double*** values;  // Triple pointer!  Ow!!  *values is a pointer to
                   // (an array of) pointers to (arrays of) doubles.
*values = new double*[*valuesOuterLen];
for (size_t i=0; i<valuesOuterLen; i++) {
    (*values)[i] = new double[*valuesInnerLen];
}

На самом деле вы вызвали оператор запятой C ++, который вычисляет и отбрасывает первый операнд, изатем оценивает второй операнд.Сверните предупреждения вашего компилятора;хороший компилятор предупредит, что первый операнд не имеет побочных эффектов.

...