Welcome to MSDN Blogs Sign in | Join | Help

Recently launched at http://blogs.msdn.com/vcblog, the VC team is attempting an almost frightening level of transparency (in my opinion).  Customer comments I've read so far have ranged from mildly disinterested to "I'm never going to use your product again."

Brandon is fond of saying: "Transparency is painful."  It gives people a glimpse into how we're attempting to get and stay organized, and what agility does to a bug bar.  The cuts we're making are harsh, and I know that people won't be pleased to see their Connect-filed feedback sometimes not being fixed, but my hope is that the vcblog can help explain why.

There are some interesting things coming up, and the Tex has made some feints in that direction, but I don't think anyone is ready to show their hands yet.  We're close, though - and when the river card hits, we'll see if going all-in made sense.  Do we have the nuts, or are we just nuts?

Expect a post from me on vcblog (if they'll let me) explaining some of the rationale of our big shift - if I can.

Disclaimer.  This is an ancient post.  By the looks of it, I originally intended to write this almost a year ago, as a follow up to my scalar properties writeup.  That was back when I was testing properties (and more exactly, default properties) and some of the design was still in flux.  Now it's pretty nailed down (there's very little time to change it for Whidbey, anyhow), and it dawned on me that I hadn't posted this material.  So, I'll do it now, with a bit of fixup.

The Way C# Does It.  Reader Rob Walker asked over a year ago:

Why do I have to specify the type of the property 3 times in the definition? It makes this new syntax more verbose than the old.  Why not just adopt the C# style?

Let me start by mentioning that I'm not a member of the design team.  I've been included on various discussions that take place regarding the syntax, but I haven't been the one making the decisions.  I can only make guesses as to their reasonings (or ask them).  Either way, the reasoning is my own.

I believe there are two reasons why we wouldn't adopt the C# style.  First, there's an existing property syntax that users are familiar with.  The changes between the two syntaxes are somewhat minor.  Second, the C# property syntax doesn't fit well with C++ paradigms.  Though simple, I imagine the syntax could wreak havok on parsers, and could introduce a number of ambiguities in the language.  One of the goals of adding CLI to C++ was to not break existing paradigms, where possible.  Finally, the designers wanted to do what felt natural to C++ users.  C++ users aren't familiar with functions that have no parameter lists, that's unusual.

Speaking of how C# does it, here it is:

public class ArrayWrap {
  public ArrayWrap(){ arr = new int[10]; }

  public int this[int idx] {
    get{  return arr[idx]; }
    set{  arr[idx] = value; }
  }

  private int[] arr;
}

And here's code to produce the equivalent class in C++:

public ref class ArrayWrap {
public: 
  ArrayWrap(){ arr = gcnew array<int>(10); }

  property int default[int] {
    int get(int idx){  return arr[idx]; }
    void set(int idx, int value){ arr[idx] = value; }
  }

private:
  array<int>^ arr;
};

We stole the default keyword and co-opted it for use in indexed properties.  Note that you still make use of the property keyword.  When this comes out in IL, it looks almost exactly like the C# indexer - with the default property being named "Item" and the getters and setters also thusly named.  You can change the name of the default property by using the attribute System::Reflection::DefaultMember on the class.  The compiler sees this attribute and interprets it to mean that the default member you created in the class should be named whatever you pass in the attribute ctor.

A weird IL trick leads to another way you can generate a class with a default indexer using that attribute.  In IL, a property is a default indexer if it meets two requirements: 1) it is an indexed property, and 2) its name matches the name in the DefaultMember attribute.  So, you can actually generate a default member in C++ using code like this:

