Interior Pointers

Where's the rest of the properties stuff? I was going to write about default properties in this entry (and have quite a lengthy one saved for future use), but there are a few disagreements I have with the current implementation of default properties, and I want to see those issues resolved before I discuss a feature. (I hate when I discuss something, and then it changes on me, and I have to backpedal.)

Interior Pointers. Instead, I'm going to take this opportunity to delve into interior pointers. I'd say the major difference between interior pointers in V1 and the updated C++ syntax is, in V1, you used the same __gc keyword to denote both interior pointers and handles to objects (for which we now use the carat or "hat" symbol). The confusion, for many users, is that a __gc * would actually be something different, depending on the type of object it pointed to.

#using <mscorlib.dll>
using namespace System;

__gc class A{
public:
int i;
};

int main(){
A* a = new A;
int __gc *p = &(a->i);
*p = 10;
}

Seems simple enough. The confusion, for most users, is why they can't take that p and pass it to some unmanaged function or static library and perform the same sorts of operations. The reason, though, is actually somewhat obvious, when you consider it. The type handle a is pointing to the GC heap - a managed, garbage-collected heap. At any time, the gc can fire up, and perform collections and compactions.

Compaction? The collection doesn't worry us so much - it's the compactions we need to worry about. The GC is allowed to move our objects around during compaction. The runtime then updates the active type handles to continue to point to the proper place. What about types that the runtime doesn't know anything about? They're not updated, plain and simple. That's why the runtime has its own type of pointer that gets updated. Native C++ pointers know nothing about the GC, and frankly, that's the way we want it. To protect the user, we prevent the creation of native pointers to memory in the GC heap (except for pinning pointers - which I'll discuss in a later post).

What's the new syntax? The new syntax is similar to template smart-pointers (such as std::auto_ptr or shared_ptr from the boost library). The interior pointer template name is interior_ptr<T> and it's located in the namespace cli, along with array, pin_ptr, and safe_cast. The above sample would look something like:

using namespace System;
using namespace cli;

ref class A{
public:
int p;
};

int main(){
A^ a = gcnew A;
interior_ptr<int> p = &(a->p);
*p = 10;
}

What's the point of interior pointers? I was having a discussion with a coworker while preparing this post, and that question came up, and we were both dumbfounded for a split second. The answer came to me, but it took a little while: the number one use in Whidbey is probably by-ref parameters. If I want a by-ref parameter that modifies the value of an int that's passed to it, the best type to use is an interior_ptr. The main reason is that native pointers to T can convert to interior_ptr<T>, so I can pass by-ref parameters from almost anywhere into that function, and have it modify the value whether the type is on the heap or on the stack. For example:

using namespace System;
using namespace cli;

ref class A{
public:
int i;
};

void applyTen(interior_ptr<int> p){
*p=10;
}

int main(){
A^ a = gcnew A;
int i;
applyTen(&i);
applyTen(&a->i);
}

If the parameter to applyTen was an int* as opposed to an interior_ptr<int>, the types wouldn't match for the second call. In a future article, I'll delve into the pinning pointer.