Выделение простых и сложных типов данных в / из объекта ^% / void * - PullRequest
3 голосов
/ 15 января 2011

Полагаю, это будет просто для гуру C ++ / CLI.

Я создаю оболочку, которая будет предоставлять высокопроизводительные собственные классы C ++ для приложения C # WinForms.Все прошло хорошо с простыми известными объектами, и я мог также обернуть функцию обратного вызова для делегирования.Но теперь я немного запутался.

У нативного класса C ++ есть следующий метод:

int GetProperty(int propId, void* propInOut)

Сначала я подумал, что могу использовать void * в качестве IntPtr, но потом я обнаружил, чтоМне нужно получить доступ к нему из C #.Поэтому я подумал о методе-обёртке:

int GetProperty(int propId, Object^ propInOut)

, но, просматривая исходный код C ++, я обнаружил, что метод должен модифицировать объекты.Очевидно, мне нужно:

int GetProperty(int propId, Object^% propInOut)

Теперь я не могу передать объекты нативным методам, поэтому мне нужно знать, как их обрабатывать в оболочке.Поскольку вызывающая сторона всегда должна знать, какие данные он / она передает / получает, я объявил обертку:

int GetProperty(int propId, int dataType, Object^% propInOut)

Я думаю, я могу использовать ее для передачи ссылочных типов и типов значений, например,int вот так:

Object count = 100; // yeah, I know boxing is bad but this will not be real-time call anyway
myWrapper.GetProperty(Registry.PROP_SMTH, DATA_TYPE_INT, ref count);

Я только что добавил набор констант dataType для всех нужных мне типов данных:

DATA_TYPE_INT, DATA_TYPE_FLOAT, DATA_TYPE_STRING, DATA_TYPE_DESCRIPTOR, DATA_TYPE_BYTE_ARRAY

(DATA_TYPE_DESCRIPTOR - простая структура с двумя полями: int Idи описание wstring - этот тип также будет перенесен, поэтому я предполагаю, что маршалинг будет простым копированием данных назад и вперед; все нативные строки - Unicode).

Теперь вопрос - как реализовать метод-оберткудля всех этих 5 типов?Когда я могу просто привести Object ^% к чему-либо (int, float безопасен для этого?) И перейти к нативному методу, когда мне нужно использовать pin_ptr и когда мне нужно более сложное маршалинг для native и обратно?

int GetProperty(int propId, int dataType, Object^% propInOut)
{
    if(dataType == DATA_TYPE_INT)
    {
        int* marshaledPropInOut = ???
        int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut);
        // need to do anything more?
        return result;
    }
else
    if(dataType == DATA_TYPE_FLOAT)
    {
        float* marshaledPropInOut = ???
        int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut);
        // need to do anything more ?
        return result;
    }
else
    if(dataType == DATA_TYPE_STRING)
    {
        // will pin_ptr be needed or it is enough with the tracking reference in the declaration?
        // the pointers won't get stored anywhere in C++ later so I don't need AllocHGlobal
        int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut);
        // need to do anything more?
        return result;
    }
else
    if(dataType == DATA_TYPE_BYTE_ARRAY)
    {
         // need to convert form managed byte[] to native char[] and back; 
         // user has already allocated byte[] so I can get the size of array somehow

         return result;
    }
else
    if(dataType == DATA_TYPE_DESCRIPTOR)
    {
         // I guess I'll have to do a dumb copying between native and managed struct, 
         // the only problem is pinning of the string again before passing to the native

         return result;
    }

    return -1;
}

PS Может быть, есть более элегантное решение для обертывания этого метода void * со многими возможными типами данных?

1 Ответ

0 голосов
/ 13 июля 2011

Не обязательно имеет смысл приравнивать объект C # к пустоте *. Нет никакого способа маршалировать произвольные данные. Даже с объектом C # все еще знает, к какому типу он относится, и для маршалинга, что означает преобразование из мира C ++ в C # или наоборот, тип данных должен быть известен. Пустота * - это просто указатель на память совершенно неизвестного типа, так как бы вы преобразовали ее в объект, где тип должен быть известен?

Если вы описываете ограниченное количество типов, которые могут быть переданы из мира C #, лучше всего сделать несколько перегрузок в вашем коде C ++ / CLI, каждый из которых принял один из этих типов, а затем вы может закрепить переданный тип (при необходимости), преобразовать его в void *, передать его в вашу C ++-функцию, которая принимает void *, и затем выполнить маршалинг обратно в зависимости от типа.

Вы можете реализовать оператор case, как вы перечислили, но что тогда вы будете делать, если не можете обработать переданный тип? Человек, вызывающий функцию из C #, не может узнать, какие типы допустимы, и компилятор не может помочь вам понять, что вы сделали что-то не так.

...