Tech Talk

C++ Standards Library Report

by Reg. Charney

I have just returned from Santa Cruz where we held the semi-annual Standards meeting on C++. I spent most of my time in the Library Working Group discussing new features for the next Library Extensions Technical Report. I have reported on some of the main topics below.

Move semantics

Move semantics promises to significantly increase run-time efficiency of many library elements — in some cases by a factor of 10+. The proposal has generated a lot of discussion been for years and is finally bearing fruit. It originates in the Core Working Group but mainly affects the Library. Changes are 100% compatible with existing code, while introducing a fundamental new concept into C++.

This proposal augments copy semantics. A user might define a class as copyable and movable, either or neither. The difference between a copy and a move is that a copy leaves the source unchanged. A move on the other hand leaves the source in a state defined differently for each type. The state of the source may be unchanged, or it may be radically different. The only requirement is that the object remain in a self consistent state (all internal invariants are still intact). From a client code point of view, choosing move instead of copy means that you don't care what happens to the state of the source. For plain old data structures (PODs), move and copy are identical operations (right down to the machine instruction level).

This paper proposes the introduction of a new type of reference that will bind to an rvalue:

struct A {/*...*/};
void foo(A&& i);    // new syntax

The && is the token which identifies the reference as an “rvalue reference” (bindable to an rvalue) and thus distinguishes it from our current reference syntax (using a single &).

The rvalue reference is a new type, distinct from the current (lvalue) reference. Functions can be overloaded on A& and A&&, requiring such functions each to have distinct signatures.

The most common overload set anticipated is:

void foo(const A& t); // #1
void foo(A&& t);      // #2

The rules for overload resolution are (in addition to the current rules):

Examples:

struct A {};
A source();
const A const_source();
// . . .
A a;
const A ca;
foo(a);              // binds to #1
foo(ca);             // binds to #1
foo(source());       // binds to #2
foo(const_source()); // binds to #1

The first foo() call prefers the lvalue reference as (lvalue)a is a better match for const A& than for A&& (lvalue -> rvalue conversion is a poorer match than A& -> const A& conversion). The second foo() call is an exact match for #1. The third foo() call is an exact match for #2. The fourth foo() call can not bind to #2 because of the disallowed const A&& -> A&& conversion. But it will bind to #1 via an rvalue->lvalue conversion.

Note that without the second foo() overload, the example code works with all calls going to #1. As move semantics are introduced, the author of foo knows that he will be attracting non-const rvalues by introducing the A&& overload and can act accordingly. Indeed, the only reason to introduce the overload is so that special action can be taken for non-const rvalues.

Tuples

Tuples are fixed-size heterogeneous containers containing any number of elements. They are a generalized form of std::pair. The proposal originates in the Core Working Group from a Boost Library [1] implementation, but it will mainly affect in the Library.

The proposed tuple types:

The tuple template can be instantiated with any number of arguments from 0 to some predefined upper limit. In the Boost Tuple library, this limit is 10. The argument types can be any valid C++ types. For example:

typedef
  tuple< A, 
         const B, 
         volatile C, 
         const volatile D 
       > t1;

An n-element tuple has a default constructor, a constructor with n parameters, a copy constructor and a converting copy constructor. By converting copy constructor we refer to a constructor that can construct a tuple from another tuple, as long as the type of each element of the source tuple is convertible to the type of the corresponding element of the target tuple. The types of the elements restrict which constructors can be used:

class no_default_ctor { no_default_ctor(); };
tuple<no_default_ctor, float> b; // error - need default ctor
tuple<int&> c;                   // err no ref default ctor
tuple<int, float> a;             // ok

If an n-element tuple is constructed with a constructor taking n elements, all elements must be copy constructible and convertible (default initializable) from the corresponding argument. For example:

tuple< int, const int, std::string >(1, ’a’, "Hi") // ok
tuple< int, std::string >(1, 2);                   // error
tuple< char, int, const char(&)[3] > t1(’a’, 1, "Hi");
tuple< int, float, std::string > t2 = t1;               // ok

The argument to this constructor does not actually have to be of the standard tuple type, but can be any tuple-like type that acts like the standard tuple type, in the sense of providing the same element access interface. For example, std::pair is such a tuple-like type. For example:

tuple< int, int > t3 = make_pair(’a’,1);// ok

The proposal includes tuple constructors, utility functions, and ways of testing for tuples. The I/O streams library will be updated to support input and output of tuples. For example,

cout << make_tuple(1,"C++");

outputs

(1 C++)

Hash Tables

Hash tables almost made it into the first issue of the Standard, but our deadline precluded doing due diligence on the proposal at the time.

The current proposal is what you would expect and is compatible with the three main library implementations for hash tables: SGI/STLport, Dinkumware, and Metrowerks. What is new is that things like max_factor, load_factor, and other constants are now treated as hints. Each implementation is free to deal with them as it sees fit. Also, most double values and parameters are going to be float. It was felt that only one or two significant digits were meaningful, so the extra space needed by double was wasted.

There was one major outstanding issue. Since there are already widespread hash library implementations in use, how do we avoid breaking existing code. Proposals included making the new hash library names slightly different, or placing them in different namespaces, or usurping the current names because only the committee has the authority to place things in namespace std (any private implementation that placed their hash library into std was, by definition, in error.)

