- 23.1 Introduction
- 23.2 The Fibonacci Sequence
- 23.3 Fibonacci as an STL Sequence
- 23.4 Discoverability Failure
- 23.5 Defining Finite Bounds
- 23.6 Summary

## 23.5 Defining Finite Bounds

There are two clear and related solutions to this problem.

- Have
`end()`return an iterator in the range [`begin()`, ∞) whose value does not overflow the value type. - Allow the user to specify an upper limit for the effective range provided by the sequence, represented in the value returned by
`end()`. This value would have to be within the valid range.

A good implementation would provide both, where solution 1 is merely the default form of solution 2. We'll examine this by looking at the user-specified limits first.

### 23.5.1 Iterators Rule After All?

Before we proceed, I must cover an issue that some readers may now be considering. Earlier I ruled out the representation of Fibonacci sequences as independent iterators. The cunning linguists among you may be considering a form that does exactly that, as in:

std::copy(Fibonacci_iterator(), Fibonacci_iterator() + 40 , std::ostream_iterator<Fibonacci_sequence::value_type>(std::cout , " "));

In this case, the putative `Fibonacci_iterator` would implement the addition operator, such that the expression `Fibonacci_iterator() + 40` would evaluate to an instance that would terminate the iteration of a default-constructed iterator on its fortieth increment. At first blush this seems like an adequate solution to the problem.

However, the problem is that use of the addition operator on an iterator indicates that the iterator type is a random access iterator. Further, random access iterators have constant time complexity. To be sure, we're perforce violating pure STL requirements here and there in STL extension. But such violations are never done without due care and particular attention to the effects on discoverability and the *Principle of Least Surprise*. For example, it's hard to imagine that users of the **InetSTL** `findfile_sequence` class (Section 21.1), an STL collection that provides iteration of remote FTP host directory contents, will assume any particular complexity guarantees, given the vagaries of Internet retrieval. However, I suggest that it's far more likely that someone would assume constant time seeing pointer arithmetic syntax on an iterator.

