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

173
17 февраля 2018, 20:14

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

Ничего не понимаю, если я перегрузил оператор & в библиотеке c++-cli, то почему при попытке извлечь реальный адрес указателя, значение которого закрыто от внешнего доступа (нативного указателя void*), c# мне вдруг говорит:

Не удается получить адрес, определить размер или объявить указатель на управляемый тип ("vlc_instance").

Пример класса:

public ref class SomePtr
{
private:
    void* m_hide_ptr_;
    SomePtr(): m_hide_ptr_(nullptr)
    {
        m_hide_ptr = get_native_ptr(); // Не указатель на управляемый объект!
    }
    // static // Оставим static на всякий, вдруг кто подумает что что-то не то
    void*& operator &(const SomePtr^ value)
    {
        return value->m_hide_ptr_;
    }
    ...
}

Неужели перегрузка оператора тут не поможет?

Answer 1

Нет, то, что вы хотите, невозможно.

Вы собираетесь вернуть ссылку на поле m_hide_ptr_. Это поле, вместе со всем объектом, находится в управляемой куче, и, следовательно, может быть перемещено вместе с объектом. Хотя сборщик мусора при этом и обновляет управляемые ссылки, вашу неуправляемую ссылку он не обновит, поэтому взятие такой ссылки запрещено.

Без ссылки код компилируется:

static void* operator &(const SomePtr^ value)
{
    return value->m_hide_ptr_;
}

(ну и ещё вам следует объявить его открытым, а то им никто не сможет воспользоваться).

Но при этом ваша цель — получить адрес поля/ссылку на поле — не достигается. Повторюсь, адрес переменной в управляемой куче вам взять не дадут, а если вы каким-то извращённым способом и исхитритесь, первый же пробег сборщика мусора превратит ваш указатель/ссылку в висящий.

На уровне C# вы не можете вызвать ваш перегруженный оператор & потому, что с точки зрения C# перегружать унарный & нельзя. Поэтому C# трактует & как взятие адреса, а брать адрес управляемого объекта нельзя.

Вы можете, по идее, выкрутится, вызвав операцию напрямую:

SomePtr obj = new SomePtr();
unsafe
{
    void* p = SomePtr.op_AddressOf(obj);
}

В этом случае, вероятно, имеет смысл назвать использовать обыкновенную функцию-член. Но это, снова-таки, для случая, если ваш оператор возвращает чистый указатель, а не ссылку.

Answer 2

Перегрузка операторов тут не нужна. Нужен, вероятно, pin_ptr, в сочетании с Pinned GCHandle (который позволяет предотвратить перемещение объекта сборщиком мусора). Для этого придется немного модифицировать класс:

#include <stdlib.h>
#include <stdio.h>
typedef void* LPVOID ;
using namespace System;
using namespace System::Runtime::InteropServices;
LPVOID get_native_ptr(){
    return (LPVOID)& (L"Hello, C++/CLI world!\n");
}
[StructLayout(LayoutKind::Sequential)]
public ref class SomePtr
{
private:
    LPVOID m_hide_ptr_;
    GCHandle m_handle;
public:   
    SomePtr(): m_hide_ptr_(nullptr)
    {
        m_hide_ptr_ = get_native_ptr(); 
        m_handle = GCHandle::Alloc(this,GCHandleType::Pinned);//закрепляем объект в памяти
    }
    IntPtr GetPtr() //получаем адрес m_hide_ptr_
    {
        pin_ptr<LPVOID> p = &m_hide_ptr_;
        return (IntPtr)p;
    }       
    //(добавить деструктор, освобождающий m_handle)
};

int main(array<System::String ^> ^args)
{
    SomePtr ^ foo = gcnew SomePtr();
    LPVOID* bar= (LPVOID*)(LPVOID)foo->GetPtr();//указатель на m_hide_ptr_
    wprintf(L"%s",*(bar));//тестируем чтение
    *bar = (LPVOID)& (L"Thanks, bye!\n");//тестируем запись
    wprintf(L"%s",*bar);
    system("PAUSE");
    return 0;
}

Не знаю, имеет ли какой-то смысл данный код, но он работает.

READ ALSO
Binding для элемента Picker в Xamarin.Forms

Binding для элемента Picker в Xamarin.Forms

Добрый деньПодскажите где поколдовать что бы заработал binding для контрола Picker? Вот XAML:

189
Передача данных из C# приложения методом POST

Передача данных из C# приложения методом POST

Задача сериализовать класс в json и передать его POST'ом на серверЯ написал код и он работает но у меня ощущение что я наделал много лишнего

154
Получение информации из XML2

Получение информации из XML2

Добрый деньПытаюсь распарсить XML в Visual Studio, но не получается сделать структуру как это сделано в XML

213
C# получить ID обновлённой записи в MS ACCESS

C# получить ID обновлённой записи в MS ACCESS

Нужно в приложении на c# получить ID только что обновлённой записи в MS Access, тк

173