[System::Reflection::DefaultMember("Foo")]
public ref class ArrayWrap {
public:  
  property int Foo [int] { ...  }

However, I do not recommend this approach, except as a possible way to workaround bugs, or to obfuscate for fun, as the C++ compiler will not recognize Foo as a default property when compiling, only when importing.  Also, although it is legal in IL, I do not recommend using this trick to make default scalar properties.  That's just unpleasant.

In a future posting, I'll go over the various ways you can access properties, which may come in handy as workarounds.

Back from the dead

Well, not precisely dead, but I certainly began feeling that way - shipping a product is hard work, and it is incredibly easy to get "heads down." Here on the VCQA team, we're very focused on stabilization. A lot of testruns. Harsh bug bars. Only the highest quality fixes. Beta 2 is out the door, and it's time to tighten up for the endgame.  As we're fond of saying: the product has 2005 on it.  By my count, we've got about 6 months to ship a product.

About that DF thing. While composing what was to be my final post on this whole DF topic, I ran into a brick wall. I couldn't seem to explain how to tie it together. It didn't fit well enough with the Dispose(bool) pattern. There were too many caveats - too many special cases that the user had to handle, both from the authoring end and the consuming end.

The new, new DF is more straightforward. When I heard about it, I was glad. Glad as a user - as a tester, when I hear "redesign," it sounds an awful lot like "here's a bunch of extra testing work, and some of those cool tests you wrote can go in the wastebasket." Even though the redesign impacted my already heavy workload (my dentist: "Do you know you're grinding your teeth at night?"), I was happy to see we were doing what I felt was right.

The executive overview is this : we implement the Dispose(bool) pattern for you, and hook it up in the best way possible to your base. (If you have one.)

Here, I'm only going to delve into the first part; that "best way possible" stuff requires a complex decision tree that I don't have the energy to iterate over right now.

We still have the same general design: ~T is basically your destructor, which we implement with Dispose(void), and !T is basically your finalizer, implemented through Finalize(void). What we've added to the mix is Dispose(bool).

Here's how I would write what the compiler does for you:

Dispose(bool disposing) {
 
if(disposing) {
   
GC::SuppressFinalize(this);
   
//call your ~T code
  } else { /* call your !T code */ }

  //contingent upon what your base class looks like we may call
  __super::Dispose(disposing);
}

The major difference between our Dispose(bool) and the one suggested by the CLR Dispose pattern is the else. This is mostly because the user writes Dispose(bool) in the CLR version - in the C++ version, the compiler authors it, so it is left up to the user how to handle it. (The best thing to do, usually, is to invoke the finalizer from the destructor, unless you *really* know what you're doing.)

Typically, you release Disposable managed items from your destructor and unmanaged items for your finalizer. I was confused by this at first.  For some reason, I thought it would be the reverse. But eventually, I understood why.

An object allocated on the GC heap has a few potential invocation states for cleanup: user-invoked, intentional finalization, and unintentional finalization. Intentional finalization is easiest, so we'll tackle it first.  Sometimes, the cleanup of an object isn't a priority. If you're just dealing with managed heap space, for example, the GC is great at that. It prioritizes, finds the right times to go about cleaning up, and is quite efficient at it.

As to user-invoked cleanup; when dealing with a scarce or untracked resource (e.g. hunks of unmanaged heap space, or database connections), you typically want to free up those resources as soon as you know you're done with them. This is why you want objects with Disposers: it's good engineering practice to let go of scarce resources ASAP.  That's why you want to provide them to your users.  However, not all of the people using your type may get this (or care) and - this is important - there's no way in the CLR to get them to care. You can't force it. Heck, you yourself are bound to forget now and then.

So we need to handle unintentional finalization, to make sure we don't leak. In your Finalizer, you clean up those resources that wouldn't be cleaned up on their own. And those are the only resources you should clean up in a Finalizer.  You should read that again, it's an important distinction in my mind.

Finalization is NOT deterministic. When your finalizer is invoked by the CLR, you can't be certain that any finalizable objects you have references to have not already been finalized. Trying to Dispose something that's already been finalized is dangerous.

With all these strong admonitions and specifics, a good example is due - look for that in an upcoming post.

Astute readers may note two things: 1) It has been a while since I posted what should have been a followup to the previous posts, wherein I complete my discussion of the C++ DF model, and finally get this DF monkey off my back.  And 2) that a previous post (DF Part 2, where I actually explain how the C++ DF model works) has been temporarily removed.