Further, since a user will reasonably expect to be able to type `*(Fibonacci_iterator() + 40)` if he or she can type `Fibonacci_iterator() + 40`, we'd have to implement full random access semantics. But, as far as I know, there's no constant-time function integral formula with which you can determine the * N*th value of the Fibonacci sequence. (There are a couple of formulas that may be used, but they rely on the square root of five, which would rely on floating-point calculation. One of them is

`((1 + sqrt(5)) / 2) - ((1 - sqrt(5)) / 2) ^ n`. I'm just enough of a computer numerist to know that I know far too little about floating point to be confident of writing a 100% correct sequence using floating-point calculations.)

Thus we would have to perform a number of forward or backward calculations to arrive at the required value, which is a linear-time operation. This would be a very unobvious violation of a user's expectations and is, in my opinion, unacceptable.

(Of course, we could provide amortized constant time by storing the calculated values in an array. We could go further and provide a static member array with precalculated values. We could even use template metaprogramming and effect compile-time calculation. But the purpose of this chapter is pedagogical. Feel free to do any of these, and let me know how it goes. I'll gladly post interesting solutions on the book's Web site.)

### 23.5.2 Constructor-Bound Range

One use case of a sequence might be to retrieve the first N elements in the sequence. It would be straightforward to implement the sequence and iterator classes such that you would specify the number of elements in the sequence constructor, which would then return a bounding iterator instance via its `end()` method, as shown in Listing 23.7. (This corresponds to * Fibonacci_sequence_5.hpp* on the CD.)

#### Listing 23.7. Version 5: Constructor and `end()` Method

public:// Constructionexplicit Fibonacci_sequence(size_t n) // Max # entries to enumerate : m_numEntries(n) {} . . . public:// Iterationconst_iterator begin(); const_iterator end() { return const_iterator(m_numEntries); // Define end of sequence } . . .

You could use this as follows:

Fibonacci_sequence fs(25); std::copy(fs.begin(), fs.end() , std::ostream_iterator<Fibonacci_sequence::value_type>(std::cout));

This could be implemented by adding an additional `m_stepIndex` member to `const_iterator`, which would be incremented each time `operator ++()` is called, and by evaluating equality (in `equal()`) by comparing the `m_stepIndex` members of the comparands (Listing 23.8).

#### Listing 23.8. Version 5: `const_iterator`

classFibonacci_sequence::const_iterator: . . . // As shown previously { public:// Constructionconst_iterator(value_type i0, value_type i1) : m_i0(i0) , m_i1(i1) , m_stepIndex(0) {} const_iterator(size_t stepIndex) : m_i0(0) , m_i1(0) , m_stepIndex(stepIndex) {} public:// Iterationclass_type& operator ++() { . . . // Perform the advancement summations as before ++m_stepIndex; return *this; } public:// Comparisonbool equal(class_type const& rhs) const { return m_stepIndex == rhs.m_stepIndex; } . . .

This is a nice solution, and it also allows us to meaningfully support the `empty()` method. However, there's an equally valid use case, that of constraining the enumeration within a given integral range, for example, to enumerate all entries less than the value 1,000,000,000. The sequence might look like that shown in Listing 23.9. (This implementation corresponds to * Fibonacci_sequence_6.hpp* on the CD.)

#### Listing 23.9. Version 6: Constructor and `end()` Method

classFibonacci_sequence{ . . . public:// Constructionexplicit Fibonacci_sequence(value_type limit); // Value ceiling : m_limit(limit) {} . . . public:// Iterationconst_iterator begin(); const_iterator end() { return const_iterator(m_limit); // Define sequence ceiling } . . .

Comparison would be conducted by the somewhat abstruse implementation of `equal()` shown in Listing 23.10. (There's an overflow bug in here, which I've left since this is a pedagogical class. Try setting the limit to 1,836,311,904 for a 32-bit unsigned value type. If readers want to implement the full testing for overflow, I'll be happy to post any correct solutions on the book's Web site.)

#### Listing 23.10. Version 6: `const_iterator::equal()` Method

bool Fibonacci_sequence::const_iterator::equal(class_type const& rhs) const { if( 0 != m_i1 && 0 != rhs.m_i1) { // Both definitely normal iterable instances return m_i0 == rhs.m_i0 && m_i1 == rhs.m_i1; } else if(0 != m_threshold && 0 != rhs.m_threshold) { // Both definitely threshold sentinel instances return m_threshold == rhs.m_threshold; } else { // Heterogeneous mix of the two types if(0 == m_threshold) { return m_i0 >= rhs.m_threshold; } else { return rhs.m_i0 >= m_threshold; } } }

A more flexible class would accommodate both these usage models. But doing so presents the sticky problem of how to unambiguously construct an instance of the sequence for either use case. One solution would be to use a two-parameter constructor, as follows:

. . . public:// ConstructionFibonacci_sequence(size_t n, value_type limit); . . .

The parameter for the end-marker type not used would be given a stock value, for example:

Fibonacci_sequence(0, 10000); // This uses a limit of 10,000 Fibonacci_sequence(20, 0); // This gives a sequence of 20 entries

Obviously, this is an inelegant and highly error-prone approach. A slightly less revolting alternative would be to use an enumeration to indicate the type of end marker required and use a `value_type` parameter for both the threshold and the number of entries:

. . . public:// Member Constantsenum LimitType { thresholdLimit, countLimit }; public:// ConstructionFibonacci_sequence(value_type limit, LimitType type); . . .

### 23.5.3 True Typedefs

The best solution uses true typedefs (Section 12.3), which facilitate the unambiguous overloading of essentially similar or even identical types. The final implementation of the `Fibonacci_sequence` does this, as shown in Listing 23.11. (This corresponds to * Fibonacci_sequence_7.hpp* on the CD.) Note the use of precondition enforcements in both constructors. A valid design alternative would be to throw

`std::out_of_range`(since the user's value is not predictable).

#### Listing 23.11. Version 7: Class Declaration and Traits Class

template <typename T> structFibonacci_traits; template <> struct Fibonacci_traits<uint32_t> { static const uint32_t maxThreshold = 2971215073; static const size_t maxLimit = 47; }; template <> struct Fibonacci_traits<uint64_t> { static const uint64_t maxThreshold = 12200160415121876738; static const size_t maxLimit = 93; }; classFibonacci_sequence{ public:// Member Typestypedef?? uint32_t or uint64_t ??value_type; typedef Fibonacci_traits<value_type> traits_type; typedeftrue_typedef<size_t, unsigned> limit; typedeftrue_typedef<value_type, signed> threshold; class const_iterator; public:// Constructionexplicit Fibonacci_sequence(limit l = limit(traits_type::maxLimit)) : m_limit(l.base_type_value()) , m_threshold(0) { STLSOFT_MESSAGE_ASSERT( "Sequence limit exceeded" , l <= traits_type::maxLimit()); } explicit Fibonacci_sequence(threshold t) : m_limit(0) , m_threshold(t.base_type_value()) { STLSOFT_MESSAGE_ASSERT( "Sequence threshold exceeded" , t <= traits_type::maxThreshold()); } public:// Iterationconst_iterator begin() const; const_iterator end() const { return (0 == m_limit) ? const_iterator(m_threshold) : const_iterator(m_limit, 0); } public:// Sizebool empty() const { return 0 == m_limit && 0 == m_threshold; } size_t max_size() const { return traits_type::maxLimit; } private:// Member Variablesconst size_t m_limit; const value_type m_threshold; };

Note the use of the traits. Although they're not required by the definition of the sequence as it stands, they serve two important purposes. First, they provide a clear and obvious place for the limit and threshold magic numbers to reside, as well as making them largely self-documenting. Second, should you choose to use a 32- or 64-bit value type, the change involves just a single line.

The iterator class can now be defined as shown in Listing 23.12.

#### Listing 23.12. Version 7: `const_iterator`

classFibonacci_sequence::const_iterator: . . . // As shown previously { public:// Member Typestypedef Fibonacci_sequence::value_type value_type; typedef Fibonacci_sequence::const_iterator class_type; private:// Constructionfriend class Fibonacci_sequence; const_iterator(); const_iterator(Fibonacci_sequence::limit lim); const_iterator(Fibonacci_sequence::threshold t); . . .// IterationandComparisonmethods as before };

With this definition, all the following are well defined (and thereby value constrained):

typedef Fibonacci_sequence fibseq_t; fibseq_t fs(fibseq_t::limit(0)); // Empty sequence fibseq_t fs(fibseq_t::limit(1)); // 1 value fibseq_t fs(fibseq_t::limit(10)); // 10 values fibseq_t fs(fibseq_t::limit(47)); // 47 values fibseq_t fs; // 47 values fibseq_t fs(fibseq_t::threshold(0)); // Empty sequence fibseq_t fs(fibseq_t::threshold(1)); // 1 value fibseq_t fs(fibseq_t::threshold(2)); // 3 values fibseq_t fs(fibseq_t::threshold(47)); // 10 values fibseq_t fs(fibseq_t::threshold(100)); // 12 values fibseq_t fs(fibseq_t::threshold(1000000000)); // 45 values

Equally important, the following are not well defined, and the user knows this because he or she can evaluate them against the member constants `Fibonacci_sequence::traits_ type::maxLimit` and `Fibonacci_sequence::traits_type::maxThreshold`. Furthermore, because of the enforcements placed in the constructor bodies, the user finds out immediately when something is wrong, rather than at a later point during enumeration when the values overflow.

fibseq_t fs(fibseq_t::limit(50)); // Breaks ctor precond fibseq_t fs(fibseq_t::threshold(2971215075)); // Breaks ctor precond