2015-09-07 4 views
2

В настоящее время я пишу набор Wrappers для некоторого собственного кода на C++. В оболочке я беру управляемый массив в качестве ввода и намерен использовать содержимое массива для вызова собственного конструктора C++. По какой-то причине мне, кажется, нужно либо привязать массив, либо извлечь значения отдельно от вызова конструктора. Вот несколько примеров того, что я имею в виду.Доступ к управляемым массивам и пиннингам

Конструктор для нативного типа имеет тип подписи, как это:

NativeType(const double &d) 

Первоначальная попытка:

public ref class ExampleWrapper 
{ 
    ExampleWrapper(array<double>^in) 
    { 
     for(int i= 0; i< in->Length; ++i) 
     { 
      NativeType test(in[i]); 
     } 
    } 
} 

Это возвращается с ошибкой и не будет компилироваться. Дальше я пробовал это

public ref class ExampleWrapper 
{ 
    ExampleWrapper(array<double>^in) 
    { 
     for(int i= 0; i< in->Length; ++i) 
     { 
      double d = in[i]; 
      NativeType test(d); 
     } 
    } 
} 

который, кажется, работает нормально. Наконец, я попытался укрепить массив следующим образом:

public ref class ExampleWrapper 
{ 
    ExampleWrapper(array<double>^in) 
    { 
     pin_ptr<double> pin_in = &in[0]; 
     for(int i= 0; i< in->Length; ++i) 
     { 
      NativeType test(pin_in[i]); 
     } 
    } 
} 

Который также, кажется, работает нормально.

Что я хотел бы знать, почему первый пример не работает, в то время как другие два, похоже, работают нормально. Кроме того, я хотел бы знать, какой предпочтительный метод использовать.

ответ

1

Почему первый пример не работает?

Это не удается, потому что вы просите компилятор взять адрес управляемой переменной (один двойной внутри двойного массива) и передать ее неуправляемому коду, но это незаконно.

Почему проходит второй пример?

Поскольку d не находится в управляемой памяти, это локальная переменная в коде оболочки. Вы можете инициализировать его из массива управляемой памяти, поскольку вы принимаете только значение (двойное), а не адрес.

Почему проходит третий пример?

Поскольку вы привязываете массив к фиксированной ячейке памяти, и теперь его можно безопасно передать в неуправляемый код. (P.S., думаю, вы имели в виду pin_ptr<double> pin_in = &in[0];)

+0

Итак, возникает вопрос, что конструктор получает ссылку? Я заметил, что у меня есть аналогичный пример, где у конструктора есть сигнатура типа NativeType (int i), которая, кажется, компилируется просто отлично, с эквивалентом первого примера. – Taus

+0

Да, это будет нормально работать, поскольку вы будете передавать примитивное значение, а не адрес объекта/памяти. – Amit

2

Массив in - управляемый объект, он не имеет постоянного адреса. В любой возможный момент времени сборщик мусора может ударить и переместить объект, пока он уплотняет кучу. Вряд ли это произойдет, ведь вы вызываете собственный код, поэтому у GC нет причин запускать коллекцию. Однако он не равен нулю, другие потоки в программе могут выделяться одновременно.

Когда это произойдет, произойдет стихийное бедствие. Это const double, поэтому, по крайней мере, собственный код не может испортить кучу GC. Фактическое двойное значение, которое он читает, является случайным.

Компилятор C++/CLI может обнаружить это возможное несчастье и жаловаться. Вы должны указать стабильный адрес для двойного &.Копирование его в локальную переменную, безусловно, самый простой способ, он хранится в стеке, и эти переменные никогда не перемещаются. Использование pin_ptr <> является прекрасным решением проблемы, а очень дешевый способ привязать управляемый объект. Он лишь устанавливает бит в таблице, который генерирует дрожание, что помогает GC обнаруживать ссылки, хранящиеся в локальных переменных и регистры CPU. Это только что-то стоит, когда происходит фактическая коллекция, CLR обнаруживает ее при выполнении стека, чтобы найти ссылки.


Передача двойной & довольно странно, очень важно, чтобы вы посмотрите на машинный код. С ожиданием, что вы обнаружите, что он сохраняет ссылку вместо сохранения значения. Это очень большая проблема, обходные решения, которые вы нашли, достаточно хороши для жизни вызова конструктора. Собственный код будет читать мусор, когда он использует ссылку позже.

Затем вы должны создать устойчивую ссылку, которая остается в силе за вызов конструктора, использовать родной новый оператор в вашей обертку и хранить его в поле. Очистите его в финализаторе, еще раз, проверив, что собственный код больше не может разыменовать его. Обычно это заканчивается, когда ваш финализатор также уничтожает объект родного класса.

+0

Фактический тип в конструкторе - шаблонный тип типа. В этом случае просто бывает разрешено удвоение. – Taus

+0

Это никак не меняет мой ответ. –

Смежные вопросы