результаты демаршала?

Это расширение для этого вопроса, чтобы иметь возможность возвращать массив а не скаляр.

Созданный код C из кода Matlab с помощью кодировщика Matlab теперь выглядит нормально (см. Ниже). Я просто пытаюсь понять, как вернуть результаты в мир С#. Вот моя первая попытка:

код С#

[DllImport(@"C:\bla\CPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void test(ref emxArray_real_T a, ref emxArray_real_T result);

static void Main(string[] args)
{
    double[,] array2D = new double[,] { { 1, 2, 4 }, { 1, 3, 4 } };
    var wrapper = new EmxArrayRealTWrapper(array2D);

    var t = wrapper.Value;
    var t1 = wrapper.Value;
    test(ref t, ref t1);
}

public class EmxArrayRealTWrapper : IDisposable
{
private readonly emxArray_real_T _value;
private GCHandle _dataHandle;
private GCHandle _sizeHandle;

public emxArray_real_T Value
{
    get { return _value; }
}

public EmxArrayRealTWrapper(double[,] data)
{
    _dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
    _value.data = _dataHandle.AddrOfPinnedObject();
    _sizeHandle = GCHandle.Alloc(new int[] { data.GetLength(0), data.GetLength(1) }, GCHandleType.Pinned);
    _value.size = _sizeHandle.AddrOfPinnedObject();
    _value.allocatedSize = data.GetLength(0) * data.GetLength(1);
    _value.numDimensions = 2;
    _value.canFreeData = false;
}

public void Dispose()
{
    _dataHandle.Free();
    _sizeHandle.Free();
    GC.SuppressFinalize(this);
}

~EmxArrayRealTWrapper()
{
    Dispose();
}
}

[StructLayout(LayoutKind.Sequential)]
public struct emxArray_real_T
{
public IntPtr data;
public IntPtr size;
public int allocatedSize;
public int numDimensions;
[MarshalAs(UnmanagedType.U1)]
public bool canFreeData;
}

Матлаб код:

function [result] = test(a, result)
%#codegen

if(~isempty(coder.target))
    assert(isa(a,'double'));
    assert(all(size(a) == [1 Inf]));
    assert(isa(result,'double'));
    assert(all(size(result) == [1 Inf]));
end

result = sum(a);

произведенный код C

void test(const emxArray_real_T *a, emxArray_real_T *result)
{
  real_T y;
  int32_T k;
  if (a->size[1] == 0) {
    y = 0.0;
  } else {
    y = a->data[0];
    for (k = 2; k <= a->size[1]; k++) {
      y += a->data[k - 1];
    }
  }

  k = result->size[0] * result->size[1];
  result->size[0] = 1;
  result->size[1] = 1;
  emxEnsureCapacity((emxArray__common *)result, k, (int32_T)sizeof(real_T));
  result->data[0] = y;
}

PS:

Учитывая ответ Дэвида, я сейчас пытаюсь что-то вроде этого:

[DllImport(@"C:\bla\CPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void test(ref emxArray_real_T a, ref emxArray_real_T result);

static void Main(string[] args)
{
    double[,] array2D = new double[,] { { 1, 2, 4 }, { 1, 3, 4 } };
    double[,] temp = new double[,] { { 0 }, { 0 } };
    var wrapper = new EmxArrayRealTWrapper(array2D);
    var wrapper1 = new EmxArrayRealTWrapper(temp);

    var t = wrapper.Value;
    var t1 = wrapper1.Value;
    test(ref t, ref t1);

    // initialise this by your call to the native code
    int[] size = new int[2];
    Marshal.Copy(t1.size, size, 0, 2);
    int nCol = size[0];
    int nRow = size[1];
    double[] data = new double[nCol * nRow];
    Marshal.Copy(t1.data, data, 0, nCol * nRow);
}

Это дает мне только одну запись: 7 nCol и nRow равны 1.


person cs0815    schedule 23.02.2013    source источник
comment
t1 может указывать на ту же память, поэтому вышеописанное может не сработать, но это первая попытка.   -  person cs0815    schedule 23.02.2013
comment
Прости, я не вижу, как...   -  person cs0815    schedule 23.02.2013


Ответы (1)


По сути, вы спрашиваете, как прочитать содержимое emxArray_real_T в объект С#.

Давайте сначала рассмотрим одномерный массив. Прочитайте это так:

emxArray_real_T result;
// initialise this by your call to the native code
int size = Marshal.ReadInt32(result.size);
double[] data = new double[size];
Marshal.Copy(result.data, data, 0, size);

Вот и все. Вы хотели бы утверждать, что result.numDimensions == 1.

И вам может не понадобиться делать шаг Marshal.Copy. Вероятно, у вас все еще есть доступ к массиву, который вы передали для result.data, поэтому вы можете просто использовать его.

Двухмерный случай — это то же самое. Вы снова захотите проверить, что result.numDimensions == 2.

int[] size = new int[2];
Marshal.Copy(result.size, size, 0, 2);
int nCol = size[0];
int nRow = size[1];
double[] data = new double[nCol * nRow];
Marshal.Copy(result.data, data, 0, nCol * nRow);

Это помещает данные в одномерный массив, и, вероятно, вы захотите поместить их в двумерный управляемый массив. Предполагая, что MATLAB является col-major, вам нужно будет иметь дело с преобразованием col-major в row-major.

double[,] arr = new double[nRow, nCol];
int index = 0;
for (int col = 0; col<nCol; col++)
{
    for (int row = 0; row<nRow; row++)
    {
        array[row, col] = data[index];
        index++;
    }
}
person David Heffernan    schedule 23.02.2013
comment
Извините, я не понимаю. Я попытался реализовать то, что вы предложили - см. исходный вопрос PS. - person cs0815; 23.02.2013
comment
Нужно постараться, чтобы понять, что здесь происходит. Я думаю, вы хотите, чтобы мы решили вашу проблему без вашего понимания. Конечно, nrow и ncol равны 1. Конечно, возвращается только одно значение. Это то, что возвращает нативный код. Это прямо в коде в вашем вопросе. Код в ответе верный. У вас есть все, что вам нужно, из этого ответа и других моих ответов на ваши вопросы. Если вы все еще застряли, вам нужно узнать больше о хранилище массивов Matlab. - person David Heffernan; 23.02.2013
comment
Единственное, в чем я не уверен, так это в том, являются ли данные основными или строковыми. И в каком порядке значения появляются в массиве размеров. Вы должны быть в состоянии решить это из документации. Однако мне интересно, понимаете ли вы, что означают col major и row major. - person David Heffernan; 23.02.2013
comment
Ну, nrow и ncol не должны быть одновременно одним целым. Дано: double[,] array2D = new double[,] { { 1, 2, 4 }, { 1, 3, 4 } }; (на языке С#) я ожидаю, что либо nrow, либо ncol будет равно 2. Результат должен быть 7 и 8 - одна строка и два столбца или что-то еще. Извините, если у меня сложилось впечатление, что я хочу, чтобы вы решили мою проблему. Это не мое намерение! Может быть, я что-то упустил - извините, был долгий день... - person cs0815; 23.02.2013
comment
Сгенерированный код C в вашем вопросе совершенно четко устанавливает для nRow и nCol значение 1. - person David Heffernan; 23.02.2013
comment
В любом случае, что вы пытаетесь сделать здесь. Возможно, есть более простое решение. - person David Heffernan; 23.02.2013
comment
Например, я хотел передать массив n на n и ожидал получить «двойной []» длины n назад (чтобы использовать язык С#). То, что кодировщик Matlab устанавливает для nRow и nCol значение 1, не может быть правильным imho. код Matlab работает, как и ожидалось, но сгенерированный код c явно неверен. Наверное, я был слишком амбициозен. Я всегда могу легко передать один вектор и получить результаты вектор за вектором. Я хотел избежать этого. надеюсь, это имеет смысл. Спасибо за все, что вы помогаете! - person cs0815; 23.02.2013
comment
Я имею в виду, в чем ваша проблема в более широком смысле? Что вы вызываете MATLAB здесь? Нет ли более простого способа? - person David Heffernan; 23.02.2013
comment
И да, код, сгенерированный кодером Matlab, просто кажется неправильным. Как sum может вернуть один вектор при передаче матрицы из двух столбцов? - person David Heffernan; 23.02.2013
comment
Я только что создал этот игрушечный пример, чтобы заставить интерфейс (надеюсь) работать. Спасибо! - person cs0815; 23.02.2013