Just What Is This TR1 Thing?

Just What Is This TR1 Thing?

  • Comments 54

Hi there. Rob Huyett here again. I’m an SDET on the VC Libraries team. One of the things I’ve been working on lately is the new TR1 add-on for Visual Studio 2008. When VS 2008 TR1 support was announced about a month ago, I’m sure that the reaction varied from “Woo-hoo! I can’t wait!” to “Huh? What’s TR1? Do I care about this?” and all sorts of things in between.

This post is mostly aimed at that second category of folks – the ones who hadn’t heard about TR1, and who maybe haven’t found the time or inspiration to seek out what it’s all about. I’ll talk briefly here about just what TR1 is, and I’ll describe just a few of the TR1 features that I think are particularly nifty. This isn’t going to be an in-depth discussion or how-to manual, but it will hopefully inspire someone out there to take a closer look at what’s in TR1 and see if it can help you in your projects.

So what is TR1? Well, TR1 (“Technical Report 1”) is a set of proposed additions to the C++0x standard. Most of it will likely find its way into the new standard, but in the meantime it provides a useful stepping stone toward C++0x. TR1 is full of very useful new utilities such as new types of smart pointers (called “shared_ptr” and “weak_ptr”), new containers (tuples, unordered maps, unordered sets, and a neat STL-like array), reference wrappers, regular expression support, and function wrappers. You might look at that list and think that much of it sounds familiar. Smart pointers and function wrappers, for instance, already exist. This is true, but TR1’s versions try to be easier to use and more useful than the existing stuff… sort of like the next iteration based on a few years of experience finding out what works and what doesn’t work.

Alright, on to some specifics! First, I’d like to mention the new TR1 tuple and array classes. Then I’ll talk just a bit about shared_ptr. I’ll finish up with some information about regex, TR1’s regular expression utility. Again, people who are familiar with TR1 probably won’t get much out of this, but it will hopefully whet the appetite of those who are new to TR1.

Tuple is very much like the existing pair class, except that it can hold up to ten items instead of just two. Just like you can have pair<INT, char> p;, you can have something like tuple<INT, char, int, double, char*> t;. Handy, no?

TR1’s array class is very much like a fixed-length STL vector. Vector is a very useful class, and it is probably sufficient (even preferable) for most array-type needs. However, there are some situations where the developer is absolutely positive that the array needed will always be a particular size… no more, no less. In these cases, the variable-size feature of vector is not needed and just adds extra overhead. While you could just use a regular old C-style “square-bracket” array, the TR1 array class lets you use all of the STL-type iterators and algorithms. While it lacks some of the flexibility of a vector, it opens up more options than are available with a C-style array.

Shared_ptr is a very easy-to-use tool that greatly simplifies memory management. It all but makes new/delete combinations obsolete. Shared_ptr is a smart pointer class that is pretty easy to use. The syntax is fairly simple… shared_ptr<STRING> sp(new string(“foo”)); creates a shared_ptr called sp to a string containing “foo”. This shared_ptr will act almost just like any “normal” pointer, except that you don’t need to remember to delete it when you’re done. And unlike some older smart pointers, you don’t need to modify the target class (in this case, string) to include reference counting or anything like that… nearly any class you want will work with shared_ptr as-is. Speaking of reference counting, shared_ptr takes care of that (hence the “shared” in the name). If I were to make another shared_ptr that points to the same thing (like shared_ptr<STRING> sp2 = sp;), then shared_ptr’s reference counting is smart enough to only free up the memory when BOTH sp and sp2 are gone. Of course, this barely scratches the surface of what shared_ptr is all about, but it’s a start.

Regex is a class that lets you write complex regular expressions like those commonly used in Perl. While C++ has always had some amount of support for regular expressions, TR1’s regex utilities simplify things by building in the mechanics for parsing, matching, and capture groups. The regex class holds the actual regular expression, and algorithms such as regex_search(), regex_match(), and regex_replace() make it easy to apply that expression to a string. As you can probably deduce from the algorithm names, regex_search() tells the developer if the string contains any substrings that conform to the expression, regex_match() tells if the entire string conforms to the expression, and regex_replace() provides an easy way to change the string to fit a particular format. Regex can do quite a bit more than I’ve outlined here, but this should give you an idea of what regex is all about.

Well, that’s about all I wanted to say here. If any of this kindles some new interest in TR1 in any of the vcblog readers, great! Of course, any comments or questions that you might have will be appreciated.

