Выделение элемента массива с плавающей точкой C ++ в c # напрямую без копирования - PullRequest
0 голосов
/ 20 сентября 2018

У меня есть матричный класс, который содержит массив с плавающей запятой фиксированного размера, который я хочу представить непосредственно в классе оболочки C #:

class Matrix
{
public:
    float data[16];
    ...
};

Обычно при переносе этой структуры в C # я определяю упакованный типкак структура POD, которая будет использовать [StructLayout(LayoutKind.Sequential)] и позволит мне напрямую маршалировать класс в и из C ++ / C #.Тем не менее, класс c ++ предоставляет другие типы (через члены или функции), которые не могут быть распределены таким образом, поэтому вместо этого мне придется использовать механизм PIMPL, где мой класс C # содержит непрозрачный указатель (IntPtr) на базовую структуру C ++.Однако я все же хотел бы напрямую предоставить базовый data член структуры c ++ объекту C #.Под непосредственным пониманием я подразумеваю, что модификации элементов data в C # будут напрямую отражаться в базовой структуре C ++ (т. Е. Член C # не может быть копией члена C ++).Моя идея состояла в том, чтобы выставить член data в C # следующим образом:

public class Matrix
{
    protected HandleRef cptr; // opaque reference to equivalent C++ instance
    public float [] data
    {
        get
        {
            return Get_data(cptr);
        }
    }

    [DllImport(MY_CPP_DLL, EntryPoint = "CSharp_Get_data", CharSet = CHARSET)]
    [return: MarshalAs(UnmanagedType.LPArray, SizeConst = 16, ArraySubType = UnmanagedType.R4)]
    private static extern float [] Get_Data(HandleRef in_object);
};

Где мой C ++ dll содержит функцию CSharp_Get_data, которая выглядит следующим образом:

DLLEXPORT float * STDCALL CSharp_Get_data(Matrix *obj)
{
    return obj->data;
}

Это компилируетсяхорошо как в C ++, так и в C #, но когда я пытаюсь сделать что-то подобное в c #:

Matrix asdf = new Matrix();
asdf.data[0] = 2.0f;

, я получаю исключение System.Runtime.InteropServices.MarshalDirectiveException, в котором указано Additional information: Cannot marshal 'return value': Invalid managed/unmanaged type combination.

IsЕсть ли способ, которым я могу использовать этот подход, но не столкнуться с этой ошибкой?Я знаю, что это довольно странный вариант использования, но мне кажется, что этот подход не нарушает ни один из стандартов сортировки, описанных MS здесь: https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-arrays.

Спасибо за любую помощь.

1 Ответ

0 голосов
/ 20 сентября 2018

Вы не можете получить float[], если только .NET не выделит память.Это верно и для StructLayout(Sequential) - буферы фиксированного размера, такие как float[16], не совместимы с float[].

Что вы можете сделать, однако, если вы хотите удобство и эффективность больше, чем вы хотитеfloat[] - использовать новые типы буферизации нулевого копирования.Вы можете создать System.Span<float> из указателя на первый элемент плюс количество элементов, и подписав этот диапазон, вы получите прямой доступ к памяти.Некоторый рефакторинг потребуется, но, поскольку Span<T> может одинаково хорошо обращаться к массивам .NET, после того как вы переписали свой код для использования Span, он может жить в обоих мирах (данные, выделенные для C # и C ++).

...