Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

The Technical Report on C++ Library Extensions


Matthew is a software engineer at Google, chair of the C++ Standardization Committee's Library Working Group, project editor of the Technical Report on C++ Library Extensions, and author of Generic Programming and the STL. He can be contacted at [email protected].


The C++ Standard Library is a large and successful project. The specification of the library is about 370 pages in length—longer than the parts of the C++ Standard that describe the core language itself (ISO, International Standard: Programming languages—C++. ISO/IEC 14882:2003(E), 2003). There are now dozens of books explaining how to use the Standard Library (I've listed a few in the references). Nor is this just the domain of standardization bureaucrats or textbook writers. The C++ Standard was finalized more than six years ago, every major C++ compiler ships with a complete Standard Library implementation, and millions of programmers are using them. If a C++ program was written within the last five years (and often even if it was written earlier), it's standard fare for it to use std::string, STL containers and algorithms, or IOStreams.

But if we look at it a little differently, the Standard Library doesn't seem so large after all. Granted, it's much larger than the C Standard Library (all of the 1990 C Library is included by reference!) and it includes an extensive collection of containers and algorithms (while the C Library has only qsort and bsearch). However, most of the problem areas the C++ Standard Library addresses—I/O and localization, character string manipulation, memory allocation, math functions—are in the C library as well. How does that compare to what other languages provide in their standard libraries?

The Perl, Python, and Java standard libraries include facilities for parsing XML, performing regular expression searches, manipulating image files, and sending data over the network. From that perspective, the C++ Standard Library looks like it's only the beginning.

And everyone in the Standards committee knows that, of course. When the Standard was finished in 1998, we didn't include everything that might have been a good idea—we only included what we could. Many potentially good ideas were left out for lack of time, lack of consensus, lack of implementation experience, or sometimes just lack of imagination. As Bjarne Stroustrup stated in the "The Design of C++0x" (C/C++ Users Journal, May 2005), the next C++ Standard will see far more emphasis on library evolution than core language changes. Within the Standards committee, wishing for a larger Standard Library is no more controversial than wishing for world peace.

The question isn't whether we need a more comprehensive library, but how we get one. Standards committees are good at evaluating ideas and working through corner cases, but they're slow, and they're poor at coming up with the ideas in the first place. "Design by committee," including design by Standards committee, is rarely a success.

Historically, members of the Standards committee came up with two answers. At the 1998 Santa Cruz committee meeting, Beman Dawes, then chair of the committee's Library Working Group, proposed that members of the C++ community work together informally to design and implement new library components, with the goal of advancing the state of the art and eventually building practice for a more extensive Standard Library. That proposal was wildly successful. It's what eventually became Boost (http://www.boost.org/).

But that still wasn't a complete answer. How does this informal library development, at Boost and elsewhere, get into something as formal as an official ISO Standard? At the 2001 Copenhagen meeting, Nico Josuttis and the rest of the German delegation came up with the second part—that we needed something in between the freedom of individual library development and the formality of an official ISO Standard. In 2001, a new revision of the C++ Standard seemed very far away, but many committee members were already working on libraries that they hoped could be part of that new Standard.

The fundamental issue is that, for obvious reasons, library development can go a lot faster than language development. The Standards committee decided to recognize that fact, and to start working on library extensions even before work on a new Standard began. We couldn't publish an official Standard on new library components, but what we could do, and what we did decide to do, was publish the work on library extensions in the form of a technical report. This report isn't "normative," in the jargon of standards bureaucracy, which means that compiler vendors don't have to include any of these new libraries to conform to the Standard. What it does is provide guidance to those vendors who do want to include them. In the meantime it gives us a head start on describing new library components in "standardese," gives us experience implementing and using new libraries, and will eventually make it easier to incorporate them into the next official Standard.

The idea of a library extensions technical report was first proposed at the 2001 Copenhagen meeting. The committee's library working group (which I chair) began discussing specific ideas for new library extensions at the next meeting and started to solicit proposals. The first extension proposals were accepted at the 2002 Santa Cruz meeting, and the last at the 2003 Kona meeting. Since then, the committee has been filling gaps, and fixing bugs and corner cases. At this writing, the C++ Standards committee has finalized the Proposed Draft Technical Report on C++ Library Extensions, ISO/IEC PDTR 19768, and forwarded it to our parent body. Most of the remaining steps are bureaucratic. There may be some minor changes before it is officially finalized, but the substantive work is now essentially complete; see http://www.open-std.org/jtc1/sc22/wg21/.

If you do download the technical report (TR) and read through it, you'll find that it looks a lot like the library part of the C++ Standard. Like the Standard, it contains header synopses; the Standard describes the contents of headers, such as <iterator> and <iostream>, like the TR describes the contents of headers, such as <tuple> and <regex>. (In some cases, such as <utility> and <functional>, the TR describes additions to existing headers.) Like the Standard, it contains classes and functions and type requirements. The style of specification in the TR is deliberately similar to the style in the Standard, because it is expected that much of what's in the TR will eventually become part of a future Standard.

There is one obvious difference between what's in the Standard and what's in the TR—the Standard Library defines all of its names within namespace std, but the TR defines its names within std::tr1. Why std::tr1 instead of just std::tr? For the obvious reason: Someday there may be a namespace std::tr2! For similar reasons, instead of spelling out "Technical Report on C++ Library Extensions," most people just say "TR1." Work on new Standard Library components has started. It hasn't ended.

The fact that TR1 components aren't defined within namespace std, however, is a reminder of the real difference—as official as it looks, the TR isn't a standard. Everything in it, by definition, is experimental; it's there to get user and implementor experience. The expectation is that most of what's in TR1 will make it into the "C++0x" Standard (the next official version of the C++ Standard), but it's possible, once we've got more real-world experience, that there will still be some big changes along the way. So, while there's a lot in the TR that makes C++ programming easier and more fun, and while it gives you a taste of what the next Standard will look like, be aware that using the libraries defined in the TR means living on the bleeding edge.

With that caveat in mind, let's look at some of the high points.

Taking the STL Further: Containers

The STL, originally written by Alex Stepanov, Dave Musser, and Meng Lee, was the most innovative part of the C++ Standard Library (see Alexander Stepanov and Meng Lee, "The Standard Template Library," HP Technical Report HPL-95-11 (R.1), 1995). Generic STL algorithms, such as sort and random_shuffle, can operate on objects stored in STL containers, such as vector and deque, on objects stored in built-in arrays, and in data structures that haven't even been written yet. The only requirement is that these data structures provide access to their elements via iterators. Conversely, new algorithms, provided that they access the objects they operate on only through the iterator interface, can work with all existing and yet-to-exist STL data structures. For additional flexibility, STL algorithms parameterize some of their behavior in terms of "function objects" that can be invoked using function call syntax. For example, find_if, which searches for a particular element in a range, takes a function object argument that determines whether its argument matches the search, and sort takes a function object argument that determines whether one object is less than another.

The STL was designed to be extensible, and people immediately extended it. Some of the most obvious gaps to be filled were in the containers. The STL includes three containers for variable-sized sequences of elements: vector, list, and deque. It also includes set and dictionary classes based on balanced trees: the "associative containers" set, map, multiset, and multimap. A common reaction to that list is to see an obvious omission—hash tables. Most other languages, including Perl, Python, and Java, have hash-based dictionaries. Why doesn't C++?

The only real reason was historical. There was a proposal to add hash tables to the STL as early as 1995 (see Javier Barreiro, Robert Fraley, and David R. Musser, "Hash Tables for the Standard Template Library," X3J16/94-0218 and WG21/N0605, 1995), but that was already too late to make it into the Standard. Individual vendors filled that gap. Most major library implementations include some form of hash tables, including the Dinkumware standard library that ships with Microsoft Visual C++ and the GNU libstdc++ that ships with GCC. Something that's provided by different vendors in not-quite-compatible versions is a natural candidate for standardization, so the technical report, at last, includes hash tables.

TR1 hash tables have many specialized features, but in simple cases they're used much the same way as the standard associative containers. In Listing One, the name of this class might give you pause. Why is it called unordered_map, when all previous implementations used the name hash_map? Unfortunately, that history is the biggest reason for using a different name. It's impossible to make TR1 hash tables compatible with all previous STL hash table implementations because they aren't compatible with each other. Since the committee had to use a different interface, it felt that the right decision was to use a different name to go with it.

The unordered associative containers, like all STL containers, are homogeneous. All of the elements in an std::vector<T>, or an std::trl::unordered_set<T>, have the same type. But the Standard also has one heterogeneous container: std::pair<T, U>, which contains exactly two objects that may be of different types. Pairs are useful whenever two things need to be packaged together. One common use is for functions that return multiple values. For example, the associative containers set and map (and unordered_set and unordered_map) have a version of insert whose return type is pair<iterator, bool>. The second part of the return value is a flag that tells you whether you did actually insert a new element or whether there was already one there with the same key, and the first part points to either the preexisting element or the newly inserted one.

Pairs are useful for packaging multiple values, so long as "multiple" means two. But that seems like an arbitrary restriction. Just as functions sometimes need to return more than one value, so they sometimes need to return more than two. In mathematics, an n-tuple is an ordered collection of n values; pairs are the special case where n is restricted to be 2. TR1 introduces a new heterogeneous container template, std::tr1::tuple, which removes that restriction.

Implementing tuple requires some sophisticated programming techniques, but using it couldn't be simpler. As with pair, you just supply the types as template parameters—except that with pair you supply exactly two template parameters, and with tuple you supply whatever number you like. (Up to some limit, but the limit should be large. On all implementations that I know of, it's at least 10.) For example:

#include <tr1/tuple>
#include <string>

using namespace std;
using namespace std::tr1;
...
tuple<int, char, string> t = make_tuple(1, 'a', "xyz");

If a function returns multiple values as a tuple, then, of course, one way to get those values is one at a time, the same as with a pair:

tuple<int, int, int> tmp = foo();
int x = get<0>(tmp);
int y = get<1>(tmp);
int z = get<2>(tmp);

The syntax is a little different than pair's first and second members, but the idea is similar. With tuples, however, there's an easier way—you don't even need that temporary variable. Instead, you can just write:

tie(x, y, z) = foo();

and let the library automatically handle all of this packing/unpacking.

Today, functions that need to return multiple values usually either pass in multiple reference parameters, or else define some ad hoc class to serve as a return type (think div_t). Now that we have tuple, which provides a dramatic improvement in usability, these clumsy workarounds might disappear.

Infrastructure: Smart Pointers and Wrappers

One reason tuple is useful is the same reason string is useful—it's a primitive that higher level libraries, including other parts of the Standard Library, can use in their own interfaces. What's important is that these types are a common vocabulary, so that two parts of a program, written independently of each other, can use them to communicate. The library extension technical report adds a number of other useful utility components of this nature.

One problem that appears in most programs is managing resource lifetimes. If you allocate memory or open a network socket, when does that memory get deallocated and when does the socket get closed? Two common solutions to that problem are automatic variables ("resource acquisition is initialization," or RAII) and garbage collection. Both solutions are useful and important, and every C++ programmer should be familiar with them. However, neither is appropriate in every circumstance. RAII works best when resource lifetime is statically determined and tied to the program's lexical structure, while garbage collection works better for memory than for other kinds of resources, and in any case is sometimes overkill. A third alternative, one that has been reinvented many times, is reference-counted smart pointers.

The basic idea behind reference-counted pointers is straightforward: Instead of trafficking in raw pointers of type T*, programs can use some kind of wrapper class that "looks like" a pointer. Just as with an ordinary pointer, you can dereference a smart pointer to access the T object it points to, or, more commonly, you can use the -> operator to access one of that object's members. The difference is that the wrapper class can instrument its basic operations, its constructors and destructor and assignment operators, so that it can keep track of how many owners a particular object has.

The TR1 reference-counted pointer class is shared_ptr. In the simplest case, it works just like the standard auto_ptr class—you create an object the usual way with new, and use it to initialize the shared_ptr. If there aren't any other shared_ptr instances that refer to the shared_ptr, then the object is destroyed when the shared_ptr goes out of scope.

What's more interesting is what happens if there are other instances that point to the same object—it doesn't get destroyed until the last instance that refers to it goes away. You can confirm this by doing a simple test with a class that logs its constructors and destructors. In Listing Two, the two pointers, p1 and p2, both point to the same A object, and both of those pointers are destroyed when they go out of scope. But shared_ptr's destructor keeps track of that, so it doesn't destroy the A object until the last reference to it disappears.

Naturally, real examples are more complicated than this test case. You can also assign shared_ptrs to global variables, pass them to and return them from functions, and put them in STL containers. A vector<shared_ptr<my_class> > is one of the most convenient ways of managing containers of polymorphic objects (see my article "Containers of Pointers," C/C++ Users Journal, October 2001).

Providing shared_ptr as part of TR1 has two major benefits.

  • As with tuple and string, it gives programs a common vocabulary: If I want to write a function that returns a pointer to dynamically allocated memory, I can use a shared_ptr as my return type and be confident that the clients of my function will have shared_ptr available. If I had written my own custom reference-counting class, that wouldn't have been true.
  • Second (and perhaps more importantly), shared_ptr works. That's not as trivial as it might seem! Many people have written reference-counting smart pointer classes but many fewer people have written ones that get all the corner cases right—especially in a multithreaded environment. Classes such as shared_ptr are surprisingly easy to get wrong, so you definitely want an implementation that has been well tested and that has seen lots of user experience.

Reference-counted pointers don't completely remove the possibility of resource management bugs; some discipline by programmers is needed. There are two potential problems. First, suppose that two objects are pointing to each other. If x holds a shared_ptr to y, and y holds a shared_ptr to x, then neither reference count can ever drop to zero even if nothing else in the program points to either x or y. They form a cycle, and will eventually cause a memory leak. Second, suppose that you're mixing memory-management policies, and that you have both a shared_ptr and a regular pointer to the same object:

my_class* p1 = new my_class;
shared_ptr<my_class> p2(p1)
...

If p1 outlives the last shared_ptr that's a copy of p2, then p1 becomes a dangling pointer—and ends up pointing to an object that has already been destroyed. Trying to dereference p1 will probably make your program crash. Some smart pointer libraries try to prevent this by making it impossible to access a smart pointer's underlying raw pointer, but shared_ptr doesn't. In my opinion, that was the right design decision. A general-purpose smart pointer class has to expose an underlying pointer somehow (otherwise operator-> can't work), and throwing up artificial syntactic barriers just makes legitimate uses harder.

