[C++ CLI] почему нельзя брать адрес pin_ptr'а на нативный объект?

psihodog

Кто-нть может пояснить смысл этой фразы из MSDN'а:
Taking the address of a pin_ptr that points to a native object results in undefined behavior.
?

klyv

Имо, потому что он не в управляемой куче и закреплять его незачем.

psihodog

И что? У обычных объектов из неуправляемой кучи тоже после этого нельзя брать адрес что ли?
И, кстати, pin_ptr'ы в куче нельзя располагать:
Pinning pointers can only be declared as non-static local variables on the stack.

bleyman

Может, потому что его нет? Типа, соптимизён нафиг?
Я понятия не имею, как оно работает, но допускаю, что дико виртуально и синтактически-сахарно.

bleyman

И, кстати, ты уверен, что правильно понимаешь, о чём речь вообще? Адрес у пойнтера андефайнд, а не у объекта, на который он указывает.

klyv

работает оно ни разу не виртуально и не сахарно, как я предполагаю.
с момента присваивания такому указателю чего-то и до выхода из текущего блока кода (мы же только в стеке можем быть) объект, на который указывает _он_, оказывается фиксирован в управляемой куче.
соответственно, если туда пихать указатели на объекты не из управляемой кучи, получится неизвестно что, потому и непредсказуемы последствия.
в более дружественном к .NET языке C# есть специальная конструкция - fixed, которая делает тоже самое, но при этом явно ограничивает область своего действия оператором (или блоком следующим за "заголовком".

psihodog

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

A pin_ptr represents a superset of the functionality of a native pointer. Therefore, anything that can be assigned to a native pointer can also be assigned to a pin_ptr.
не понятно только, почему нельзя брать адрес этого пин_птра и именно когда в него запихнут неуправляемый объект.

psihodog

Может, потому что его нет? Типа, соптимизён нафиг?
я думал, что оптимизация не должна оказывать такое влияние на спецификацию.
так можно дойти до того, что ни у каких объектов нельзя будет адрес брать: вдруг соптимизят! :grin:

psihodog

И, кстати, ты уверен, что правильно понимаешь, о чём речь вообще? Адрес у пойнтера андефайнд, а не у объекта, на который он указывает.
Ну, вроде понимаю... Адрес у объекта указателя вполне себе дефайнед, вроде.

Dasar

не понятно только, почему нельзя брать адрес этого пин_птра и именно когда в него запихнут неуправляемый объект.
Какой будет тип у этого указателя?
кстати есть уверенность, что pin_ptr - это именно указатель, а не "объект" с несколькими полями?

psihodog

> Какой будет тип у этого указателя?
pin_ptr<type>* ?
> кстати есть уверенность, что pin_ptr - это именно указатель, а не "объект" с несколькими полями?
Как раз наоборот, есть почти уверенность, что это объект. Хотя бы потому что у него деструктор нетривиальный.

Dasar

pin_ptr<type>* ?
и что ты будешь делать с таким указателем?
> Как раз наоборот, есть почти уверенность, что это объект. Хотя бы потому что у него деструктор нетривиальный
тогда получается, что имея указатель на этот объект ты можешь легко порушить его состояние.
зы
самое главное, а для каких операций тебе нужен указатель на pin_ptr?
так может и хотелось, чтобы ты задумывался, когда такие операции хочешь провести над pin_ptr?

psihodog

> и что ты будешь делать с таким указателем?
какая разница? какое это имеет отношение к сути вопроса?
> тогда получается, что имея указатель на этот объект ты можешь легко порушить его состояние.
да я вообще много чего гадкого могу сделать: записать случайные байты по случайному адресу, но никакая спецификация CLI C++ этого не запрещает.
> самое главное, а для каких операций тебе нужен указатель на pin_ptr?
да кто тебе сказал, что он мне вообще нужен? =)
просто запрет этот выглядит довольно странным.
не очень понятно, как на этапе компилляции определить, на какой объект указывает pin_ptr: managed или unmanaged.
было бы здорово, если бы определённость поведения при выполнении операции не зависела от логики программы.
или хотя бы было разумное объяснение, почему этого нельзя добиться.
я такого объяснения не вижу на данный момент, поэтому мне кажется, что я чего-то не понимаю.
вот я и хочу понять, чего.
ответы типа "навига тебе это нужно?" к пониманию не приводят как-то...

bleyman

