Freigeben über


The Concurrency Runtime and Visual C++ 2010: Automatic Type Deduction

Last time, we looked at how to use lambda expressions in Visual C++ 2010 to make the Concurrency Runtime even easier to use (see The Concurrency Runtime and Visual C++ 2010: Lambda Expressions). This week we’ll take a quick look at the auto keyword, which is another language feature that can enhance your productivity.

We’ll first look at the verbose way to declare a template type that is provided by the Asynchronous Agents Library. We’ll then use template argument deduction to refine this declaration. Finally, we’ll use the auto keyword to make the declaration super-succinct.

Consider the following example that uses the Concurrency::make_join function to create a Concurrency::multitype_join object. The multitype_join class represents a message block type that combines messages from multiple source buffers.

    1: // explicit-creation.cpp
    2: // compile with: /EHsc
    3: #include <agents.h>
    4: #include <vector>
    5:  
    6: int wmain()
    7: {
    8:    using namespace Concurrency;
    9:    using std::vector;
   10:    using std::tuple;
   11:  
   12:    single_assignment<bool>       b1;
   13:    unbounded_buffer<double>      b2;
   14:    overwrite_buffer<vector<int>> b3;
   15:  
   16:    multitype_join<tuple<
   17:       single_assignment<bool>*, 
   18:       unbounded_buffer<double>*,
   19:       overwrite_buffer<vector<int>>*>> j = make_join<
   20:                                           single_assignment<bool>*, 
   21:                                           unbounded_buffer<double>*,
   22:                                           overwrite_buffer<vector<int>>*>(&b1,
   23:                                                                           &b2,
   24:                                                                           &b3);
   25: }

Although templates are a powerful way to enable you to make use of reusable libraries, the declaration of j (lines 16-24) was rather tedious to write. It’s also more difficult to visualize at a glance what’s going on. Thanks to template argument deduction in the Visual C++ compiler, we can rewrite this example to eliminate the template argument specification on the right-hand side of the assignment expression:

    1: multitype_join<tuple<
    2:    single_assignment<bool>*, 
    3:    unbounded_buffer<double>*,
    4:    overwrite_buffer<vector<int>>*>> j = make_join(&b1, &b2, &b3);

Although this revised example eliminates the need to specify the template types on both sides of the assignment expression, it’s still somewhat tedious to write. If you’re familiar with implicit typing on other languages (such as var in C#), it seems reasonable that the Visual C++ compiler can also infer the types of local variables based on their initial value. In fact, Visual C++ 2010 supports the use of the auto keyword to support implicit typing.

Sidebar: Although it was rarely used, you may have already seen auto used in C++ code. Before Visual C++ 2010, auto was used as a storage-class specifier. – End Sidebar

Here’s a final example that revises the previous one to use the auto keyword to entirely eliminate the need to provide type arguments in the assignment expression:

    1: auto j = make_join(&b1, &b2, &b3);

There you have it! The auto keyword is a convenient way to declare instances of container types and iterators in the Standard Template Library and the Concurrency Runtime. It can also help you use functions that take a type that is either difficult or impossible to express explicitly. For example, the Concurrency::structured_task_group class requires you to manage the lifetime of the work function for each task. However, if you use a lambda expression to define this work function, you cannot know its type because lambda functions have anonymous type. Therefore, you can use the auto keyword when you declare the lambda expressions, like the following (you can find the full example here):

    1: // Use the make_task function to define several tasks.
    2: auto task1 = make_task([] { /*TODO: Define the task body.*/ });
    3: auto task2 = make_task([] { /*TODO: Define the task body.*/ });
    4: auto task3 = make_task([] { /*TODO: Define the task body.*/ });
    5:  
    6: // Create a structured task group and run the tasks concurrently.
    7:  
    8: structured_task_group tasks;
    9:  
   10: tasks.run(task1);
   11: tasks.run(task2);
   12: tasks.run_and_wait(task3);

Although auto is a great time saver, overuse can cause your code to become hard to read. I try to limit its use to when I work with complex template types or when I need to create a lambda expression variable. For more information and examples of how to use the auto keyword, see auto Keyword (Type Deduction) and Stephan’s post Lambdas, auto, and static_assert: C++0x Features in VC10, Part 1.

 

Next time we’ll look at another language feature in Visual C++ 2010 that works great with the auto keyword.

Comments

  • Anonymous
    March 01, 2011
    "Thanks to template argument deduction in the Visual C++ compiler, we can rewrite this example to eliminate the template argument specification " You make it sound as if VC++ did something new while this feature is part of langage for past 14 years at least. auto is the only new thing in this entire barrage of words.

  • Anonymous
    March 01, 2011
    Tanveer, I actually think Thomas' point was to show how to use templates and the new auto keyword with the new concurrency runtime objects provided in VS2010.  This might not be obvious to everyone and meta-programming is still a stretch for some, so this is a nice way to show how you can use all these features of the language together. I would hope that we are all striving to write "beautiful code".  (AKA, easy to read, understand and maintain while also being efficient and scalable.)  Sometimes complex datatypes provided by template libraries such a ppl.h and agents.h can result in hard-to-read code.  Thomas showed us how to use "auto" to make it better. Thomas, are there limitations to implicit typing that we should know and avoid?  Are there traps or pitfalls?

  • Anonymous
    March 11, 2011
    Thanks Tanveer and Danulf for your comments! Tanveer, my apologies for not being clear about template argument deduction. What I wanted to show was a progression from having a very verbose declaration to how you can simplify it before Visual C++ 2010 to how you can simiplify it even more in Visual C++ 2010 by using the auto keyword. Danulf, to answer your question, one thing that comes to mind is overuse of the auto keyword. For example, if you use it to declare all of your variables, then your code might become difficult to read later -- especially when the size of your variables has implications on how your code performs. The MSDN documentation for auto (msdn.microsoft.com/.../dd293667.aspx) also lists some of the restrictions on its use. Understanding these errors might help guide you as you write future code.