Many reasonable scenarios exist to explain these phenomena.  Suprisingly, the truth has very little to do with the Bermuda Triangle.  I believe the C++ DF model may be undergoing some revision - and it would be a Bad Thing to leave up a post on what could be an outdated (and incorrect) description of the model.

When the language design team (a team I must stress I am an observer of) has Finalize()d their design, I will recreate post #2 anew, and the final post on DF will also see the light of day.  I can't say much about it yet (not until I've had a chance to play around with it), but from what I've seen, I'm very pleased with the new design.

So, I'm on board with the new design, if for no other reason than the fact that my final post (how the C++ DF model and the CLR Dispose pattern) was nearly impossible for me to write under the previous design.  Not because I'm a poor writer :), but because the previous design left the developer with some very tough problems to handle elegantly.  This new design is far more elegant, to my eyes.

While we wait on that post, I may take some time later this week to mull over some general issues that have been bothering me recently.  In the meantime, here's a juicy article by Herb (who is a member of the language design team) on a subject very close to our current lines of discussion.

1 Comments
Filed under: ,

Long ago, I wrote a post on the first part of DF benefits.  Now, I'm finally getting back to it.  My apologies about the laxness in posting.  Blame it on my Cards losing to the Sox.  And on being really busy with testpasses and bug bounces for a while.  We're finally settling down a little bit, so that gives me time to pull out the keyboard and do some more blogging.

Last time, we discussed ref types on the stack, and the benefits they provide.  But, if you can put ref types on the stack, where else can you put them?  Try this one on for size:

using namespace System;

ref struct A{ ~A(){ Console::WriteLine("~A()"); } };
ref struct B{
  A a1;
  A a2;
};

int main(){
  B b;
}

Compile and run that, and you'll see ~A() output twice.  Now, you can have ref types as class members, and we call the destructors for you automatically.  Dig this, they can even be static!  The same rules as native C++ apply all over the place.  If I want to call a constructor with arguments, I can do it inside the ctor initializer list.  And object unwinding works as well.

Okay, so maybe I didn't need to split this into a separate posting.  I could have bundled it, and divluged this little nugget of coolness weeks ago.  In any case, next time, I'll wrap up this whole DF business with a discussion on how our DF model works with the CLR Dispose pattern.  (See part 1, or some CLR persons' blog for more on the CLR Dispose pattern.)  After that, I'll probably get into copy ctors on ref types, or maybe my new favorite subject: hidebysig.

3 Comments
Filed under:

I'm pretty angry at blogs.msdn.com right now (or maybe I'm just angry at myself), as it completely nuked a post I had composed, because my session had timed out on it.  I went to post, and it asked me to log in, and in the process destroyed a lot of work.

I'll try to put my frustrations out of mind, and continue with my increasingly tardy discussion of C++ DF.  I've already discussed the CLR Dispose pattern, and the C++ destructor syntax.  Originally, I'd planned for this third part to be about interaction between those two models, but I think I'll save that for later, and instead start talking about the benefits of C++ DF, by way of discussing a comment on my previous blog post.

A bit of discussion about Dispose brought up an interesting point - in C++, now, we've disabled the ability to call the Dispose function directly.  Instead, you call it thusly:

ref class MyClass{
public:
  ~MyClass(){} //overrides IDisposable::Dispose()
};

int main(){
  MyClass^ mc = gcnew MyClass();
  //do stuff

  mc->Dispose(); //error!
  mc->~MyClass(); //legal, calls Dispose()
  delete mc; //also legal, also calls Dispose()
}

The options left open to the user are, I think, a little more familiar.  However, there's a large caveat included in this discussion.  Imagine, in that innocuous comment titled "do stuff," that I throw an exception.  I haven't handled it in my code snippet, so I'm likely to run into issues.  The most obvious way to handle this would be to wrap our instantiation in a try/finally:

int main(){
  MyClass^ mc = gcnew MyClass();
  try {
    //do stuff, maybe even throw an exception
  } finally {
    delete mc; //dispose, regardless of execution path
  }
}