These two sources of bugs go together because people commonly mix reference-counted pointers and raw pointers precisely to avoid cycles. If two objects need to point to each other, a common strategy is to choose one of those links as owning and the other as nonowning; the owning link can be represented as a shared_ptr, and the nonowning link as something else. This is a perfectly valid technique, especially if you resist the temptation to use a raw pointer for that "something else." The TR1 smart pointer library provides a better alternative—weak_ptr. A weak_ptr points to an object that's already being managed by a shared_ptr; it doesn't prevent that object from being destroyed when the last shared_ptr goes out of scope, but, unlike a raw pointer, it also can't dangle and cause a crash, and again unlike a raw pointer, it can safely be converted into a shared_ptr that shares ownership with whatever other shared_ptrs already exist. Listing Three is an example where it makes sense to combine shared_ptr and weak_ptr.

TR1 includes other primitives as well as smart pointers, including components that make it easier to use functions and function objects. One of the most useful is the new function wrapper class.

Suppose you want to write something that takes two arguments of type A and type B and returns a result of type C. C++ gives you lots of choices for how to express that operation! You might write an ordinary function:

C f(A a, B b) { ... }

Or, if C is your own class, you might express this as a member function:

class A {
...
C g(B b);
};

Or, as the STL does with such classes as std::plus<T>, you might choose to write this operation as a function object:

struct h {
C operator()(A, B) const {...}
};

There are syntactic and semantic differences between these options, and member functions, in particular, are invoked in a different way. You can encapsulate the syntactic differences using the Standard's mem_fun adaptor (or more conveniently, the TR's new mem_fn adaptor or bind adaptor), but there's one thing this won't help with—the types. We have three different ways of performing the same fundamental operation, A×B->C, and as far as the language is concerned they all have different types. It would be useful to have some single type that could represent all of these different versions.

That's what the TR1 function wrapper does. The type function<C(A,B)> represents any kind of function that takes A and B and returns C, whether it's an ordinary function, member function, or function object. Again, implementing function is quite difficult but all of that complexity is hidden. From the user's point of view, it just does what you expect: You can instantiate the function template with any reasonable types (as usual there's some limit on the number of parameters, but the limit should be large), you can assign anything to it that makes sense, and you invoke it using the ordinary function call syntax.

Use function<void(my_class)>, for example, to hold and invoke three different kinds of functions, all of which take a my_class argument and return nothing; see Listing Four. Putting these function<void(my_class)> objects into a vector may seem like an unnecessary complication. I did it to give a hint about why this is useful. In one word—callbacks. You now have a uniform mechanism that higher level libraries can use to hold the callbacks they're passed by their clients. I expect that in the future we will see this mechanism in the interfaces of many new libraries, especially ones designed for dynamism and loose coupling.

