Как передать int из C# в C ++ по ссылке? - PullRequest
2 голосов
/ 20 июня 2020

Можно ли передать int из C# в C ++ по ссылке?

У меня есть простой метод на C ++, который просто увеличивает число и выводит сообщения.

void ProcessOrder(int& num)
{
    int start = num;
    int stop = num + 10;

    for (int i = start; i < stop; i++)
    {
        cout << "[Legacy] counting " << i << endl;
    }

    cout << endl;

    // Note the above was original posted code and I made a stupid mistake and was never
    // even changing `num` value passed by reference. The correct function to test
    // should be as below (discard all of the above)
    num =  num + 5; // just modify the number to any value
}

Теперь я хочу вызвать это из C ++ / CLI, а затем из C#.

void StoreWrapper::ProcessOrder(int^ num)
{
    LegacyStore* legacyStore = new LegacyStore();
    legacyStore->ProcessOrder(num); // error here
}

Но это возвращает ошибку компилятора:

error C2664: 'void LegacyStore::ProcessOrder(int &)': cannot convert argument 1 from 'System::Int32 ^' to 'int &'

Я убедился, что все три проекта - x86 но ошибка все еще сохраняется. Чтобы быть ценой, модуль CLR - это Win32, а C# Консольное приложение - x86.

Проблемы здесь:

Если я объявляю void StoreWrapper::ProcessOrder(int& num), то этот метод компилируется, но теперь я считаю, что его чистый C ++? Я не могу вызвать это со стороны C#, получите следующую ошибку компилятора:

error CS1503: Argument 1: cannot convert from 'int' to 'int*'

Если я объявлю это `void StoreWrapper :: ProcessOrder (int ^ num) ', теперь метод даже не скомпилировать.

Я пробовал использовать тип переменной System::Int32, но результат тот же.

Есть ли способ передать int в C ++ из. NET world по ссылке? Возможно ли, если сторона C ++ 32-битная, а сторона. NET 64-битная?

Обновление

Код C# здесь, в основном я хочу номер для обновления в приведенном ниже коде.

  int number = 0;
  Console.WriteLine("Original number = {0}", number);
  store.ProcessOrder(ref number);
  Console.WriteLine("After ProcessOrder number = {0}", number);

Ответ dxiv, к сожалению, не обновляет номер выше.

1 Ответ

0 голосов
/ 20 июня 2020

Попытка использовать num непосредственно как ProcessOrder(num); завершается неудачей из-за ошибки, указанной в исходном сообщении « не может преобразовать аргумент 1 из System::Int32 ^ в int &» из-за различных уровней косвенности между переданной (указателем) переменной и ожидаемым (ссылочным) аргументом.

Попытка исправить это с помощью ProcessOrder(*num); исправляет косвенное обращение и получает типы для соответствия, но не может скомпилировать с ошибкой « объект из кучи g c (тип значения без упаковки) не может быть преобразован в собственную ссылку". Это указывает на реальную проблему, заключающуюся в том, что неуправляемая / неупакованная ссылка не может указывать на управляемую кучу g c, которая подлежит сжатию и перетасовке.

Самое простое решение - использовать ссылка для отслеживания int% вместо этого, как указано в ответе на вопрос, связанный как дубликат.

void StoreWrapper::ProcessOrder(int% num)
{
    LegacyStore* legacyStore = new LegacyStore();

    legacyStore->ProcessOrder(*&num);  // shorthand for:
                                       //
                                       // int &n = *&num;
                                       // legacyStore->ProcessOrder(n);
                                       // num = n;
}

// to be called from C# as:
//
// int number = 0;
// store.ProcessOrder(ref number);


[ EDIT ] Также можно использовать num по адресу, хотя для этого требуется явное закрепление.
void StoreWrapper::ProcessOrder([System::Runtime::InteropServices::Out] int %num)
{
    LegacyStore* legacyStore = new LegacyStore();

    pin_ptr<int> p = &num;
    legacyStore->ProcessOrder(*p);
}

// to be called from C# as:
//
// int number = 0;
// store.ProcessOrder(out number);


[ EDIT # 2 ] Отредактированный код и добавленные комментарии для покрытия возврата значение для вызывающего C#.
...