That works, but it's likely to get nasty very quickly when you have multiple disposable objects, as you should be nesting try/finallys (to guard against exceptions in an object Disposer, for example).  C# has a nifty piece of syntax set up to handle this, the using statement (not to be confused with the using directive).  You can scope out the code example in that article for more information on it.  In C++, however, we've developed our own bit of syntactic sugar to handle disposable objects cleanly: ref types on the stack.  With a nod to the example above, here's a quick look at ref types on the stack:

#using <System.Drawing.dll>
using namespace System::Drawing;

int main(){
  Font f1("Arial", 10.0f);
  Font f2("Courier New", 8.0f);
  //do stuff with these fonts
} //Dispose called automatically, even with exceptions

This is a syntax that should be intimately familiar to C++ users - what's more, if you look at the IL, it actually behaves the way they will expect.  That is, your disposer gets called even if an exception is thrown during the "do stuff" phase.  Ref types on the stack aren't really anything special, they're still a ^ underneath, it's just a lightweight "shim," with a few added bonuses.  However, this syntax does represent a slight problem - there are no BCL methods that take a font-on-the-stack, because this whole "on the stack" thing is a C++ convention.

To handle this, (pun intended) we introduced the % (Anders-of) operator.  This basically gets you the handle underneath our implementation.  So, you can have a ref type on the stack, and still pass it to functions that expect a handle to that type.  It's used exactly like the & (address-of) operator, which is pleasantly analogous, in light of the & reference type and the % tracking reference type.

#using <System.Drawing.dll>
using namespace System;
using namespace System::Drawing;

void foo(Font^ f);

int main(){
  Font f1("Arial", 10.0f);
  Console::WriteLine(f1.FontFamily);
  foo(%f1); //foo requires a Font^
}

Why not use something similar to the boxing conversion?  (Is it some kind of coincidence that my post on boxing was also apparently nuked at some point?)  Anyhow, there's a very good reason for not using a boxing-style conversion, and it's something new we've implemented for Whidbey C++: deep copy semantics.  (That is, copy constructors for ref types.)

We're starting to stray outside the bounds of this conversation, however.  We'll save copy ctors for another day.  Next time, I'll get into more benefits of DF.  Also still to come: how C++ DF interacts with the CLR Dispose pattern.

6 Comments
Filed under:

A large subject like DF needs a few posts.  My generalized plan to lay it out will start by describing the CLR's Dispose pattern, how our DF pattern works, and finally how the two patterns fit together.

The CLR's Dispose patterns can be quite confusing.  It took me a while to get my mind around Dispose() and Dispose(bool), and I'm still not sure I fully understand it.  But, I'm going to take a crack at it.  Most of what I know about Dispose/Finalize patterns comes from two sources; talks with Brandon, and this excellent whitepaper on the subject.  It's wordy, but it will do a far better job explaining Dispose than I can, and it will talk about more Dispose patterns than I plan to.  In fact, it might be a good idea to read that whitepaper, and then return here, to see how my take on it differs from your own.  I'll take this opportunity to remind you that this is my opinion of the way things are, not the way they really are.

The usefulness of Disposing is primarily for freeing unmanaged resources.  In a nutshell, the Garbage Collector really only gets invoked when there's memory pressure.  But the GC can't "feel" pressure from unmanaged resources.  If you have a bunch of objects hanging around that have gobs of natively allocated memory (C++ new, or Marshal.AllocHGlobal), the GC won't know about this memory pressure, and won't begin freeing up these objects.  (It may notice the free memory being reduced, but it has no sense of which objects are holding the memory.)  The aforementioned whitepaper also talks about database handles, file handles, and message queue handles as other forms of unmanaged resource.

