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

Tuple Types


June 2003/Tuples


As I reported in the last column [1], the big library news at the October 2002 standards meeting was that the first two library extensions were approved for the Standard Library extensions technical report:

  • Doug Gregor's proposal for polymorphic function object wrappers. [2]
  • Jaakko Järvi's proposal for tuple types. [3]

Both come directly from the Boost project, which is a collection of C++ libraries. [4] Last time, I promised that this column and the next would each cover one of these two accepted library extensions. This month, I'll give an overview of tuple types.

Tuple Types: A Simple Motivating Case

Have you ever wanted to return more than one result from a function? For example, consider this function:

// Example 1(a): Integer division,
// yields a quotient only
//
int IntegerDivide( int n, int d ) {
  return n / d;
}

// Sample use:
cout << "quotient = " << IntegerDivide( 5, 4 );

What's wrong with this? Nothing much, maybe. After all, we're doing exactly what the built-in integer division does, including rounding down the resulting quotient.

But what if we want to do more—specifically, what if we also want to provide a way to get the other important result of the division, namely the remainder? We're already using the (single, one and only) return value to encode the quotient, and there's no easy way to also provide the remainder without changing the function's signature in some way. One way we could do it is to add another "output" parameter:

// Example 1(b): Integer division,
// yielding a quotient and remainder,
// one as the return value and one via
// an output parameter
//
int IntegerDivide( int n, int d, <font color="red">int& r</font> ) {
  r = n % d;
  return n / d;
}

// Sample use:
int remainder;
int quotient = IntegerDivide( 5, 4, remainder );
cout << "quotient = " << quotient
    << "remainder = " << remainder;

That's kind of clunky, but it's the kind of thing we've all done many times. It seems a little weird that one part of the output is returned via the return value, while another is returned via an output parameter. One might argue that the following is more consistent:

// Example 1(c): Integer division,
// yielding a quotient and remainder,
// this time via two output parameters
//
void IntegerDivide( int n, int d, <font color="red">int& q, int& r</font> ) {
  r = n % d;
  q = n / d;
}

// Sample use:
int quotient, remainder;
IntegerDivide( 5, 4, quotient, remainder );
cout << "quotient = " << quotient
    << "remainder = " << remainder;

That may be more consistent, but perhaps vaguely unsatisfying. With a little thought, we remember why: Ralph Waldo Emerson insists on reminding us that "a foolish consistency is the hobgoblin of little minds." Rats. This version works, but I wouldn't blame you if you had an uneasy, queasy, cheesy feeling about it.

What to do? Well, it's usually at just about this point that we might remember that we have a tool in the Standard Library that may be appropriate: std::pair. After all, there are functions in the Standard Library that return multiple things, notably iterator ranges, as a single value—and they do it by yielding a pair<iterator, iterator>. The same approach could work here! Let's try it:

// Example 1(d): Integer division,
// yielding a quotient and remainder,
// this time both in the return value
//
<font color="red">std::pair<int,int></font> IntegerDivide( int n, int d ) {
  return pair<int,int>( n/d, n%d );
}

// Sample use:
pair<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << "quotient = " << quot_rem.first
    << "remainder = " << quot_rem.second;

As it turns out, this is a promising direction, and one that can be improved, generalized, and refined.

Tuples In Action

Some languages, including Haskell, ML, and Python, support tuple types directly. C++ doesn't, but hey, this is C++, the do-anything systems- and library-building power language—we can make our own tuple types, as a library! (And our own lambda functions, and our own— but I digress.) What Järvi and others have done is to package up a tuple type representing a "bundle-o-types" as a sort of generalization of pair, but much more so. A tuple object (object of tuple type) is similarly a "bundle-o-values."

The code for a tuple-ized IntegerDivide is very similar to the pair-ized one in Example 2(d), but don't be fooled:

// Example 2(a): Integer division,
// yielding a quotient and remainder,
// via a type return type
//
<font color="red">tuple<int,int></font> IntegerDivide( int n, int d ) {
  return tuple<int,int>( n/d, n%d );
}

// Sample use:
tuple<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << "quotient = " << quot_rem.get<0>()
    << "remainder = " << quot_rem.get<1>();

The syntax isn't quite as elegant as for pair, but that's a pretty small nit compared to the great flexibility that tuples offer compared to simple pairs.

For one thing, tuples aren't restricted to just two members; they can have any number of members, and, therefore, can bundle (and unbundle) any number of values. Just to illustrate why that might be useful, even for something as simple as the example we just looked at, consider this variant:

// Example 2(b): Floating-point division,
// yielding a quotient and remainder,
// but also an underflow
//
<font color="red">tuple<float, float, bool></font> // quotient, remainder, underflow
FloatDivide( float n, float d ) {
  // —
}

Try that with std::pair! (And because I know full well that some readers may be unable to resist doing exactly that, let me avoid some e-mail by adding: Yes, you could chain pairs manually, in this case for example std::pair<float, std::pair<float, bool> >, but I think we can all agree that would be the basest hackery.)

Now, you won't always want to work with the tuple as a bundle-o-values, so there are ways to "tie" ordinary standalone variables to a tuple. This is useful for bundling and unbundling values. For example, returning to the plain old quotient-and remainder version:

// Example 3: Bundling and unbundling
// using "tie"
//
tuple<int,int> IntegerDivide( int n, int d ) {
  return tuple<int,int>( n/d, n%d );
}

// Sample use:
int quotient, remainder;
<font color="red">tie( quotient, remainder )</font> = IntegerDivide( 5, 4 );
cout << "quotient = " << quotient
    << "remainder = " << remainder;

By the way, we don't have to write our own manual outputting like this if we don't want to. Tuples have their own stream insertion and extraction operators that work perfectly well:

// Example 4(a): Streaming tuples
//
tuple<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << quot_rem;	// "(1 1)"

Or, if you want to get a little fancier, you can control the open and close brackets and the delimiter:

// Example 4(b): Customizing streamed tuples
//
tuple<int, int> quot_rem = IntegerDivide( 5, 4 );
cout << tuples::set_open('[') << tuples::set_close(']')
    << tuples::set_delimiter(',')
    << quot_rem;	// "[1,1]"

Next Time

For more details about tuple, see also the freely available Boost implementation at www.boost.org for your reading and programming pleasures.

In the next column, I'll report on function facility, and after than on the April 2003 C++ Standards meeting, held in Oxford, UK. Stay tuned.

References

[1] H. Sutter. "Trip Report: October 2002" (C/C++ Users Journal, 21(2), February 2003).

[2] D. Gregor. "A Proposal to add a Polymorphic Function Object Wrapper to the Standard Library," ISO/ANSI C++ Standards committee paper (ISO/IEC JTC1/SC22/WG21 paper N1402, ANSI/NCITS J16 paper 02-0060).

[3] J. Järvi. "Proposal for adding tuple types into the standard library," ISO/ANSI C++ standards committee paper (ISO/IEC JTC1/SC22/WG21 paper N1403, ANSI/NCITS J16 paper 02-0061).

[4] www.boost.org


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.