Polymorphic Function Object Wrapper

This proposal is based on the Boost Function Template Library [1]. It was accepted in Santa Cruz for inclusion in the next TR of the standard. It introduces a class template that generalizes the notion of a function pointer to subsume function pointers, member function pointers, and arbitrary function objects while maintaining similar syntax and semantics to function pointers.

This proposal defines a pure library extension, requiring no changes to the core C++ language. A new class template function is proposed to be added into the standard header <functional> along with supporting function overloads. A new class template reference_wrapper is proposed to be added to the standard header <utility> with two supporting functions. For example,

int add(int x, int y)
{ return x+y; }

bool adjacent(int x, int y)
{ return x == y-1 || x == y+1;}

struct compare_and_record
{
  std::vector< std::pair<int,int> > values;
  bool operator()(int x, int y)
  {
    values.push_back(
    std::make_pair(x,y));
    return x == y;
  }
};

function< int (int, int) > f;

f = &add;

cout << f(2, 3) << endl; // outputs 5

f = minus<int>();

cout << f(2, 3) << endl; // outputs -1

The proposed function object wrapper supports only a subset of the operations supported by function pointers with slightly different syntax and semantics. The major differences are detailed below, but can be summarized as follows:

The function class template is always a function object.

Conclusions

This short report did not touch on other exciting new features that will likely appear in the language, including the Regular Expression proposal, the use of static assertions to allow more extensive compile-time checking based on type information, and the auto expressions proposal to simplify template function definitions.

As you can see from the wide range of topics we discussed in Santa Cruz, C++ is still an exciting language that is continuing to adapt to users needs.

References

The Boost Library (www.boost.org)

Editorial

By Reg. Charney

Sea Change in C++

At the C++ ANSI/ISO Standards Committee last month, Andy Koenig gave a technical presentation that I believe heralds a sea change in C++’s future.

C++ is a spin-off of C and introduced classes back around 1983. C++ reflects the needs and environment of computing at that time. Things have changed significantly since then. We now have computers that are magnitudes faster, having thousands of times more memory and storage. The standard user and developer environment is no longer the command line, but is rich multimedia experience. Many of our newer applications run across networks that did not exist when C was first implemented.

Under current and future environments, many of our old assumptions are no longer valid or significant. For example, writing small, fast programs was a point of pride from the original days of computing. Now, writing a C/C++ program of less than 100KB can be difficult, often due to the mandatory libraries that are included. In the past, we wrote programs to run in closed environments on isolated uniprocessors. Today, all that has changed. Now, security is a major concern, and by extension, so is reliability. Network considerations play into any design and newer languages are often better suited to particular applications.

Also, the area served by C and C++ are diverging. C continues to serve its core user base, but C++ is now used more and more for large systems where its multi-paradigm, object oriented, and generic programming features are a real advantage. While source code compatibility is still a part of the C++ standardization effort, we are coming to a time when library and extension differences are going to force source incompatibility between the two languages. The silver lining is that we will still have binary compatibility when compiled under the same operating system.

This isn’t your grandmother’s C/C++ world any more.

Book Reviews

The Power of Events—An Introduction to Complex Event Processing In Distributed Systems by David Luckham, Addison-Wesley, ISBN 0-201-72789-7

Rating: Very highly recommended!
Reading Level: Easy.

I believe this book heralds the next step in system design, reliability, and effectiveness. On every page, new ideas jumped out at me.

In today’s ever more complex world with computer systems trying to model this complexity, we often do not have the tools to understand what is happening. Because of this ignorance and lack of ability to effectively measure what is occurring, many of our major systems are failing and we are unable to choose effective and efficient ways of improving the process. This book shows how to master this and future complexity.

The book comes in two parts, an introduction; and a more rigorous approach. The first part contains examples from a variety of distributed application fields: stock trading, protocol handling, order processing, etc. The problems are non-trivial. The central concept is an event. An event is an envelop that encloses a message, one or more time stamps and an indication of the cause of the event. From this starting point, we can track when something happened and why. For example, shipping a package involves receiving an order, doing a credit check, billing, and physically shipping the goods. Such an example shows a simple event pattern for shipping goods. Using this pattern, we can search for goods shipped and goods rejected. Where the event pattern match fails indicates why it failed to ship. Reporting on these patterns and the patterns themselves are views of interest to different groups — all based on the same set of events.

(Caveat—I am so taken with this book, that I have asked if I can co-author the next in the series of these texts. Thus, take my comments with a grain of salt, but my enthusiasm must indicate that this is something special.)

— Reg. Charney

Trends

By Reg. Charney

Turnaround?

Things have been pretty gloomy recently, so the slight up-tick that is shown in this month’s graph is interesting. While there has been a slight downturn in the total number of IT jobs available, both in the U.S. and Silicon Valley (SV), the opening for software engineers seems to be bucking this trend. (see Figure #1).

Least we get too excited, the graph shows that we are currently running at about 18% U.S.-wide and 5% of Silicon Valley of the job openings that we had in January 2000.