When creating .NET Disposable types, you usually want to create a Finalize (C# destructor syntax), Dispose(), Dispose(bool), and inherit from System.IDisposable.  Almost all of your actual object cleanup will occur inside Dispose(bool)That's the pattern they came up with, and it's a good way to keep all your cleanup in one place.  We'll get to cases in a second, but first, here's a hunk of C# code plaigarized from the whitepaper:

public class BothType: IDisposable{
  public void Dispose(){
    Dispose(true);
    GC.SupressFinalize(this);
  }

  ~BothType(){  //finalizer
    Dispose(false);
  }

  protected virtual void Dispose(bool disposing){
    if(disposing){
      //'Disposable'
 managed resource cleanup code 
    }
    //Unmanaged resource cleanup code
    
base.Dispose(disposing);

  }
}

When Disposing, the first call is always to Dispose().  Biggest take-away from one of my talks with Brandon.  All disposing starts with that call.  The CLR way of doing "chaining" destructors is the manual Dispose(bool) pattern.  Basically, the user calls Dispose(), and inside that function, Dispose(true) is called.  This does a callvirt on Dispose(bool), which will thunk down to the lowest child instance.  Then, you have the manual base.Dispose(disposing) calls that will walk up the chain.  I had to step up to my whiteboard for a while to prove to myself that this pattern works - but it does.

The Finalizer is a backup system in this paradigm, only there to ensure that your unmanaged resources get cleaned up.  That is, if a Disposable object is being Finalized, it's probably a mistake - the user forgot to Dispose().  So a call to Finalize() yields a call to Dispose(false), which takes care of only those unmanaged resources it holds, those that the GC is unable to clean up.

Note: a call to Dispose(false) should only clean up the unmanaged resources.  If we're finalizing, we have no discrete order for finalization.  If I try and Dispose objects the GC is aware of within a finalizer, I could be in for a world of pain, because those objects might already be collected.  Finalization is not deterministic.  There was an MSDN TV episode where Brad Abrams tells an antecdote about a bug where they were calling Console.WriteLine() in their Finalizer and getting a NullReferenceException, because the Console object had already been finalized.  Talk about hard to track down...

You clean up your managed resources inside the if(disposing) scope.  Note that in that scope, you should only clean up your Disposable member objects.  Don't bother with objects that don't have a Disposer, those objects will be cleaned up just fine by the GC.  It's a trade-off you make between those objects you need to get rid of, to free up scarce resources, and not having to clean it all up right away.  Like at home; I might do the dishes on a weeknight (especially if I've run out of ramen bowls), but I don't go vaccuuming the floor every time I walk on it.

The call to GC.SuppressFinalize() shouldn't be overlooked - it ensures the Garbage Collector won't Finalize an object that's already been Disposed.  In general, you're either going to Dispose an object, or the GC will Finalize it, not both.

Though this is the most flexible cleanup model, it isn't the only cleanup model for objects in the CLR.  The whitepaper goes over most of them.  The simplest case is where your object has no Disposable members, or unmanaged references.  ("Simple" objects.)  No Disposers or Finalizers needed there, the CLR will clean everything up for you.  Or, your object might only have non-scarce resources and the aforementioned Simple objects, in which case you might have a Finalizer, but not a Disposer.  But, if you've got only unmanaged resources, or both unmanaged and managed resources, you probably want both a Disposer and a Finalizer, where the Finalizer is more a safety net than intended use.  The whitepaper also talks about the case where you might want only a Disposer.  While this case does exist, I think it should be used with caution.  If you or your user forgets to Dispose() such an object, your unmanged resources could be hanging around for a very long time.

Coming in Part II, I'll discuss the C++ DF model, and how it fits with the CLR's Dispose() pattern.  (Depending on how long it takes, that discussion might be split into two parts.)  I'm also planning on a Part III (or IV), where I'll talk about the C# using declaration (see 2.4 of the whitepaper), and how C++ tackles the same problems.

10 Comments
Filed under:

Reader Andy Neilson writes in with another bug:

The current compiler implementation has some problems. If the variable is a field of this, then the compiler will die. For example:

class MyClass {
public:
  int i;

  void Foo() {
    array<int>^ x = {1, 2, 3};

    for each (i in x) {
      if (i == 2) break;
    } 
  }
};