Application Support: Regular Expressions

Low-level components that make it easier to write other libraries are important, but so are library components that directly solve programmers' problems. Most programs have to work with text, and one of the classic techniques for text processing is pattern matching by regular expressions. Regular expressions are used in compilers, word processors, and any program that ever has to read a configuration file. Good support for regular expressions is one of the reasons that it's so easy to write simple "throwaway" scripts in Perl. Conversely, the lack of support for regular expressions is one of C++'s greatest weaknesses. Fortunately, as of TR1, we now have that support.

TR1 regular expressions have many features and options but the basic model is quite simple; it should seem familiar if you've used regular expressions in languages like Python or Java. First, you create a tr1::regex object that represents the pattern you'd like to match, using the standard syntax from ECMA-262 (that is, the same syntax that JavaScript uses). Next, you use one of the regular expression algorithms (regex_match, regex_search, or regex_replace) to match that pattern against a string. The difference between "match" and "search" is that regex_match tests whether the string you're trying to match is described by the regex pattern, while regex_search tests whether the string contains something that matches the pattern as a substring. Both regex_match and regex_search return bool, to tell you whether the match succeeded. You can also, optionally, pass in a match_results object to get more details.

Actually, you probably won't use match_results explicitly. The TR1 regular expression library, like the standard IOStream library, is templatized, but most of the time you can ignore that feature. You probably use istream, and not basic_istream. Similarly, you will probably use regex, which is an abbreviation for basic_regex<char>, instead of using basic_regex directly. In the case of match_results, you will probably use one of two specializations: smatch if you're searching through a string, and cmatch if you're searching through an array of char.

