| Last Updated: | 2005/12/28 02:56:45 CST |
|---|
First, a big “thank you” to everyone who has reported problems so far:
Index to Errata and Corrections
Page 17
Fredrik Hedman noted that the second example takes the log of a negative number, and that floats were being converted to ints, probably losing information. Therefore, change
int a = apply_fg(5.0f, std::negate<float>(), log2); int b = apply_fg(3.14f, log2, std::negate<float>());to:
float a = apply_fg(5.0f, std::negate<float>(), log2); float b = apply_fg(-3.14f, log2, std::negate<float>());
Page 19
Jim Jowdy correctly points out that:
iter_swap in section 2.1.3should actually be:
iter_swap in section 2.1.2
Page 22
The swap algorithm is in the <algorithm> header, not <utility>. Accordingly, change:
#include <utility> // for swapto:
#include <algorithm> // for swap
Pages 81-83, 110, 123, and 133
The use of i and j to denote iterator types in this chapter is slightly confusing. Accordingly, change all uses of the identifiers i and j to p and q, respectively.
Jordan DeLong noticed that there are two extra commas in the second example, which should read:
template <class Seq>
struct biggest_float_as_double
{
typedef typename mpl::replace<
typename mpl::begin<Seq>::type
, typename mpl::end<Seq>::type
, float
, double
>::type replaced;
typedef typename mpl::max_element<
typename mpl::begin<replaced>::type
, typename mpl::end<replaced>::type
, mpl::less<mpl::sizeof_<_1>, mpl::sizeof_<_2> >
>::type max_pos;
typedef typename mpl::deref<max_pos>::type type;
};
Andrew Savige noted that in the third paragraph, last sentence,
GCC is one such a compiler
should be changed to
GCC is one such compiler
Rob Stewart noticed that an em dash was misplaced.
The basic idea—that member functions—can be seen as free functions accepting an initial class argument is supported by languages such as Dylan, but once again, not by native C++.
should have been
The basic idea—that member functions can be seen as free functions accepting an initial class argument—is supported by languages such as Dylan, but once again, not by native C++.
Phillip Ngan noticed that
template class T> struct base;
should really be
template <class T> struct base;
To the end of the fourth paragraph, add:
If you need a hint, consider discussing the problem on the boost-users mailing list (see http://www.boost.org/more/mailing_lists.htm). The book's Web site, http://www.boost-consulting.com/mplbook, has a link to wiki pages containing answers to selected exercises.
Change:
These idioms have been applied to the copies of the book's examples that appear on the accompanying CD, but to avoid distracting the majority of readers they don't appear in the main text.
to:
To avoid distracting the majority of readers, these workarounds don't appear in the main text, but you can download patched code from the book's website, http://www.boost-consulting.com/mplbook.
Joshua Berne writes:
You claim that the results of binary<678> would make a strange sort of sense as 6x22 + 7x21 + 8x20. This, however, would only work if binary's template recursion was doing its composition using addition instead of bitwise |, which doesn't carry and so produces a far less sensical result.
Joshua is right; accordingly we changed the definition of binary<unsigned long N> to:
template <unsigned long N>
struct binary
{
static unsigned const value
= binary<N/10>::value * 2 // prepend higher bits
+ N%10; // to lowest bit
};
template <> // specialization
struct binary<0> // terminates recursion
{
static unsigned const value = 0;
};
Replace:
iter_swap_impl<use_swap>::do_it(*i1,*i2);
with
iter_swap_impl<use_swap>::do_it(i1,i2);
Thanks to Yutaka Leon Suematsu for pointing this problem out. Instantiating iter_swap on some appropriate types would have been enough for us to discover this flaw. Accordingly, you might want to append the following to chapter2/example17.cpp in your electronic copy of the book's code samples:
#include <vector>
#include <string>
int main()
{
std::vector<bool> x(20);
nonstd::iter_swap(x.begin(), x.end() - 1);
std::vector<std::string> y(20);
nonstd::iter_swap(y.begin(), y.end() - 1);
}
In exercise 2-2, change:
struct A {};
to:
struct A { virtual ~A() {} };
Thanks to Andy Moreton for reporting this error.
In exercise 2-2, change:
B& b_ref = polymorphic_downcast<B&>(b_ref);
to:
B& b_ref = polymorphic_downcast<B&>(a_ref);
Thanks to Nicholas Mongeau for pointing out this error.
In exercise 2-3, change:
// prints "long const*& volatile" std::cout << type_descriptor<long const*& volatile>();
to
// prints "long const*&" std::cout << type_descriptor<long const*&>();
Thanks to Bruno Martínez Aguerre for pointing out this error.
Michael Spiegel quite correctly points out that
angle
is a dimensionless (scalar) quantity, and accordingly should be replaced with the real seventh SI unit,
amount of substance.
likewise, on
typedef mpl::vector_c<int,0,0,0,0,0,0,1> angle;
ought to be:
typedef mpl::vector_c<int,0,0,0,0,0,0,1> amount_of_substance;
Michael also points out, as a point of interest, that
Actually temperature is a derived unit, since you can convert from Kelvins into electron volts. But the SI system still keeps Kelvins as a separate unit.
Change:
void transform(
InputIterator1 start1, InputIterator2 finish1
to:
void transform(
InputIterator1 start1, InputIterator1 finish1
Thanks to Rob Stewart for noticing this error.
Larry Evans points out that
dependent names in apply's initializer list
should be changed to
dependent names in apply's base-specifier-list
Yutaka Leon Suematsu points out that we repeated our error from page 24 here. Replace:
iter_swap_impl<use_swap>::do_it(*i1,*i2);
with
iter_swap_impl<use_swap>::do_it(i1,i2);
Rob Stewart suggested that we clarify the last sentence. Accordingly, change:
By taking advantage of the fact that Boost's integral metafunctions all supply a nested ::value, we can make yet another simplification to param_type:
to:
Since all of Boost's integral metafunctions supply a nested ::value, their specializations are valid integral type wrappers, and can be passed directly to mpl::if_, yielding another simplification:
Rob Stewart asked for a clarification here as well:
Because touching a template's nested ::value forces instantiation, the logical expression boost::is_scalar<_1>::value || is_reference<_1>::value is evaluated immediately.
should be changed to:
Touching a template's nested ::value forces instantiation, so the logical expression boost::is_scalar<_1>::value || is_reference<_1>::value is evaluated immediately and only tests the properties of the placeholder type _1 itself.
Change:
The author of an decrementable iterator
to:
The author of a decrementable iterator
Thanks to Rob Stewart for catching this one.
Rob Stewart points out that we neglected to introduce S and t in Tables 5.8 and 5.9. Accordingly, change:
In Tables 5.8 and 5.9, k and k2 can be any type and pos1 and pos2 are iterators into S.
to:
In Tables 5.8 and 5.9, S is an Associative Sequence, pos1 and pos2 are iterators into S, and t, k, and k2 can be any type.
Change:
The following expressions have constant complexity and return a new sequence that models all the same MPL sequence concepts as S does.
to:
The following expressions have constant complexity and return a new sequence S' that models all the same MPL sequence concepts as S does.
Thanks to Rob Stewart for this clarification.
Change:
#include <boost/mpl/iterator_tag.hpp>
to:
#include <boost/mpl/iterator_tags.hpp>
Thanks to Larry Evans for noticing this one.
Replace:
template <>
struct end_impl<tiny_tag>
{
template <class Tiny>
struct apply
: eval_if<
is_same<none,typename Tiny::t0>
, int_<0>
, eval_if<
is_same<none,typename Tiny::t1>
, int_<1>
, eval_if<
is_same<none,typename Tiny::t2>
, int_<2>
, int_<3>
>
>
>
{};
};
with:
template <>
struct end_impl<tiny_tag>
{
template <class Tiny>
struct apply
{
typedef tiny_iterator<
Tiny
, typename eval_if<
is_same<none,typename Tiny::t0>
, int_<0>
, eval_if<
is_same<none,typename Tiny::t1>
, int_<1>
, eval_if<
is_same<none,typename Tiny::t2>
, int_<2>
, int_<3>
>
>
>::type
> type;
};
};
Thanks to Jay Graham for pointing out this error.
In exercise 5-8, change:
typedef mpl::lower_bound< fibonacci_series, int_<10> >::type n; BOOST_STATIC_ASSERT( n::value == 8 ); typedef mpl::lower_bound< fibonacci_series, int_<50> >::type m; BOOST_STATIC_ASSERT( m::value == 34 );
to:
typedef mpl::advance_c< mpl::begin<fibonacci_series>::type, 6 >::type i; BOOST_STATIC_ASSERT( mpl::deref<i>::type::value == 8 ); typedef mpl::advance_c< i, 4 >::type j; BOOST_STATIC_ASSERT( mpl::deref<j>::type::value == 55 );
Thanks to John Lynch and Ariel Badichi for pointing out a number of errors in the original exercise leading to this correction.
In exercise 5-9, replace:
BOOST_STATIC_ASSERT( mpl::back<seq>::type::value == 21 );
with:
BOOST_STATIC_ASSERT( mpl::back<seq>::type::value == 13 );
Thanks to John Lynch and Ariel Badichi for reporting this one.
Rob Stewart suggested that we change the comment in the first code example from:
// Given a nonempty sequence Seq, returns the largest type in an // identical sequence where all instances of float have been // replaced by double.
to:
// Given a nonempty sequence Seq, returns the largest type in a // copy of Seq where all instances of float have been replaced // by double.
We think that makes things much clearer.
Rob Stewart pointed out that it was unclear what use of transform the 2nd sentence was referring to. Accordingly, change:
That's why we were able to use transform so effectively without specifying an inserter.
to:
That's why in Sections 3.1.4 and 3.1.5 we were able to use transform so effectively without specifying an inserter.
In Table 6.1, replace:
mpl::find_if<seq, T, pred>
with:
mpl::find_if<seq, pred>
Thanks to Robert Valkenburg for reporting this one.
Change “Logarithmic in invocations to compare” to “Logarithmic in invocations of compare” (twice). Also change “for all positions j” to “for all positions j in seq” (twice).
Thanks to Rob Stewart for these fixes.
Replace
they fold the results into S using push_front or push_back.
with:
they fold the results into S using push_front or push_back, respectively.
Thanks to Rob Stewart for the suggestion.
Rob Stewart notes that the sentence:
It follows that there's no reason an inserter (or a sequence building algorithm) needs to build new sequences; it can produce an arbitrary result depending on its function component.
bears some clarification. Accordingly, we've replaced it with:
Thus, a sequence building algorithm only actually builds sequences if its inserter's function component does: If, say, mpl::multiplies<_> is used instead, the algorithm's result will be a number and not a sequence.
Change:
In English, this means “return the size of the result of the element at the first position satisfying some condition,” where some condition is determined by the comparison predicate passed to lower_bound.
to:
In English, this means “return the size of the element at the first position satisfying some condition,” where some condition is determined by the comparison predicate passed to lower_bound.
Thanks to Ariel Badichi for noticing the extra words “of the result of.”
Change:
[[s1, t1, u1...], [s2, t2, u2...], [s3, t3, u3...] ...]
to:
[[s1, t1, u1], [s2, t2, u2], [s3, t3, u3] ...]
John Lynch found this error.
Change:
We can clean the code up using
to:
We can clean up the code using
Thanks to Rob Stewart for the suggestion.
Rob Stewart points out that, contrary to our statement, our use of joint_view in the sort example actually doesn't yield the promised performace benefits.
Replace:
// return a sorted vector of all the elements from seq1 and seq2
typedef mpl::sort<
mpl::copy<
mpl::joint_view<seq1,seq2>
, mpl::back_inserter<mpl::vector<> >
>::type
>::type result;
with:
// return a sorted vector of all the elements from seq1 and seq2
typedef mpl::sort<
mpl::joint_view<seq1,seq2>
, mpl::less<_1,_2>
, mpl::back_inserter<mpl::vector<> >
>::type result;
Replace:
but let's try to firm the idea up a bit.
with:
but let's try to firm up the idea a bit.
Thanks to Rob Stewart for the suggestion.
Ariel Badichi points out that the two instances of transform_view at the bottom of this page ought to be simply transform, and that in:
next<::zip_iterator<IteratorSeq> >
<: is a digraph for [. Insert a space to fix that line:
next< ::zip_iterator<IteratorSeq> >
Ariel Badichi notes that exercise 7-1 incorrectly states that library implementation of zip_iterator employs transform_view instead of transform. This translates into the following new wording:
- 7-1.
- Our implementation of zip_iterator uses transform to generate its nested ::type. Would using transform_view instead yield any advantages in this case?
In exercise 7-2, replace:
Modify your zip_iterator so that its ::iterator_category
with:
Modify your zip_iterator so that its iterator category
Thanks to Ariel Badichi for reporting this one.
Brad Austin writes:
I believe your factorial example on page 160 has a number of issues.
Firstly I believe:
#include <boost/mpl/equal.hpp>should be:
#include <boost/mpl/equal_to.hpp>Secondly, the multiplies<> metafunction doesn't appear in the 1.32.0 version MPL Reference manual. Should this perhaps be changed to times<>?
Thirdly, I can't find documentation for multiplies<>, but assuming it works the same way as times<>, the Reference Manual indicates that its arguments are expected to meet the requirements of an integral constant. The second argument is:
factorial<typename mpl::prior<N>::type>so factorial<> must model an integral constant. However factorial<> derives from an expression of the form:
mpl::eval_if<unspecified>without adding any nested types or a value of its own, so factorial<> only models an integral constant if eval_if<unspecified> does, which it doesn't. (eval_if<unspecified>::type may, depending on the selected type, but eval_if<unspecified> itself is just a nullary metafunction.)
[...] even after fixing all of the previous issues, the original purpose of the example was to demonstrate how to use BOOST_STATIC_ASSERTION to cause the compiler to produce a meaningful error message when factorial is invoked with a negative argument, and this purpose is defeated (at least with my compiler, gcc 3.3) by the fact that invoking factorial with a negative argument still throws the compiler into a semi-infinite loop (as instantiating factorial<-1> instantiates factorial<-2>, which instantiates factorial<-3>, etc.). I believe this problem can be solved by changing equal_to to less_equal when comparing the argument against zero.
Thanks to Brad for pointing out all these issues! Here's the corrected version of the example in question:
#include <boost/mpl/int.hpp>
#include <boost/mpl/multiplies.hpp>
#include <boost/mpl/less_equal.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/prior.hpp>
#include <boost/static_assert.hpp>
namespace mpl = boost::mpl;
template <class N>
struct factorial
: mpl::eval_if<
mpl::less_equal<N,mpl::int_<0> > // check N <= 0
, mpl::int_<1> // 0! == 1
, mpl::multiplies< // N! == N * (N-1)!
N
, factorial<typename mpl::prior<N>::type>
>
>::type
{
BOOST_STATIC_ASSERT(N::value >= 0); // for nonnegative N
};
Change:
the backtrace material shown as the ellipsis
to:
the backtrace material shown as ellipses
Thanks to Rob Stewart for noticing this one.
Replace:
By using this second form, we can avoid building a whole new sequence of wrap specializations:
mpl::for_each<s, wrap<_1> >(print_type());
with:
By using this second form, we can avoid building a whole new sequence of wrap specializations. Thus,
mpl::for_each< mpl::transform<s, wrap<_1> >::type >(print_type());becomes:
mpl::for_each<s, wrap<_1> >(print_type());
Thanks to Rob Stewart for suggestion.
Change:
yielding value of type R:
to:
yielding a value of type R:
Thanks to Rob Stewart for reporting this one.
Replace:
int m4(typename metafunction3<T>::type p); ... };In this example, metaprograms are computing the value of m1, the type m2, and the parameter type of m4.
with:
int m3(typename metafunction3<T>::type p); ... };In this example, metaprograms are computing the value of m1, the type m2, and the parameter type of m3.
Thanks to Rob Stewart for noticing the inconsistency.
Larry Evans points out that:
requires knowing all the types following its type in the original sequence.
should actually be:
requires knowing all the types preceding its type in the original sequence.
Change:
- And pointers to data members
to:
- Pointers to data members
Thanks to Rob Stewart for pointing out this one.
Rob Stewart points out that the first code block on the page "is written as though it is a complete example, yet sin_squared() is not defined".
Replace:
inline float log2(float x) { return std::log(x)/std::log(2.0f); }
with:
float log2(float x) { return std::log(x)/std::log(2.0f); }
float sin_squared(float x) { return std::sin(std::sin(x)); }
Rob Stewart suggested that the format and wording of the "Type Erasure" section's opening paragraph can be confusing unless the reader already knowns where we are going with this. Consequently, we decided to make the following changes to the text.
Change:
To see what we mean, consider the following two expressions:
compose<float>(std::negate<float>(), &sin_squared) with type:
compose_fg<float,std::negate<float>,float(*)(float)>std::bind2nd(std::multiplies<float>(), 3.14159) with type:
std::binder2nd<std::multiplies<float> >Even though the results of these expressions have different types, they have one essential thing in common:
to:
To see what we mean, consider the following two runtime expressions:
- compose<float>(std::negate<float>(), &sin_squared)
- std::bind2nd(std::multiplies<float>(), 3.14159)
Even though the results of these expressions have different types (compose_fg<float,std::negate<float>,float(*)(float)> and std::binder2nd<std::multiplies<float> > correspondingly), they have one essential thing in common:
At the bottom of the page, after:
... though, your program may get bigger and sometimes even slower.
add:
In this section we'll show how a single non-templated function can operate on objects of all types conforming to a given interface—in this case, types whose instances are callable with a float yielding a float result.
At the bottom, change:
We could integrate our discovery by adding a std::vector<int> member to screensaver, and changing next_screen to pass that as an additional argument to the customize function:
to:
We could integrate our discovery by adding a std::vector<int> member to screensaver, and changing next_screen to pass that as an additional argument to the get_seed function:
Ariel Badichi found this error.
Change:
float operator()(float x) const
{
return f(x); // Delegate
}
to:
float operator()(float x) const
{
return this->f(x); // Delegate
}
Thanks to Rob Stewart for pointing this out.
Replace:
If the ::value of the enabling condition C is true, enable_if<C,T>::type will be T
with:
If the ::value of the enabling condition C is true, enable_if<C,T>::type would be T
Change:
If we tried to compute the result type greedily, there would be error during overload resolution
to:
If we tried to compute the result type greedily, there would be an error during overload resolution
Thanks to Rob Stewart for reporting this error.
Replace:
You don't need to understand how to generated code works:
with:
You don't need to understand how the generated code works:
Change:
It should be clear at this point that DSLs can make code more concise and easy-to-write.
to:
It should be clear at this point that DSLs can make code more concise and easier to write.
Thanks to Rob Stewart for this correction.
Robert Valkenburg noticed what appears to be a bug in the Blitz++ documentation: There's an extra A(I,J+1,K) term in the stencil. We think it should probably read:
// a three-dimensional stencil (used in solving PDEs) Range I(1,6), J(1,6), K(1,6); B = (A(I,J,K) + A(I+1,J,K) + A(I-1,J,K) + A(I,J+1,K) + A(I,J-1,K) + A(I,J,K+1) + A(I,J,K-1)) / 7.0;
Change:
Here, the expression slew(.799) would build a instance of class named_params<slew_tag, float, nil_t>
to:
Here, the expression slew(.799) would build an instance of class named_params<slew_tag, double, nil_t>
Note “a” versus “an” and “float” versus “double”. Thanks to Ariel Badichi for catching these errors.
Larry Evans points out that
, name_type_is<char const*> // slew_type = char const*
should be
, name_type_is<char const*> // name_type = char const*
Replace:
passed as the first argument to the std::multiplies<float>
with:
passed as the second argument to the std::multiplies<float>
Thanks to Rob Stewart for pointing out this error.
Robert Valkenburg noticed that:
, bind2nd(std::minus<float>(), 1)
should really be:
, bind2nd(std::minus<float>(), 0.5)
Replace:
- Print a sequence, replacing odd elements with periods.
With:
- Print a sequence, replacing even elements with periods.
Replace:
cd_detected(
"louie, louie"
with:
cd_detected(
"Louie, Louie"
Replace:
there's no way we can ask the user to write that type down.
with:
there's no way we can ask the user to spell that type's name.
Rob Stewart points out that the supposedly complete FSM declaration example we present is lacking declarations for the event classes.
Change:
// concrete FSM implementation
class player : public state_machine<player>
{
to:
struct play; struct open_close; // events
struct cd_detected; struct pause; struct stop;
class player : public state_machine<player>
{
and remove the following comment lines to make room:
// concrete FSM implementation
// the list of FSM states
// transition table
Bruno Martínez points out an erroneous space in:
void close_drawer(open_ close const&);
The line was meant to be:
void close_drawer(open_close const&);
We wrote:
“In your Boost Installation, libs/mpl/book/chapter10/player.cpp contains a complete implementation of what we've expored here.”
That should actually read:
On this book's companion CD, examples/chapter11/example16.cpp contains a complete implementation of what we've expored here.
Yutaka Leon Suematsu points out that:
template <class Event> int no_transition(Event const& e)
should be:
template <class Event> int no_transition(int state, Event const& e)
We wrote:
In the examples directory of the code that accompanies this book, player2.cpp illustrates a state_machine that dispatches using O(1) lookup into a static table of function pointers.
That should actually say:
You can download a state_machine that dispatches using O(1) lookup into a static table of function pointers from http://boost-consulting.com/mplbook/examples/player2.cpp
Replace:
Eventually you will want to understand the reason for their existence and how they can be used to optimize preprocessing speed;
with:
Eventually you will want to understand how they can be used to optimize preprocessing speed;
Replace:
template <class R, BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)>
struct signature<function<R( BOOST_PP_ENUM_PARAMS(n,A) )> >
: mpl::BOOST_PP_CAT(vector,n)<
R, BOOST_PP_ENUM_TRAILING_PARAMS(n,A)
> {};
with:
template <class R BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)>
struct signature<function<R( BOOST_PP_ENUM_PARAMS(n,A) )> >
: mpl::BOOST_PP_CAT(vector,n)<
R BOOST_PP_ENUM_TRAILING_PARAMS(n,A)
> {};
Thanks to Rob Stewart for reporting this one!
Rob Stewart points out that:
Where s, r, and d appear they have
should be:
Where s, r, and d appear, they have
Johan Råde writes:
The naive recursive algorithm for fibonnacci numbers does not have time complexity O(n^2). It is even worse than that. It is O(fibonacci(n)). And fibonacci(n) grows exponentially with n.
Johan is right! This translates into following two changes:
Replace:
Consider the classic recursive Fibonacci function, with O(n2) complexity:
With:
Consider the classic recursive Fibonacci function, with exponential complexity:
Replace:
The complexity of the compile-time fibonacci function is not O(n2), but O(n).
With:
The complexity of the compile-time fibonacci function is not O(exp(n)), but O(n).
It's not exactly an erratum, but we wrote:
“...some associative sequences use function overload resolution to implement their lookup strategies. Overload resolution can have a nontrivial cost in the compiler, but we're not considering it.”
One thing we probably should have added there is that overload resolution seems in practice to be so much faster than template instantiation that when you have a choice, using overload resolution is a huge win. Otherwise, there would be little point in having associative sequence operations that are only O(1) if you only consider template instantiations but O(N), strictly speaking, if you consider overload resolution.
It would have been good to have included some tests showing that to be true; we'll try to do that for the next edition.
Jin-ho Park writes:
the item [Veld04] has a wrong internet URL. It should be http://www.cs.chalmers.se/~tveldhui/papers/2004/dissertation.pdf
Note the character ‘l’ in “/~tveldhui/.”
Ariel Badichi notes that str[0] is accessed without checking whether the string is empty or not. If the input contains a blank line, it will invoke undefined behavior. Accordingly, change:
if (str[0] == 'q' || str[0] == 'Q')
to:
if (!str.empty() && (str[0] == 'q' || str[0] == 'Q'))