This is also a bug.  I'll file it in our bug tracking database.  I'm not sure what the design decision will be; it feels like we should disallow the construct you show - but I'm not certain.  I found a "sort-of" workaround; you can use a tracking reference to an integer to do it, such as:

void Foo() {
  array<int>^ x = {1, 2, 3};
  int% a = i;
  for each (a in x) {
    ...
  }
}

Thanks for the bug report!

1 Comments
Filed under:
Reader Rob Walker asks:
Is there a neat way of handling dictionaries?  I have a Dictionary<Guid, Object^> and want to iterate over the values.

Currently I have to use the syntax:

for each(KeyValuePair<Guid, Object^> v in dict)
{
  v.Value ...
}

I can't find an invocation that would allow

for each (Object^ v in dict->Values)

(This is using the Whidbey beta 1 compiler refresh).
I imagine, Rob, that you're getting what I'm getting from the compiler, which is:
   error C3285: for each statement cannot operate on variables of type 'overloaded-function'
 
That just doesn't seem right to me.  In fact, I made an incredibly simple case, and tried it with a very recent compiler:
using namespace System;
 
ref struct R{
 property array<int>^ p; 
};
int main(){
 R^ r = gcnew R;
 for each( int i in r->p ){}
}
And this also gives me that same error.  I talked with the guy responsible for testing for each, and he confirmed my suspicion - that's a bug.  Good catch!  I've filed it in our bug tracking database, and shot it over to the proper developer.  (With a note that this bug came from a customer - which always raises bug priority.)
 
In the meantime, there's a pretty simple workaround.  You should be able to get around this problem by calling the property accessor method directly.  In my case, that'd be r->p::get(), and in your case, it'd be dict->Values::get().  That should allow you to use for each on that collection.
 
By the way, I'm sort-of touching on bits of the property syntax which I haven't discussed yet in this blog.  I'll try to get back to that subject after DF.
2 Comments
Filed under:

For Each?  I won't go into a huge justification - suffice to say, there are some instances where it is nice to be able to iterate over a set, and perform operations on each member of that set.  A good primer might be the MSDN node on C# foreach.

A basic sample.  If you're familiar with C++ for statements or the C# foreach statement, the C++ for each statement should be fairly familiar.  I'll steal from a C# sample, and convert to C++:

using namespace System;

int main(){
  array<int>^ arr = gcnew array<int>{0,1,2,5,7,8,11};
  int even=0, odd=0;

  for each (int i in arr) {
    if (i%2 == 0) 
      even++;      
    else
      odd++;         
  }

  Console::WriteLine("Found {0} Odd Numbers, and {1} Even Numbers.",
    odd, even);
}

Pretty simple.  There are some definite advantages to for each.  Most importantly, it beats struggling with iterators and IEnumerables almost any day of the week.  There are also some interesting things you can do with for each.  For example, you can do fun things like for each(Char c in "abcdefg").  Go crazy with Generic Collections.  And, here's a code sample that I know will make some of you out there salivate:

#include <vector>
#include <iostream>
using namespace std;

int main() {
  int total = 0;

  vector<int> v(6);
  v[0] = 10; v[1] = 20; v[2] = 30;
  v[3] = 40; v[4] = 50; v[5] = 60;

  for each(int i in v) {
    total += i;
  }
 
  cout << total << endl;
}

Here's the kicker: you can compile the above natively.  Why throw in the CLR if you don't want to?  Of course, it compiles just fine with the CLR, but for each works on any STL-compliant container in native C++.  Now that's a compiler improvement I can get behind.  Next time, I'll take a first look at DF.  I think it's going to take me a few posts to get it sorted out.

15 Comments
Filed under:

Hot on the heels of my article on interior pointers, comes a much more insightful one by Stan Lippman on the same issue.  That happens sometimes.  I enjoyed the chat we had on the VC++ 2005 Beta, and I wanted to point that there are two other online chats coming up.  One is on upgrading COM apps to .NET, and the other is on the library and runtime enhancements in VC++ 2005.  They're lumped together with other chats that might be of interest on this page.