Listing Five shows how you might use TR1 regular expressions to write the core of the UNIX grep utility. With do_grep, you're only concerned with whether you have a match, not with any substructure. But one of the other main uses of regular expressions is to decompose compound strings into individual fields, as in Listing Six(a). In Listing Six(b) we take this further using regular expressions to convert between American and European customs for writing dates.

When you use regex_search it only shows you the first place where there's a match, even if there may be more matches later on in the string. What if you want to find all of the matches? One answer would be to do a search, find the first match, examine the match_results to find the end, search through the rest of the string, and so on. But that's harder than it needs to be. This is a case of iteration, so naturally you can just use an iterator. To collect all matches into a vector:

const string str =
"a few words on regular expressions";
const regex pat("[a-zA-Z]+");

sregex_token_iterator first(str.begin(),
str.end(), pat);
sregex_token_iterator last;

vector<string> words(first, last);

The Future

Clearly, the entire technical report is too much to cover in a single article. I mentioned shared_ptr and function, but I only alluded to reference_wrapper, result_of, mem_fn, and bind. I mentioned tuple and the unordered associative containers, but I left out the other new STL container in TR1, the fixed-size STL container array<T, N>. I entirely left out type traits, because it's mostly useful to library writers (it's very exciting if you do happen to be a library writer or if you do template metaprogramming!), and I left out the extensive work on random-number generation and on mathematical special functions. Either you care deeply about Bessel functions, hypergeometric functions, and the Riemann z function or you don't; if you do, now you know where to find them. And finally I left out the section on C99 compatibility. That's for essentially the opposite reason as the others—it's useful, it's important, and it just works. C99 functions in C++ should work just the way you would expect.