Rob Huyett, VC Libraries Team

  • [Stephan - What you call "delegates" in the managed world, I call "bound functors". TR1 binders are psychotically powerful. You just have to learn a different way of looking at things]

    I know. But don't you think the compiler couldn't do it more efficiently, regarding performance of the code and compilation ?

    Doesn't mean that boost/TR1 implementation is bad, I only have the feeling that this is a basic feature, which should be implemented in the core language (too). The libraries are growing and growing, basically everything could be exported to a library, even a for loop. Yes, regarding compatibility libraries have true advantages, but TR1 needs highly compliant compilers anyway.

    But anyways, very good work guys - keep it up. And (have) a good and happy new year.

    [Just in case it happened: sorry for double posting]

  • [Andre]

    > I know. But don't you think the compiler couldn't do it more efficiently

    I do not think that the compiler could do it more efficiently. There is nothing magical about binding arguments to a functor, and TR1 should generate code which is as efficient as hand-written C++.

    It so happens that the STL and TR1 are very high performance, although VC9's optimizer isn't perfect (data members frustrate the inliner).

    > I only have the feeling that this is a basic

    > feature, which should be implemented in the core

    > language (too).

    C++'s core language is already packed with features, and you want to add *more*?

    There are very important things that need to be added to the core language which will improve expressiveness (variadic templates) and performance (rvalue references). Bound functors can already be implemented well in a library. C++ doesn't need first-class functions like Scheme because classes can imitate functions - in fact, C++ encapsulates modifiable state better. And C++ doesn't need delegates like managed languages because it has more powerful library support (templates, templates, and more templates).

    Now, it turns out that true lambda functions can't be implemented well in a library (Boost.Lambda was a good try, demonstrating the difficulty), so I'm eager to see them be added to C++0x. Merely binding arguments to existing functions is a simpler case, though.

    > The libraries are growing and growing

    As they should!

    Actually, C++'s standard library is small compared to those of other languages (because the C++ standard library has a different focus). It has room to grow, as TR1 demonstrates.

    > basically everything could be exported to a

    > library, even a for loop.

    Like std::for_each().

    There's a reason that I'm picky about terminology - "delegate" sounds special and fundamental, while "bound functor" sounds like a modification of a special, fundamental thing (and indeed, functors are powered by core language features - operator overloading, templates, and the like).

    There *are* compromises in TR1 that really want to be part of the core language, but can't in a library-only addition. result_of is the best example.

    Stephan T. Lavavej

    Visual C++ Libraries Developer

  • [Stephen]

    "If you think that these features are valuable, and aren't convinced by arguments otherwise, then you should make your concerns known - but to the relevant people. Commenting on posts by library testers like Rob (or library devs like myself) is highly unlikely to get you anywhere."

    Actually the Visual C++ leadership team reads all the comments on all the vcblog entries. We take these comments into account as one of the factors when we make decisions on the evolution of the product.

    Ronald Laeremans, Product Unit Manager

  • [Stephan]

    "Boost is special because some of its libraries still attempt to provide limited support for VC6 (the Infinite Enemy of modern C++ programming"

    IMHO, Microsoft did a great job in improving the C++ compiler since VC6 age. I don't like very much the VC6 C++ compiler, but I do love the VC6 IDE!

    I think that one of the reasons of VC6 success is that you in Microsoft developed a *great* IDE for VC6: the IDE is snappy and robust (it was so also in the old days of 64 MB of RAM and Pentium 150 MHz, it's not that VC6 became fast only with big powerful hardware of these days).

    The edit-and-continue does work.

    The help system integrated in VC6 does work: you find what you are looking for (it's not like the help system of VC7 or lather... that I prefer using Google to find things on MSDN :(

    VC6 + Visual Assist X + WndTabs is a great development experience, IMHO.

    There was a very interesting thread on Mr. Somasegar's blog about VC6 and VC++ improvements, and I believe that you already read that:

    http://blogs.msdn.com/somasegar/archive/2007/08/08/visual-c-futures.aspx

    So, what I just ask to VC++ Team is to continue the *great* work they have alrady done with the C++ compiler and libraries improvments, but also work like this with the IDE.

    Best wishes for the new year!

    Giovanni

  • [Stephan - I do not think that the compiler could do it more efficiently]

    Ok. I'll take your words ;-).

    Currently a simple member function call with 1 parameter in boost needs 30 assembly instructions with 3 jumps.

    The same call in C++/CLI needs 5 assembly instructions and 1 single jump.

    (And it's managed code)

    [Stephan - it has more powerful library support (templates, templates, and more templates)]

    Once I've been in love with them and I've been a hardcore C++ developer using them even for meta template programming. But since I've seen that I can be (much much) more powerful and have a higher abstraction level with code generation on a higher level I don't find them that >worthwhile< anymore.

    Andre

  • [Ronald Laeremans]

    > Actually the Visual C++ leadership team reads all

    > the comments on all the vcblog entries.

    Aha!  I didn't realize you read everything.  That is cool.

    (For those following along at home, Ronald is my great-grandboss, the manager for all of VC.  Our org actually has a new shiny name now, but that's basically what it is.)

    [Sys64738]

    > IMHO, Microsoft did a great job in improving the

    > C++ compiler since VC6 age. I don't like very much

    > the VC6 C++ compiler, but I do love the VC6 IDE!

    You and everyone else on the planet, as far as I can tell! :-)

    The IDE team has definitely gotten your feedback, and they're working hard to make VC10's IDE better. We recently got T-shirts with the slogan "10 is the new 6", heh.

    [Andre]

    > Currently a simple member function call with 1 parameter

    > in boost needs 30 assembly instructions with 3 jumps.

    > The same call in C++/CLI needs 5 assembly

    > instructions and 1 single jump.

    Hm, that's not good.  What exactly are you doing?  Are you simply binding a member function to an object and invoking it later (this would look like bind(&foo::bar, obj, arg)), or are you introducing a boost::function into the mix?

    Send me a self-contained repro at stl@microsoft.com, and I'll see how VC9 TR1 handles it. We might be able to do something on the libraries side better, or at least I'll be able to file an optimizer bug. Fundamentally, native code should never be less efficient than managed code.

    I do know that the optimizer can't see through data members (e.g. the one that powers mem_fn()), and I've already got a bug open about that. This might be the same thing, or something different - either way, it'll be good to get a repro.

    Stephan T. Lavavej

    Visual C++ Libraries Developer

  • [Stephan]

    "The IDE team has definitely gotten your feedback, and they're working hard to make VC10's IDE better. We recently got T-shirts with the slogan "10 is the new 6", heh."

    That is *great* news! :)

    And I do like that slogan!

    Thanks,

    G

  • [Stephan - Hm, that's not good.  What exactly are you doing?]

    Sorry, my fault. I wasn't aware that boost::function has that much more overhead as direct binding. Forget one of the main performance rules, first look at the implementation ;-).

    Without boost::function everything is as it should be:

    The C++ optimizer even removed the call. Quite hard to write short test/reproduction code, if most of the code is removed ;-).

    Thank you for the hint and sorry for the misinterpretation.

    Can't (now) wait to get hands on TR1 and are much excited about the next "10 is the new 6" release *g*.

  • [Stephan - Hm, that's not good.  What exactly

    [Andre - Sorry, my fault]

    Sigh, forget my last post. Got myself confused and no bind was involved. I need a function object, to write a generic callback function, correct me if I'm wrong.

    Sample:

    class test { public: void cb(int i) {} };

    test t;

    boost::function<void (int)> foo = boost::bind<void>(&test::cb, t, _1);

    foo(100);

    This C++ code needs the 30 assembly instructions. While the comparable C++/CLI code:

    ref class Test { public: void cb(int i) {} };

    delegate void CB(int i);

    Test^ test = gcnew Test();

    CB^ foo = gcnew CB(test, &Test::cb);

    foo(100);

    Needs "only" 5 for the function call.

    Did I got something (again) wrong, or is the managed version really more efficient ?

  • Stephen and the VC++ team,

    I advocated keeping your project free, when possible, of dependencies on external libraries to reduce the project's cost and risk over its life cycle.  This is shooting for a 5+ year life cycle between major upgrades or rewrites.  

    This is much more true for non-GUI code than it is for GUI code.

    The compiler and libraries have made significant security improvements in the CRT since 6.0.  Much more useful changes include better compiler diagnostics and run time security checks.

    I appreciate MS's effort in VC++ since 6.0.

    New features/libraries are not put into production use for about the first year for the same reason why our production machines do not get an OS upgrade until at least service pack 1 is released.

    This works out OK because our release cycle is about 6-8 months so we can develop using new features a few months before production use.

    Our environment is such that new tools, libraries, build tools, plug-ins, etc. need to get approval from our technical advisory team before being used.

    This is the result of the ease of which each developer brought in his favorite set of open source/low cost tools for his application during 1996-2005.  This turned over with each developer and about every two years.  This proliferation and staff turnover led to us having tens of tools that products relied on but no-one had seriously used, were unsupported or nearly dead.  FWIW, we have C++, C#, VB6, VB.NET, .NET 1.x, 2.x, classic ASP, ASP.NET, C as languages for production systems.

  • [Andre]

    > Sorry, my fault. I wasn't aware that boost::function

    > has that much more overhead as direct binding.

    This is a fundamental consequence of how boost/tr1::function works. It "forgets type" (wrapping a functor of arbitrary type with a given signature, inside a type that depends only on the signature), as if by using virtual functions, but virtual functions are incompatible with inlining. Even when virtual functions themselves aren't used because of other overheads, you're still going to incur some penalty.

    > boost::function<void (int)> foo = boost::bind<void>(&test::cb, t, _1);

    > Did I got something (again) wrong

    Yes, you're unnecessarily using a boost::function.

    (Note that you can say boost::bind(stuff) instead of boost::bind<void>(stuff) here, although it doesn't make a difference in terms of the generated code. I suggest avoiding explicit return types unless they are necessary.)

    To clarify:

    boost/tr1::function's purpose is to provide "insulation" by forgetting type. Ordinarily, the only way to make an algorithm accept functors of arbitrary type is to template the algorithm on the functors, but that can lead to lots of template instantations. Some things make it even more difficult - consider an object that wants to be constructed from a functor of arbitrary type. Now you'd need to template the object on that functor type, and anything taking that object needs to be a template too (if full generality is desired), etc.  (Think of boost::thread here.) Also, containers have to be homogeneous, so you can't fill a container with functors of different types.

    Instead, you can insulate these functor-taking algorithms and functor-containing objects/containers from the exact types of the functors by introducing a boost/tr1::function.  Now, your algorithms and containing objects can be non-templates (or, at least, not templated on that functor type), since the type variability is handled by the boost/tr1::function.  This introduces a small overhead (after all, you're forgetting type - "dynamic" languages do this all the time so it's easy to forget about, but C++ is capable of both compile-time and run-time polymorphism, and only the latter forgets type and introduces overheads), but it can be very useful and lead to efficiency gains elsewhere (e.g. because now you have only one implementation of the algorithm, which fits into instruction cache, etc.).

    boost/tr1::function has a second purpose, but this one is not fundamental. It's a convenient way to store a functor whose type is inconvenient to say. In C++03, the return types of mem_fn() and especially bind() are difficult to say. You could dig into the implementation, but you'd end up with nonportable types (boost::detail, or Dinkumware's _Bind, etc.). You could use tr1::result_of, but it would be kind of complicated (bind() is a template function, so you'd have to give it explicit template arguments). It's a lot easier to just store the return value of bind() in a boost/tr1::function.  However, that introduces overheads.

    C++0x auto/decltype is the true solution for dealing with types that are inconvenient to say. Otherwise, you have a couple of choices:

    1. Use result_of. However, if you're using any placeholders (i.e. you're doing incomplete binding), I don't think this will work. As far as I can tell, there's no way to say the type of _1, _2, etc. portably (without C++0x decltype). TR1 doesn't provide a placeholder_type<N> (in VC9 TR1, the types are _Ph<N>, but this is nonportable). This is a very minor oversight on the part of TR1 (and since C++0x has auto/decltype, there's no point in filing a Library Issue).

    2. Hand the result of bind() to a template function.  This is easy:

    template <typename F> void foo(F f) {

       // do stuff with f

    }

    foo(bind(stuff));

    foo() will know the exact type of F, whatever it is, so you'll make the inliner happy by not introducing any yucky virtuals. You may have to take the functor by reference instead of by value in order to avoid copying (C++03 does love copying stuff).

    TR1 is, of course, an intermediate step in the evolution of C++ - it makes a lot of things easier to write, but some things require the full power of C++0x. Until then, awkward workarounds will occasionally be necessary.

    Stephan T. Lavavej

    Visual C++ Libraries Developer

  • [Stephan]

    Thank you for the clarification and a happy new year.

    My intention was it to decouple the sources and not using templates, besides for boost::function and to bind the member function.

    As you already wrote, templates have a high coupling factor and they restrict other goodies to work, e.g. Intellisense.

    Handing the bind result to a template function helps me not that much IMHO, because I cannot store the pointer and since I don't want to use the additional lambda goodies in this case I could also use a template function without bind:

    template <typename F, typename T>

    void C(F f, T& p)

    {

      (p.*f)(100);

    }

    Auto won't perhaps help too, because it can't be used AFAIK as a class member, since it must derive the type from a return type of a function.

    So, I think we agree here, for the "simple task" of storing a member function, not bound to a special class type, in an ordinary non templated class I have to use boost::function ?

    Currently simply speaking the managed implementation is 6 times more efficient regarding code complexity on the assembly level as the native one.

    We will see if variadic templates and other C++0x features will help to make the code more efficient.

    I'm still not convinced that a core implementation of a delegate type *and* the library extension in combination couldn't be more efficient. In the managed version it obviously is. I'm only nitpicking on this, because C++ always claims to be (most) efficient in code generation.

    ----------------------------------------

    By the way: I had a look at the preprocessor output of my sample code:

    Managed: 46      lines

    Native:  121000  lines

    And the native output has many, many empty lines, just wondering (in case that's not only for some kind of optimization and other devs responsible for the preprocessor read this post).

    Andre

  • [Andre]

    > As you already wrote, templates have a high coupling factor

    What?

    Templates *decouple* code by generalizing over types.

    > and they restrict other goodies to work, e.g. Intellisense.

    That's Intellisense's deficiency.

    > I could also use a template function without bind

    This is similar to a handwritten binder, which I suggest as another workaround.

    The problem with handwritten binders is that they involve more typing (they aren't particularly brittle, just tedious). The problem with bind() in C++03 is that it's difficult to say its return type. (The absence of perfect forwarding is another problem, but not relevant here.) If you can't use bind(), fall back to handwritten binders.

    > Auto won't perhaps help too

    decltype will.

    > So, I think we agree here, for the "simple task" of storing

    > a member function, not bound to a special class type, in an

    > ordinary non templated class I have to use boost::function ?

    No.

    > Currently simply speaking the managed implementation is 6

    > times more efficient regarding code complexity on the

    > assembly level as the native one.

    That's because you're not performing a proper comparison.

    > C++ always claims to be (most) efficient in code generation.

    Only when you write efficient code to begin with.

    > I had a look at the preprocessor output of my sample code

    Templates go in headers, and the Standard Library has a lot of templates. Also, for ease of implementation, VC's Standard Library headers include each other more often than if they tried to avoid doing so.

    (I thought about it some more, and I don't think result_of can be used to work around the absence of auto/decltype here.)

    Stephan T. Lavavej

    Visual C++ Libraries Developer

  • I wish compiler diagnostic and debugger support for tr1 will be also on high level. I don't want to land in the middle of some macro expansion during debugging some tr1:function/bind code.

    Does the tr1 in vc9 use the same preprocessor tricks as boost does?

  • [Stephan]

    [What? Templates *decouple* code by generalizing over types]

    Perhaps I've not expressed me correctly. Coupling factor - meaning if I want to store a templated type, which has a template parameter, in a class as member, I have to make the class containing this type templated too.

    Since there's no real separation between declaration and implementation for templated classes I have to include the implementation too.  

    Ever tried to separate template based code via pimpl or ship libraries of template classes with code and implementation separated, where the implementation code is compiled to a library / dll ?

    In the sense of a template type can be everything you are right.

    So let me rewrite my post:

    Templated classes tend to make classes containing this classes templated too, if the template type is or cannot be specified directly.

    [That's Intellisense's deficiency]

    Don't think so. For Intellisense to function constraints are needed. Otherwise how should Intellisense know of which type the template paramter is, when I write the implementation code of the template ? It could be any type.

    [decltype will]

    Not with an ordinary non templated class. Or can you give me an illustrative example - for the code below.

    [No]

    So please give me an example - rewrite the following code:

    class CallbackHolder

    {

      boost::function<void(int)> myCallback;

    };

    By replacing boost::function with auto or decltype and without converting the class CallbackHolder to a templated one.

    With a delegate type directly supported by the C++ core I could write:

    class CallbackHolder

    {

      delegate<void(int> myCallback;

    };

    [That's because you're not performing a proper comparison]

    What would be a proper comparison ? I want to use the style boost::function allows me to use. I want to use it in a simple non templated class and perhaps export this class and ship the implementation code compiled to a dll.

    [Only when you write efficient code to begin with]

    And if C++ allows me to do ;-).

    [Templates go in headers, and the Standard Library has a lot of templates]

    Ups. Templates should decouple better ;-)

    [I thought about it some more, and I don't think result_of can be used to work around the absence of

    auto/decltype here]

    But you could give me / us a sample for my class CallbackHolder anyways, how you would write it by using auto/decltype

    *without* using templates.

    Andre

    Senior C++ software engineer

Page 2 of 4 (54 items) 1234