Hi, I'm Stephan, the Visual C++ Libraries Developer working on TR1.
Recently, I gave 3 presentations within Microsoft about the most novel components of TR1: shared_ptr, regex, and the additions to <functional> (including mem_fn(), bind(), and tr1::function). These presentations explained where to use, how to use, and how to avoid misusing these components, with plenty of examples.
Attached at the end of this post is a ZIP of the slide decks for each of the presentations, in PowerPoint 2007 format.
Each slide deck mostly stands alone, although the "shared_ptr To Noncopyable" and "shared_ptr To Polymorphic" examples require some context and additional explanation:
The "shared_ptr To Noncopyable" example demonstrates how to create a container of noncopyable resource managers. A resource is something that must be acquired from the system and then released afterwards. The most common resource is memory (acquired in C++ with new, and released with delete), but there are many non-memory resources, such as files, sockets, textures, fonts, and so forth. A resource manager is an object that encapsulates a resource, controlling its lifetime and permitting access to it. Resource managers acquire resources in their constructors, and release resources in their destructors (this is the somewhat-mysteriously-named RAII principle: Resource Acquisition Is Initialization, implying RRID: Resource Release Is Destruction). std::vector is an example of a resource manager for memory, while std::ifstream is an example of a resource manager for files. Memory resource managers are usually copyable (and assignable); chunks of memory can easily be copied and assigned. But non-memory resource managers are usually noncopyable (and nonassignable); either their underlying resources can't be copied without additional information (e.g. copying a file requires a new name), or copying them is a relatively expensive operation (e.g. textures consume precious video card memory, etc.). So, it's best to prohibit copy construction and copy assignment of non-memory resource managers. This requires programmers to explicitly copy the underlying resources when that's desired.
The problem here is that STL (and TR1) containers require ordinary value types. An ordinary value type is "something that behaves like an int"; ints are copyable and assignable. vectors copy their contained elements when undergoing reallocation, and so forth. You can't have a vector<ifstream> because ifstream is noncopyable. But sometimes, that's exactly what you want.
In this example (admittedly somewhat contrived), I'm writing a program to interleave text files. The user will have an arbitrary number of text files, and will provide the names of the files to the program. Then, the program will have to loop through the files in order, printing out the first line of each file, then the second line, and so forth. When a text file runs out of lines (they can have different numbers of lines), the program has to start skipping that file. When all the lines from all the files have been printed, the program has to stop.
Now, I could write this by reading each file into a vector<string> (storing each line), and building up a vector<vector<string> > (storing each file), and then looping through that. But that would require reading everything into memory. Suppose I don't want to do that.
Well, if I could use a vector<ifstream>, I could repeatedly loop through the vector, reading one line from each file in turn. When a file ran out of lines, I could remove it from the vector. But ifstream is noncopyable.
To solve this, I can use shared_ptr. shared_ptr can be used as a handle to a noncopyable object; the shared_ptr itself is copyable and assignable.
Slide 1/3 of the example shows the contents of 3 text files.
Slide 2/3 shows the code. To fit the code onto a single slide, I use a queue<shared_ptr<ifstream> >. I begin by reading filenames from cin (with the for-loop). From each filename, I new up an ifstream, held by shared_ptr, and push it into the queue. At the end of this for-loop, the queue contains shared_ptrs to the files mentioned by the user in order.
Then, I loop through the queue (with the while-loop) as long as it contains files to process. I attempt to read a line from the file at the front of the queue (this is the next file to process). If that's successful (the file still contains lines), I print it out. I want to keep working on that file, so I push() a copy of the shared_ptr to this file (q.front()), to the back of the queue. Then I pop() the front of the queue. (At this point, particularly attentive readers should ask whether q.push(q.front()) raises the specter of iterator invalidation; the answer is that it does not. I would have avoided this in the interests of clarity, but it would have taken more lines.) If I wasn't able to read a line from the file, then it's empty, so I simply want to pop it from the front of the queue. Then I continue with the loop.
Slide 3/3 shows the filenames being entered, and the program's output.
The "shared_ptr To Polymorphic" example demonstrates how to create a container of polymorphic objects (i.e. classes with virtual functions). Polymorphic objects should always be noncopyable, because of the danger of slicing. Inheritance means substitutability; a Derived object has to be usable wherever a Base object would be usable. However, copying presents a problem. If Base is copyable, then you can copy from a Base to a Base. But substitutability then implies that you can copy from a Derived (which is a Base) to a Base. This "slices" off any Derived behavior and state, which is almost always undesirable. Therefore, copy construction and copy assignment of polymorphic base classes should be prohibited. (You should provide a virtual clone() function if you need to copy polymorphic objects safely.)
If Base is noncopyable, you can't have a vector<Base>. You could try to use a vector<Base *>, newing up Derived objects and storing pointers to them, but that would be highly likely to leak, especially in the presence of exceptions. As with resource managers, shared_ptr allows us to manipulate noncopyable objects with copyable handles. vector<shared_ptr<Base> > is a powerful construct that can be used in many situations.
In this example (even more contrived; you can mentally substitute your favorite Base and Derived classes), I have a polymorphic base class Animal. Therefore, it has a virtual destructor, no copy constructor, and no copy assignment operator. It stores a string m_name (the name of the Animal), and has an explicit constructor taking that name. Animal uses the Non-Virtual Inheritance idiom, so it has a public, non-virtual member function named noise(), which returns "<name> says <something>". The "something" is customizable by derived classes, through the private pure virtual member function noise_impl(). (Yes, private virtual member functions can be overridden; the difference is that derived classes cannot call the base implementation, whereas they could if the base implementation were protected. As noise_impl() is pure, there is no base implementation, so the difference is theoretical - but I prefer to lock down access control unless I need looser control.)
Slide 2/4 shows three derived classes, Cat, Dog, and Pig, identical in structure except for what their noise_impl()s return.
Slide 3/4 constructs a vector<shared_ptr<Animal> >, which is then filled with a Cat, Dog, and Pig. Then I print out the noises that each Animal makes. I use the STL algorithm transform() on the vector, printing strings to cout separated by newlines. mem_fn(&Animal::noise) uses tr1::mem_fn() to adapt a pointer-to-member-function (&Animal::noise) to a function-object (what transform() wants).
Slide 4/4 shows the output, demonstrating that the virtual function calls worked as usual.
The point of these two examples is that shared_ptr is useful for much more than sharing and exception safety. shared_ptr allows you to use the STL in more situations, by wrapping noncopyable objects in copyable clothing.
If you have any questions, I'll be happy to answer them in the Comments!
Stephan T. Lavavej
PingBack from http://www.biosensorab.org/2008/02/22/tr1-slide-decks/
Please tell me that you were presenting this to C# developers and not C++ developers. Anyone who needs an introduction to these concepts should be smacked upside the head! All these "additions" have been around for years in Boost.
Not all professional C++ programmers have the opportunity to use Boost at work, nor do all of them choose to spend their free time, as I do, using Boost at home.
Additionally, these slides can be useful even to Boost-experienced programmers. For example:
1. Our shared_ptr allocator support is a C++0x feature, backported to VC9 TR1 (and Boost 1.35, which isn't out yet).
2. Understanding shared_ptr's internal representation is useful to get a feel for how it behaves.
3. It's important to know where Boost differs from TR1. Boost.Regex's grammar contains stuff that TR1 doesn't (for example \< and \>). Also, Boost.Regex supports some function calls that TR1 doesn't (see regex slide 28; I filed a Library Issue about that).
4. Boost's documentation is very good, but even users of Boost.Regex might not be familiar with regex_iterator and regex_token_iterator; my slides provide a useful overview of what they do.
Even the STL is unfamiliar to some professional C++ programmers; the best way to deal with this is education.
Stephan T. Lavavej, Visual C++ Libraries Developer
Great job Stephan. Thanks for posting this.
it is so silent about MFCNext and TR1. Do you have any information about the release date?
I would like to start using the beta but unfortunately my VS 2008 Team System Developer is not supported. The beta installs only on Professional and Team Suite.
> it is so silent about MFCNext and TR1.
That's what this post is for! :-)
> Do you have any information about the release date?
As we mentioned in http://blogs.msdn.com/vcblog/archive/2007/11/09/announcing-a-major-mfc-update-plus-tr1-support.aspx , the Feature Pack will be finalized in Q1 CY 2008, so you can expect to be able to download it soon.
> I would like to start using the beta but
> unfortunately my VS 2008 Team System Developer is
> not supported. The beta installs only on
> Professional and Team Suite.
That's a known bug - one of those things that betas are good for flushing out. While it's unfortunate that the beta wouldn't install on some common SKUs (causing lots of frustration), the good news is that the final version will install on all non-Express SKUs as we originally intended.
I'm interested in C++ and TR1. I think that due to such efforts as you are doing your c++ library is getting more powerful debugging features and robustness following C++ standard.
Anyway, to tell an expected defect I guess I write his comment.
Already I wrote a post regarding the defect in MSDN forum (Visual C++ Language)
As you know, many violations of the C++ standard in VC6.0 has been fixed from VC8.0 (or 7.0) and it make developers happy in their life.
I'd like you to look over above article. And if it is not your job or you are busy, could you tell me a person in charge?
I missed this but last Friday the VC blog guys put up some slide decks on the "new" stuff in
> I'd like you to look over above article.
I've posted a reply to that thread. To summarize: Indeed, VC8 and above accepted that code, while VC7.1 and below rejected it, but this is not a library bug. Instead, it's a consequence of VC8's introduction of iterator checking and iterator debugging, which made vector<T>::iterator into a class type (instead of T *). Your code triggers undefined behavior.
Yes, right. I have just checked it and found out the reason as you answer through debugging with VC8.0.
As you answer, in case of VC6 vector<T>::iterator is a T*. But in case of VC8 it is a class and the class has operator++ and --.
So, my failure to notice makes this thing.
But I think that to such developer like me the difference of polices between VC6 and VC8 can make a little confusion.
Anyway thanks a lot for your kindly answering with good explanation in MSDN forum.
Gidae Yeo (in Korea)
Hello Recently we shipped a beta of our MFC/TR1 Feature Pack that, naturally enough, included a large
> Our shared_ptr allocator support is
> a C++0x feature, backported to VC9
> TR1 (and Boost 1.35, which isn't out
And neither is C++0x.
I am still disappointed that MS is implementing a non-finalized standard before completing existing decade old standards. Until existing standards fully implemented, your product is simply non-conformant.
One example: in the last few years, I have *inherited* an increasing amount of code utilizing export. I would like to develop with VS, but since you do not support this standard feature, I am forced to use other compilers. Yeah, yeah, I know: "nobody needs export". Regardless, it is being used increasingly and I can not (and am usually not allowed to) rewrite all of the code.
> Hi, I'm Stephan, the Visual C++ Libraries Developer working on TR1.
You're *the* only developer working on TR1?
I wonder if all those "export" requests are trolls...
Implementing export is very difficult and a waste of time (as the only team that has ever done it admits).
Considering that only EDG-based compilers support it, it is naive to try and use export and then expect to compile on any other compiler.
By the way, I know this is Microsoft, but making the attachments available as PDF would be nice; I don't have Office 2007 or the Office viewers installed.