On to pinning pointers.  Sometimes, the interior pointer simply won't suffice.  Say I have a function I really need to access in a native code module.  For example, I have a native function that loads an array for me, and I want to use it to load a managed array.  Sounds like a complicated task, and it can be.  Conventional wisdom tells us that the native function can't get access to the managed array, because it's on the GC heap, and could move around all over the place.  I can't use interior pointers because this is native code.  So it seems the onlyh choice left is to make another native array, call into the native function, and then loop through the native array, copying the members over from it to the managed array.  Clunky, to say the least.

There's a better way?  You bet.  It's called the pinning pointer.  In Managed Extensions, we exposed it using the keyword __pin.  In the new syntax, we expose it through another smart-pointer-ish type, pin_ptr<>, located in the cli namespace (the namespace formerly known as stdcli::language).  What the pinning pointer does is "pin" our managed object down on the GC heap, preventing the garbage collector from moving it around.  In addition to this pinning, it gives us what we need; a conversion to a native pointer.

Though pinning pointers are cool, they are sometimes not well understood.  The best way to think of them is that an object is pinned so long as a pinning pointer points to it.  That's important enough a concept that you ought to read that sentence again.  I'll wait.  What this means is that your pin_ptr object is only pinning something on the GC help while its in scope.  When it goes out of scope, it stops pointing to that object, and that object can be moved at any time.  That means you can't go saving a native pointer, and expect your pin_ptr to hold it forever. 

Dangers of pinning pointers.  In fact, because of the dangers of misinterpretation, we severely restrict where and how you can use pinning pointers.  They can't be involved anywhere temporaries are created, can't be the argument to or the return type from a function, can't be members of a type, and can't be involved in casts, to name a few.  But this is C++, and what would C++ be without a way to shoot yourself in the foot.  Long ago, I wrote an article that included some warnings about the pinning pointer.  The examples are in Managed Extensions, but the concepts are pretty solid.  Rather than repeat myself, I'll just link to it, and request that you read it.  Especially that example of how to make a quick and easy GC hole.

Enough talk, let's see an example.  Right.

using namespace System;
using namespace cli;

void myUnmanagedFunction(wchar_t* p, int size){
  for(int i=0; i<size; i++)
    p[i] = 'A'+i;
}

int main(){
  array<Char>^ arr = gcnew array<Char>(10);
  pin_ptr<Char> ppC = &arr[0];  //implicit conversion from interior to pin pointer
  myUnmanagedFunction(ppC, 10);
 
  for(int i=0; i<10; i++)
    Console::Write(arr[i]);
  Console::Write("\n");
}

Putting it all together can be complicated.  Brandon helped me sort it out one day by drawing a helpful diagram, which I'll replicate here.

Don't quit your day job.  I know, I'm not much of an artist.  Think of the arrows as "can convert to."  Note that for orthogonality, native pointers can convert to pin and interior pointers.  Hey, it's sometimes useful, you'll be glad it's there.  That's it for pin pointers.  In a future article, I might look at our upcoming for each syntax.

4 Comments
Filed under:

We're having an online VC++ chat this coming Thursday.  I and several of my coworkers from all areas of the product (IDE, front-end, back-end, etc.) will be available for questions.  If you're interested in attending, here's the announcement they asked us blog hosters to post:

Join the Visual C++ team to discuss your questions and comments on the Beta
release of Visual C++ 2005. Whether you are a first-time user of the Visual
C++ Express Edition Beta (
http://lab.msdn.microsoft.com/express/visualc) or
an experienced developer exploring the full Visual Studio 2005 suite, we
want to answer your questions to provide you with a smooth development
experience. So please bring your questions and issues to our attention and
get the answers you need from the Visual C++ team.

August 19, 2004
12:00 - 1:00 P.M. Pacific time
3:00 - 4:00 P.M. Eastern time
19:00 - 20:00 GMT

Chat time for cities world-wide:
http://www.timeanddate.com/worldclock/fixedtime.html?day=19&month=8&year=2004&hour=12&min=0&sec=0&p1=234&sort=1

To add this chat to you calendar:
http://msdn.microsoft.com/chats/outlook_reminders/MSDN_VC2005B_Aug19_04.ics

For more info on MSDN chats, including other upcoming developer chats, chat
archives, and other info see http://www.msdn.microsoft.com/chats.

0 Comments
Filed under:

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.

8 Comments
Filed under:

What are properties?

Technically, properties are CLR "aliases."  They are exposed as standard methods, and any compiler that consumes them simply transforms the user's code into the proper function calls.  Similarly, any compiler that wants to author CLR properties just needs to follow the naming convention rules, and provide the necessary metadata entries.

The property appears as a red triangle in ildasm.  In the shot below, note that the functions implementing the property are not hidden or obfuscated.  Indeed, languages that don't support the property can still call the underlying functions.

The Managed Extensions property syntax

__gc class MyClass{
  __property int get_MyProp(){ ... }
  __property void set_MyProp(int value) { ... }
}

int main(){
  MyClass* mc = new MyClass();
  return mc->get_MyProp();
}

By using the keyword __property, and providing a getter and/or a setter (using the naming convention get_PropName), a user was able to signal to the compiler that they wanted the extra property information generated.  When a user wanted to call a property, they would simply use the get_ and set_ functions directly.  This fit in the C++ syntax neatly, but it wasn't exactly first-class.  Whereas other languages could get the length of an array by saying MyArray.Length, we were limited to MyArray->get_Length().

The new C++ property syntax

In the Whidbey syntax, the property is defined as a block, similar to how C# handles properties.  The general syntax uses the context-sensitive word property, followed by the general type of that property, followed by the name.  Curly-braces then define the "scope" of the property block, and inside, you're allowed to define get and/or set functions that match the expected signature.  (The C# MSDN node on properties provides more context on how C# exposes properties.)

ref class MyClass{
  property int MyProp{
    int get(){ ... }
    void set(int v){ ... }
  }
};

Calling properties is also similar to how C# handles them, with a few caveats.  In general, you should be able to call a property via its alias name, as in int i = myObject->MyProp, or myObject->MyProp = 10.  The compiler then transforms these calls into a getter or setter method, as appropriate.  This works on all properties, not just those you create.

More to come

In future articles, I'll talk more about what you can do with properties, including:

  • default properties / default indexers
  • overloading of properties
  • limitations of properties in C++
4 Comments
Filed under:

A reader asked the question:

Is .NET, in fact, the SAME THING as Visual Studio 7.0? Could it be possible that a developer with .NET would be able to simply open the project file and recompile without rewriting code?

.NET itself is a runtime.  Unfortunately, the word ".NET" has started creeping into a lot of our product names, including our latest Server OS, and a few versions of Visual Studio.  Visual Studio .NET is, indeed, version 7.0.  The latest public offering, Visual Studio .NET 2003 (codename Everett), is version 7.1.  The next product we're going to offer, codename Whidbey (and named externally as Visual Studio 2005), is version 8.0.

 

(I have a little more on this subject here: http://blogs.msdn.com/arich/archive/2003/12/17/44187.aspx)

 

You'd have to talk to marketing as to why we can't use release names that make sense.  Believe me, they make no sense inside the company, either.  (At least the next version won't have ".NET" in the product name.  That was starting to bug me.)

 

Furthermore, you should be able to open a VS7.0 project file in VS7.1 (.NET 2003).  Upon opening it, the IDE will ask if you want to convert the project to 7.1.  Be warned, however, that this conversion is *not* reversible.

 

As to your question about being able to take 7.0 code into other versions, and compile immediately, the answer is most usually no.  Improvements, bug fixes, and conformance all tend to hurt a compiler's backwards compatibility, and occasionally, we find it necessary to “break“ our upgrading customers in the name of progress.  We try to do this as infrequently as possible.  For help on converting from 7.0 to 7.1, I suggest you check out the breaking changes article on MSDN.

 

 

2 Comments
Filed under:
More Posts Next page »
 
Page view tracker