Маршальные массивы из c # (Mono) в переполнение стека

Я пытаюсь с (очень) ограниченным успехом передать массив созданной мной DLL. Я работаю в C # и выполняю некоторые операции машинного обучения в C ++. Подвох в том, что я работаю с Mono вместо .Net framework (для Unity). Я перешел по нескольким ссылкам с инструкциями, например этот а также этот.

Первая ссылка на самом деле очень полезна, я могу передать массив из c # в c ++ с известными границами без использования этого массива сборщиком мусора. Однако SAFEARRAY не поддерживается в Mono (только в .Net)

Соответствующая часть моего кода C #:

[DllImport("Classification.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void DebugStruct([In] IntPtr ptest_struct_with_arrays);

internal struct LabeledDataManaged
{
//[MarshalAs(UnmanagedType.ByValArray,SizeConst = 720)]
public float[] floatArray;
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 45)]
public uint[] uintArray;
}

static unsafe void TestMarshalMono(float[] testArr, uint[] labelsArr)
{
LabeledDataManaged labledData = new LabeledDataManaged();
labledData.floatArray = testArr;
labledData.uintArray = labelsArr;int iSizeOfTestStructWithArrays = Marshal.SizeOf(labledData);
IntPtr pStruct = Marshal.AllocHGlobal(iSizeOfTestStructWithArrays);
Marshal.StructureToPtr(labledData, pStruct, false);

DebugStruct(pStruct );
}

Моя проблема:
При отладке DLL я вижу мусор на стороне C ++. Я также попытался перейти с IntPtr на HandleRef, но я не понимаю, что нужно изменить на стороне c ++, чтобы это работало. Кстати, любой способ передачи массива без его смешивания с помощью GC — это хорошо, независимо от того, заключен он в структуру или нет.

Я обращаюсь ко всем гениям Stackoverflow с этой раздражающей проблемой.

2

Решение

Я определяю ту же структуру на стороне C ++, используя std::vector<float> а также std::vector<unsigned int>,

Важная деталь, должна быть в вопросе. Но нет, это никогда не сработает. Типы языка C ++ не имеют истории взаимодействия, детали реализации классов слишком сильно различаются между реализациями компилятора. Или, если на то пошло, слишком сильно различаются в одном и том же компиляторе. Стандартная помощь, такая как отладка итератора, изменяет расположение объектов стандартной библиотеки C ++.

Взаимодействие Pinvoke основано на типах языка C. Ваша C # декларация у вас сейчас совпадает

typedef struct LabeledData {
float* floatArray;
unsigned int* uintArray;
}

И вы, без сомнения, можете увидеть большую, большую проблему с этой структурой. Нет никакого достойного способа использовать эти массивы. Вы понятия не имеете, насколько они велики. Вы можете использовать UnmanagedType.ByValArray, но тогда эквивалентное объявление C ++ должно выглядеть так:

typedef struct LabeledData {
float floatArray[42];
unsigned int uintArray[666];
}

Вы, несомненно, видите большую, большую проблему с этой структурой, длины массивов фиксированы. И когда вы думаете, std :: vector<> тогда вам точно не нравятся фиксированные длины массивов.


Вот почему .NET нравится тип взаимодействия COM. SafeArray безопасен, потому что он также хранит длину массива. И имеет четко определенный способ их размещения и уничтожения, еще одна большая проблема с необработанными массивами. Без них лучшее, что вы можете сделать, это:

typedef struct LabeledData {
size_t floatArraySize;
float* floatArray;
size_t uintArraySize;
unsigned int* uintArray;
}

И сопоставьте его на стороне C # с:

internal struct LabelData {
public int floatArraySize;
public IntPtr floatArray;
public int uintArraySize;
public IntPtr uintArray;
}

Вам нужен Marshal.AllocHGlobal () для размещения массивов, вы уже знаете, как это сделать. И беспокоиться о том, кто и когда звонит маршалу. FreeHGlobal (). Поскольку вы, вероятно, все еще хотите сохранить std :: vector, вы скопируете массивы, чтобы освободить память после вызова pinvoke.


Копирование данных массива, конечно, очень, очень уродливо. Продвиньтесь, не используя структуру вообще, но используя четыре функции. Как CreateLabeledData, DestroyLabeledData, SetLabeledDataFloats, SetLabeledDataIntegers. И соответствующий класс C ++ с фабричным методом, который реализует эти функции.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]