Interior Pointer vs Handle
-
Can someone clarify the differences between interior pointers and handles for me? As I understand it, interior pointers do what handles can plus they allow for pointer semantics (arithmetic and comparison). Interior pointers also can point to unmanaged objects and native pointers are implicitly converted to interior pointers (but not vice versa). Both seem to be able to point to interior members of managed objects (such as an array of managed objects, or a managed class with a member variable of another managed type). Neither support double indirection (ie handle to a handle). So is there anything a handle can do that an interior pointer can't? In which case I assume handles are used purely for convenience as they're far easier to read/write/work with if you don't need the extra capabilities of an interior pointer. So basically use interior pointers over handles only when you need pointer semantics or some sort of interop capability?
-
Can someone clarify the differences between interior pointers and handles for me? As I understand it, interior pointers do what handles can plus they allow for pointer semantics (arithmetic and comparison). Interior pointers also can point to unmanaged objects and native pointers are implicitly converted to interior pointers (but not vice versa). Both seem to be able to point to interior members of managed objects (such as an array of managed objects, or a managed class with a member variable of another managed type). Neither support double indirection (ie handle to a handle). So is there anything a handle can do that an interior pointer can't? In which case I assume handles are used purely for convenience as they're far easier to read/write/work with if you don't need the extra capabilities of an interior pointer. So basically use interior pointers over handles only when you need pointer semantics or some sort of interop capability?
-
iddqd515 wrote:
So is there anything a handle can do that an interior pointer can't?
It seems to be the other way around regarding iterators etc. http://blogs.msdn.com/slippman/archive/2004/08/27/221373.aspx[^]
Right, that's just making use of the pointer semantics of interior pointers. My main point of confusion right now is that my initial understanding was that handles could only point to whole CLI objects (ie not interior data members no matter their type). In which case that would be an important place where interior pointers would need to be used. However, I can make a ref class Foo with a data member of a ref class Bar and a data member of a managed value class V (each of those classes as int data members) and use a handle to point to the Bar member, the V member, or even the int members of those 2 objects. So while using a handle to the int might incur a boxing cost, its still quite possible. So the only thing I see handles unable to point which interior pointers can are native objects. This seems to say that interior pointers are only useful for when pointer semantics are required or you need them for interop which I thought for sure was not the only time you needed them. That's a little confusing considering: "CLI object references always refer to a whole object of CLI type on the CLI heap (e.g., it cannot refer to a directly held member inside a CLI reference type object, or to an object on the C++ heap)." from page 27 of the C++/CLI Design Rationale. http://www.gotw.ca/publications/C++CLIRationale.pdf :confused:
-
Right, that's just making use of the pointer semantics of interior pointers. My main point of confusion right now is that my initial understanding was that handles could only point to whole CLI objects (ie not interior data members no matter their type). In which case that would be an important place where interior pointers would need to be used. However, I can make a ref class Foo with a data member of a ref class Bar and a data member of a managed value class V (each of those classes as int data members) and use a handle to point to the Bar member, the V member, or even the int members of those 2 objects. So while using a handle to the int might incur a boxing cost, its still quite possible. So the only thing I see handles unable to point which interior pointers can are native objects. This seems to say that interior pointers are only useful for when pointer semantics are required or you need them for interop which I thought for sure was not the only time you needed them. That's a little confusing considering: "CLI object references always refer to a whole object of CLI type on the CLI heap (e.g., it cannot refer to a directly held member inside a CLI reference type object, or to an object on the C++ heap)." from page 27 of the C++/CLI Design Rationale. http://www.gotw.ca/publications/C++CLIRationale.pdf :confused:
Adding to led mikes comment, the tracking handle points to a managed object's header. It is a whole-object pointer. The interior pointer points to a manage object's data and you can use a pointer-typed interface to move from one data item to another. So, interior pointers only are used in special situations.
"We make a living by what we get, we make a life by what we give." --Winston Churchill
-
Adding to led mikes comment, the tracking handle points to a managed object's header. It is a whole-object pointer. The interior pointer points to a manage object's data and you can use a pointer-typed interface to move from one data item to another. So, interior pointers only are used in special situations.
"We make a living by what we get, we make a life by what we give." --Winston Churchill
Ok, so for the most part an interior pointer and handle can point to the same types of things (i.e., a ref class, a ref class data member of another ref class, a value type data member of a ref class, etc). The difference is that when they are seemingly pointing at the same thing, the handle is pointing at the object's header information (and thus the whole object) while the interior pointer has the specific address of the object's data itself (which is what allows it to use pointer semantics). That makes sense I think. But you say interior pointers are only used in special situations. That seems to imply that replacing handles with interior pointers in some situations is not going to work (which I didn't think was the case). Is that right? And if so, could you give me a basic example of such a situation? I'm still unclear on the implications of the quote from the Design Rationale doc. When it says handles can't point to interior data members of an object, that just means it can't point to the specific address of the data giving it pointer semantics? But it can still point to the whole object data member meaning they're perfectly useful for pointing to interior data members as long as you don't need pointer semantics and just want a tracking pointer. No?
-
Ok, so for the most part an interior pointer and handle can point to the same types of things (i.e., a ref class, a ref class data member of another ref class, a value type data member of a ref class, etc). The difference is that when they are seemingly pointing at the same thing, the handle is pointing at the object's header information (and thus the whole object) while the interior pointer has the specific address of the object's data itself (which is what allows it to use pointer semantics). That makes sense I think. But you say interior pointers are only used in special situations. That seems to imply that replacing handles with interior pointers in some situations is not going to work (which I didn't think was the case). Is that right? And if so, could you give me a basic example of such a situation? I'm still unclear on the implications of the quote from the Design Rationale doc. When it says handles can't point to interior data members of an object, that just means it can't point to the specific address of the data giving it pointer semantics? But it can still point to the whole object data member meaning they're perfectly useful for pointing to interior data members as long as you don't need pointer semantics and just want a tracking pointer. No?
using namespace System; void MultiplyArray( array^ data, int value ) { // Interior pointers only refer to the managed object's state or data. // They are initialized with an address of a managed object's data item. // They do not produce verifiable code; so, you cannot compile this code // with /clr:safe. They cannot be converted to or casted to a native pointer. // However, interior pointers can be converted to pinned pointers. interior_ptr begin = &(data[0]); interior_ptr end = begin + data->Length; while (begin < end ) { *begin *= value; ++begin; } } // Tracking references point to the whole managed object. // Using a tracking reference here does not make much sense since using a // tracking handle has the same affect in this case. void PrintArray( array^% vals ) { for each (int val in vals) { Console::WriteLine(val); } } int main(array ^args) { // Tracking handles refer to the whole managed object. // They can be initialized with gcnew. array^ values = { 1, 2, 3, 4, 5 }; MultiplyArray(values, 2); PrintArray(values); return 0; }
-
using namespace System; void MultiplyArray( array^ data, int value ) { // Interior pointers only refer to the managed object's state or data. // They are initialized with an address of a managed object's data item. // They do not produce verifiable code; so, you cannot compile this code // with /clr:safe. They cannot be converted to or casted to a native pointer. // However, interior pointers can be converted to pinned pointers. interior_ptr begin = &(data[0]); interior_ptr end = begin + data->Length; while (begin < end ) { *begin *= value; ++begin; } } // Tracking references point to the whole managed object. // Using a tracking reference here does not make much sense since using a // tracking handle has the same affect in this case. void PrintArray( array^% vals ) { for each (int val in vals) { Console::WriteLine(val); } } int main(array ^args) { // Tracking handles refer to the whole managed object. // They can be initialized with gcnew. array^ values = { 1, 2, 3, 4, 5 }; MultiplyArray(values, 2); PrintArray(values); return 0; }
Ok, thanks, that's a good example of where interior pointers can simplify things. But you could do similarly with just handles if you have an array of 'handle to typeX' even if its not quite as nice a solution. (Sorry I'm not sure how to use the code tag and ignore HTML so the array type is shown.) #include "stdafx.h" using namespace System; // Can multiply the array elements when the array type is handle to type // rather than needing interior pointers. // A little silly with int due to boxing costs but imagine with an array of // ref class objects where you do perform some useful change to each element void MulArray( array^ data, int value ) { for each(int^% i in data) { *i *= value; } } // Tracking references point to the whole managed object. // Using a tracking reference here does not make much sense since using a // tracking handle has the same affect in this case. void PrintArray( array^ vals ) { for each (int val in vals) { Console::WriteLine(val); } } int main(array ^args) { // Tracking handles refer to the whole managed object. // They can be initialized with gcnew. array^ values = { 1, 2, 3, 4, 5 }; MulArray(values, 2); PrintArray(values); return 0; } Now I understand why you'd want your version in some cases where you're being passed an array^ that you didn't choose to create. In theory you could create a new array (of array^) to copy the array^ to use with my version if you wanted to avoid interior pointers and make the code verifiable. I'm just trying to draw a solid line in my mind dividing the uses of handles and interior pointers. Like I said (and you just showed), they can be useful for pointer semantics. But you can still find a way to avoid them and use handles to point to interior members. So are the only time they're absolutely necessary are when you must have pointer semantics and in some interop scenarios (where native pointers can convert implicitly to interior pointers but not handles)? They can simplify some situations where you're pointing to interior members of a ref class but you can still get around doing so via handles (even if it may not be as elegant and performant as the interior pointer solution).