Disclaimer: я понятия не имею, о чём вы тут говорите.
Мне кажется, что в манагед С++ присутствует определённая шизофрения, которую нужно учитывать. Манагед код компилится и исполняется в CLI. Там pinned pointer не отличается от обычного референса ничем, кроме того, что он pinned (то есть помечен как таковой в какой-то внутренней табличке GC но можно вызывать его методы специфическим образом, так же, как они вызываются для value types — когда this просто указывает на кусок памяти, без каких-либо метаданных. Понимаешь?
А вот указатель на анманегед ресурсы — это уже совсем-совсем не референс. То есть внутри он такой же указатель, но если ты стайпкастишь его к настоящему pinned_ptr, то тебя могут ожидать страшные сюрпризы. Потому что pinned_ptr может обращаться к GC за разъяснениями, а эта штука будет вести себя непредсказуемо. Эммм... Хотя причём тут его личный адрес... Прозреваю, что дело всё-таки в оптимизации. Довольно многие люди объявляли некое никому не нужное поведение неправильным, implementation dependent и всё такое из соображений оптимизации!
Вообще я в этом страшно неуверен и написал только с той целью, чтобы тред остался наверху и какой-нибудь гуру C++CLI зашёл и всё объяснил!

psihodog

> можно вызывать его методы специфическим образом, так же, как они вызываются для value types
Тут поподробней, плиз. Какие методы? И в чём специфичность образа их вызова?
> this просто указывает на кусок памяти, без каких-либо метаданных.
Я думал, что value-типы отличаются от reference-типов только расположением в памяти и, соответственно способом доступа к ним.
А метаданные там есть так же, как и в референсных типах.
> Вообще я в этом страшно неуверен и написал только с той целью, чтобы тред остался наверху и какой-нибудь гуру C++CLI зашёл и всё объяснил!
+1 :grin:

bleyman

Тут я знаю, как оно работает, поэтому могу ответить.
У reference-type указатель указывает на сложную структуру в которой есть указатель на тип (то есть на другую сложную структуру). Это и в плюсах так, кстати. То есть если ты напишешь
object zzz = new System.Int32(42);
после чего попытаешься посмотреть на память, на которую реально указывает локальная переменная zzz, то увидишь там множество всякой фигни, кроме числа 42. Которая нужна для того, чтобы ты потом мог zzz куда-нибудь передать и там вызвать GetType (виртуально унаследованный от Object). Да, если чо, таким образом созданный инт является reference-type. Он, типа, boxed.
У value-type ничего такого нет, указатель указывает сразу на данные. Потому что (или поэтому) наследоваться от value-types нельзя. То есть все вызываемые методы резолвятся в конкретные адреса на этапе компиляции, а передаётся им в качестве нулевого параметра указатель на данные.
То есть в случае value-types ты получаешь видимость ООП, хотя из четырёх его свойств реализуется только одно, инкапсуляция. Зато удобно и быстро, что уж тут. Типа, синтаксический сахар: у тебя где угодно есть четыре байта и ты вместо вызова статического метода, которому они передаются, вызываешь как бы метод класса (которому они точно так же за кадром передаются, но ты этого не видишь).
Вот. Мне кажется, что где-то тут и скрыт ответ на твой вопрос. Что, типа, если всё известно на этапе компиляции, то открываются широчайшие просторы для оптимизации — вплоть до того, что память под pinned_ptr не будет выделена вообще. А зачем она? У тебя уже есть указатель, который ты передал в конструктор pinned_ptr, нафига его дублировать? И, в случае если это был managed указатель, компилятор корректно перенаправит обращения к нему (в том числе и взятие адреса) к тому реальному указателю. А специально для корректной обработки случая unmanaged указателей никто код писать не хочет.

bleyman

И, кстати, relevant вопрос, который меня довольно давно мучает.
Managed указатель это пойнтер или хэндл? То есть, это реально адрес, или инт, являющийся индексом ячейки в какой-то внутренней хештейбле, в которой лежит адрес? Типа, GC же периодически компактит память, ему, наверное, было бы сложно отслеживать все указатели на данный кусок, чтобы их исправить, гораздо легче исправить один раз в той хэштейбле. За счёт не очень больших тормозов в рантайме, потому что используемые в данный момент элементы можно как-то помечать, чтобы их GC не попортил, а если захочет попортить, то найдёт где их изменить во всех остальных местах, а тогда реальные адреса можно эффективно кэшировать.

psihodog

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

ну... хочет-не хочет, а писать всё равно придётся. =)

pin_ptr<int> p;
if (use_managed)
p = &managed_i;
else
p = &unmanaged_i;
*p = 500;
Оставить комментарий
Имя или ник:
Комментарий: