blob: a8f4e6d97685d045735e306bb50073750faf3467 [file] [log] [blame]
[/
/ Copyright (c) 2001 Jaakko Jรคrvi
/
/ Distributed under the Boost Software License, Version 1.0. (See
/ accompanying file LICENSE_1_0.txt or copy at
/ http://www.boost.org/LICENSE_1_0.txt)
/]
[article Design decisions rationale
[quickbook 1.6]
[id design_decisions_rationale]
[copyright 2001 Jaakko J\u00E4rvi]
[license Distributed under the
[@http://boost.org/LICENSE_1_0.txt Boost Software License,
Version 1.0].
]
]
[template simplesect[title]
[block '''<simplesect><title>'''[title]'''</title>''']]
[template endsimplesect[]
[block '''</simplesect>''']]
[section About namespaces]
There was a discussion about whether tuples should be in a separate namespace
or directly in the `boost` namespace. The common principle is that domain
libraries (like /graph/, /python/) should be on a separate subnamespace, while
utility like libraries directly in the boost namespace. Tuples are somewhere
in between, as the tuple template is clearly a general utility, but the
library introduces quite a lot of names in addition to just the tuple template.
Tuples were originally under a subnamespace. As a result of the discussion,
tuple definitions were moved directly under the `boost` namespace. As a result
of a continued discussion, the subnamespace was reintroduced. The final (I
truly hope so) solution is now to have all definitions in namespace
`::boost::tuples`, and the most common names in the `::boost` namespace as well.
This is accomplished with using declarations (suggested by Dave Abrahams):
namespace boost {
namespace tuples {
...
// All library code
...
}
using tuples::tuple;
using tuples::make_tuple;
using tuples::tie;
using tuples::get;
}
With this arrangement, tuple creation with direct constructor calls,
`make_tuple` or `tie` functions do not need the namespace qualifier. Further,
all functions that manipulate tuples are found with Koenig-lookup. The only
exceptions are the `get<N>` functions, which are always called with an
explicitly qualified template argument, and thus Koenig-lookup does not apply.
Therefore, `get` is lifted to `::boost` namespace with a using declaration.
Hence, the interface for an application programmer is in practice under the
namespace `::boost`.
The other names, forming an interface for library writers (cons lists,
metafunctions manipulating cons lists, ...) remain in the subnamespace
`::boost::tuples`. Note, that the names `ignore`, `set_open`, `set_close` and
`set_delimiter` are considered to be part of the application programmer's
interface, but are still not under `boost` namespace. The reason being the
danger for name clashes for these common names. Further, the usage of these
features is probably not very frequent.
[section For those who are really interested in namespaces]
The subnamespace name /tuples/ raised some discussion. The rationale for not
using the most natural name 'tuple' is to avoid having an identical name with
the tuple template. Namespace names are, however, not generally in plural form
in Boost libraries. First, no real trouble was reported for using the same
name for a namespace and a class and we considered changing the name 'tuples'
to 'tuple'. But we found some trouble after all. Both gcc and edg compilers
reject using declarations where the namespace and class names are identical:
namespace boost {
namespace tuple {
... tie(...);
class tuple;
...
}
using tuple::tie; // ok
using tuple::tuple; // error
...
}
Note, however, that a corresponding using declaration in the global namespace
seems to be ok:
using boost::tuple::tuple; // ok;
[endsect]
[endsect]
[section The end mark of the cons list (`nil`, `null_type`, ...)]
Tuples are internally represented as cons lists:
tuple<int, int>
inherits from
cons<int, cons<int, null_type> >
`null_type` is the end mark of the list. Original proposition was `nil`, but
the name is used in MacOS, and might have caused problems, so `null_type` was
chosen instead. Other names considered were /null_t/ and /unit/ (the empty
tuple type in SML).
Note that `null_type` is the internal representation of an empty tuple:
`tuple<>` inherits from `null_type`.
[endsect]
[section Element indexing]
Whether to use `0`- or `1`-based indexing was discussed more than thoroughly,
and the following observations were made:
* `0`-based indexing is 'the C++ way' and used with arrays etc.
* `1`-based 'name like' indexing exists as well, eg. `bind1st`, `bind2nd`,
`pair::first`, etc.
Tuple access with the syntax `get<N>(a)`, or `a.get<N>()` (where `a` is a
tuple and `N` an index), was considered to be of the first category, hence,
the index of the first element in a tuple is `0`.
A suggestion to provide `1`-based 'name like' indexing with constants like
`_1st`, `_2nd`, `_3rd`, ... was made. By suitably chosen constant types, this
would allow alternative syntaxes:
a.get<0>() == a.get(_1st) == a[_1st] == a(_1st);
We chose not to provide more than one indexing method for the following
reasons:
* `0`-based indexing might not please everyone, but once its fixed, it is less
confusing than having two different methods (would anyone want such
constants for arrays?).
* Adding the other indexing scheme doesn't really provide anything new (like a
new feature) to the user of the library.
* C++ variable and constant naming rules don't give many possibilities for
defining short and nice index constants (like `_1st`, ...). Let the binding
and lambda libraries use these for a better purpose.
* The access syntax a[_1st] (or a(_1st)) is appealing, and almost made us add
the index constants after all. However, `0`-based subscripting is so deep in
C++, that we had a fear for confusion.
* Such constants are easy to add.
[endsect]
[section Tuple comparison]
The comparison operator implements lexicographical order. Other orderings were
considered, mainly dominance /(a < b iff for each i a(i) < b(i))/. Our belief
is, that lexicographical ordering, though not mathematically the most natural
one, is the most frequently needed ordering in everyday programming.
[endsect]
[section Streaming]
The characters specified with tuple stream manipulators are stored within the
space allocated by `ios_base::xalloc`, which allocates storage for `long` type
objects. `static_cast` is used in casting between `long` and the stream's
character type. Streams that have character types not convertible back and
forth to long thus fail to compile.
This may be revisited at some point. The two possible solutions are:
* Allow only plain `char` types as the tuple delimiters and use `widen` and
`narrow` to convert between the real character type of the stream. This
would always compile, but some calls to set manipulators might result in a
different character than expected (some default character).
* Allocate enough space to hold the real character type of the stream. This
means memory for holding the delimiter characters must be allocated
separately, and that pointers to this memory are stored in the space
allocated with `ios_base::xalloc`. Any volunteers?
[endsect]