At this writing, I'm not aware of any complete implementations of the TR1 libraries. Still, there is work being done:

  • Metrowerks CodeWarrior 9.0 ships with a partial implementation of TR1, including such classes as function, shared_ptr, and tuple.
  • Many parts of TR1, including the smart pointers, regular expressions, and random number generators, were originally Boost libraries (http://www.boost.org/). Boost releases are available, and free, for all popular compilers and platforms.
  • Dinkumware is in the process of implementing the entire technical report. It is the only company I know of that's currently working on the TR1 special functions, like cyl_bessel_j and riemann_zeta, with the goal of achieving accuracy comparable to today's best implementations of functions like sin and cos.
  • The GNU libstdc++ project, which writes the C++ library that ships with GCC, is actively working on implementing TR1. The next release of GCC, GCC 4.0, will ship with a partial implementation of TR1—exactly how partial is hard to say, since TR1 components are being added to libstdc++ on a daily basis.

TR1 is real, not vaporware. All of the code samples in this article are real; I compiled them. (Well, except for the "..." parts.) The GNU libstdc++ implementation is still experimental and incomplete, but already complete enough that I was able to use it to test the examples of unordered associative containers, tuple, functional, and smart pointers. Because a GNU implementation of TR1 regular expressions doesn't yet exist, I used Boost.Regex. All I had to do was change the header and namespace names.

With the Standards committee nearly done with TR1 and implementation work underway, it's time to think about the next round of library extensions. What can we expect to see? It's too early to say. The Standards committee hasn't started discussing proposals for TR2 yet. I'm maintaining a list of some of the extensions people have asked for (http://lafstern.org/matt/wishlist.html) but there's a long way between a wish and a fully fleshed out proposal.

My own wish is better support for common practical tasks: parsing HTML and XML, manipulating GIF and JPEG images, reading directories on file systems, using HTTP. I'd like simple tasks, like trimming whitespace from a string or converting it to uppercase, to be simple. We've done an excellent job of creating general infrastructure to help library writers; now it's time to use some of that power to improve the experience of day-to-day development.

What's important to remember, though, is that Standards committees standardize; they don't invent. The only things that have a chance of making it into TR2 will be the ones that individuals feel strongly enough about to do real work on. Perhaps you'll be inspired by some of the entries on the "wish list," or by my suggestions, or by libraries from Boost or some other organization. As I wrote a few years ago, when work on TR1 had just begun (see "And Now for Something Completely Different," C/C++ Users Journal, January 2002), a library extension proposal should explain why this particular problem area is important, what the solution should look like, how your work relates to previous work in the same area, and how your work affects the rest of the library. It's a nontrivial task, but it's easier now than it was then: One thing we have now, that we didn't before, is examples of what library extension proposals can look like. The proposals that were accepted into TR1 are collected at http://open-std.org/jtc1/sc22/wg21/docs/ library_technical_report.html, and they can serve as models for TR2 proposals.

Now that the first Technical Report on C++ Library Extensions has essentially been completed, it's time to start thinking about the next round of library extensions. What comes next is partly up to you!

References

Austern, Matthew H. Generic Programming and the STL: Using and Extending the C++ Standard Template Library, Addison-Wesley, 1998.

Josuttis, Nicolai. The C++ Standard Library: A Tutorial and Reference, Addison-Wesley, 1999.

Langer, Angelika and Klaus Kreft. Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference, Addison-Wesley, 2000.

Lischner, Ray. STL Pocket Reference, O'Reilly & Associates, 2003.

Meyers, Scott. Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library, Addison-Wesley, 2001.

Musser, David R., Gilmer Derge, and Atul Saini. STL Tutorial and Reference Guide: C++ Programming with the Standard Template Library, Second Edition, Addison-Wesley, 2001.

Plauger, P.J., Alexander A. Stepanov, Meng Lee, and David R. Musser. The C++ Standard Template Library, Prentice Hall, 2000.

DDJ



Listing One

#include <tr1/unordered_map>
#include <iostream>
#include <string>

int main() 
{
  using namespace std;
  using namespace std::tr1;  
  typedef unordered_map<string, unsigned long> Map;
  Map colors;

  colors["black"] = 0xff0000ul;
  colors["red"] = 0xff0000ul;
  colors["green"] = 0x00ff00ul;
  colors["blue"] = 0x0000fful;
  colors["white"] = 0xfffffful;

  for (Map::iterator i = colors.begin(); 
       i != colors.end(); 
       ++i)
  cout << i->first << " -> " << i->second << endl;
}
Back to article


Listing Two
#include <iostream>
#include <tr1/memory>

using namespace std;
using namespace std::tr1;

struct A {
  A() { cout << "Create" << endl; }
  A(const A&) { cout << "Copy" << endl; }
  ~A() { cout << "Destroy" << endl; }  
};

int main() {
  shared_ptr<A> p1(new A);
  shared_ptr<A> p2 = p1;
  assert (p1 != NULL && p2 != NULL && p1 == p2);
}
Back to article


Listing Three
class my_node {
public:
  ...
private:
  weak_ptr<my_node> parent;
  shared_ptr<my_node> left_child;
  shared_ptr<my_node> right_child;
}; 
Back to article


Listing Four
#include <tr1/functional>
#include <vector>
#include <iostream>

using namespace std;
using namespace std::tr1;

struct my_class
{
  void f() { cout << "my_class::f()" << endl; }
};

void g(my_class) {
  cout << "g(my_class)" << endl;
}

struct h {
  void operator()(my_class) const {
    cout << "h::operator()(my_class)" << endl;
  }
};

int main()
{
  typedef function<void(my_class)> F;
  vector<F> ops;
  ops.push_back(&my_class::f);
  ops.push_back(&g);
  ops.push_back(h());

  my_class tmp;
  for (vector<F>::iterator i = ops.begin();
       i != ops.end();
       ++i)
    (*i)(tmp);
}
Back to article


Listing Five
#include <regex>
#include <string>
#include <iostream>

using namespace std;
using namespace std::tr1;

bool
do_grep(const string& exp, istream& in, ostream& out)
{
  regex r(exp);
  bool found_any = false;
  string line;

  while (getline(in, line))
    if (regex_search(line, r)) {
      found_any = true;
      out << line;
    }

  return found_any;
}
Back to article


Listing Six (a)
const string datestring = "10/31/2004";
const regex r("(\\d+)/(\\d+)/(\\d+)");
smatch fields;
if (!regex_match(datestring, fields, r))
  throw runtime_error("not a valid date");

const string month = fields[1];
const string day = fields[2];
const string year = fields[3];

(b)
const string date = "10/31/2004";
const regex r("(\\d+)/(\\d+)/(\\d+)");
const string date2 = regex_replace(date, r, "$2/$1/$3");
Back to article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.