Follow datasim on Twitter
Share this page

Introduction to the Boost C++ Libraries
Volume I - Foundations

Errata

Below the errata for the first printing of the book. These errata are addressed in the second printing and the eBook version.

The full source code sent to registered readers was already correct and according to these errata, except for the errata for section 10.6 (example changed) and section 12.8 (wrong output) for code sent before 4 March 2011. Most errata provide extra information, add code that was missing from the book or address minor problems.

 

Errata last updated: 29 March 2012

Errata first printing

Section 2.10 - page 35 (near the end):

When binding to the name we must use the greater<string> object instead of greater<int>.

Thus the line:
  boost::bind(greater<int>(), boost::bind(&Person::name, _1),

becomes:
  boost::bind(greater<string>(), boost::bind(&Person::name, _1),

 

Section 4.2 - page 59 (halfway)

The code section defines the variable avg1 which is not further used. To avoid confusion, the code section can be replaced by the following:

  // Create a vector with values
  int size=4;
  vector<double> v(size);
  for (int i=0; i<size; ++i) v[i]=i*2.5;

  // Finding the average with user-defined function object
  // for_each returns the Average object so you can directly get the result
  // from it because Average implements the "cast to double" operator.
  double avg = for_each(v.begin(), v.end(), Average());
 
cout << "Average: " << avg << endl;

  // Multiply elements of a vector by a double using function object.
  vector<double> v2(v);
  for_each(v2.begin(), v2.end(), MultiplyByValue<double>(2.0)); // Function object.

  // Print the resulting vector.
  cout<<"Vector multiplied by two: ";
  for (int i = 0; i < v2.size(); ++i) cout<<v2[i]<<", "; cout<<endl;

 

Section 4.8 - page 67 (halfway)

The code section contain a for_ statement that accesses a variable i that is defined outside the phoenix function. So at the start of this code section, the following line must be added that defines i:

  int i; // Space for the counter

 

Section 4.09 - page 68 (near the end) (extra information)

When a Phoenix lambda fucntion get an object or object pointer as argument, you might want to call a method on that object. However, you can't directly use the dot (.) or arrow (->) operator on the Phoenix variable. Instead you need to used the ->* operator with the address of the member function to call. You use this on object pointers. For objects you first need to take the address of the variable:

  // Call the Print() function on each shape.
  // It is not possible to use the -> operator directly on a Phoenix object pointer.
  // So we need to use the ->* operator with the address of the function to call.
  // Not possible: for_each(vp.begin(), vp.end(), arg1->Print() );
  for_each(vp.begin(), vp.end(), (arg1->*&Shape::Print)() );

  // Call the Print function on all Point objects.
  // Can't directly use the dot operator on a Phoenix object,
  // but we can take the address of the object and then use the ->* operator as with object pointers.
  // Not possible: for_each(v.begin(), v.end(), arg1.Print() );
  for_each(v.begin(), v.end(), (&arg1->*&Shape::Print)() );

 

Further you might need to down cast a base class pointer to a derived class pointer. This is possible using the Phoenix dynamic_cast_<T>() after which you can call a method on the derived class using the ->* operator. Note that the ->* operator only works for methods that are not overloaded:

  // Cast the argument to a point and print X.
  // We need the Phoenix dynamic_cast_<>() to cast the Shape* to a Point*.
  // Note, the ->* operator only works if the function is not overloaded.
  // So we need to use Point::GetX()/Point::SetX() instead of the overloaded Point::X() getter/setter.
  for_each(vp.begin(), vp.end(), cout<<val("x: ")<<((dynamic_cast_<Point*>(arg1))->*&Point::GetX)()<<endl );

 

Section 4.13 - page 71 (near the end)

When using the copy algorithm to copy to a list, you need the back_inserter so the values are inserted at the end of the list. So the line:

  boost::phoenix::copy(arg1, arg2)(vA, myList);

 becomes:

  boost::phoenix::copy(arg1, arg2)(vA, back_inserter(myList));

 

Section 5.6 - page 83 (at the begining)

The comment for assigning a shared pointer to a weak pointer is wrong. Thus the line:

  // Assign weak pointer to shared pointer

becomes:

  // Assign shared pointer to weak pointer

 

Section 5.10.3 - page 90 (near the end)

The header files "nvp.hpp", "utility.hpp" and "version.hpp" are not essential for XML serialisation so their include statements can be removed.


Section 5.10.3 - page 91 (halfway)

The file is deserialised in the variable myMap which is not defined. Thus before the line that creates the ifstream variable, add the following line:

  map<string, double> myMap;

 

Section 6.3 - page 98 (halfway)

The variable myTuple is defined twice. The second myTuple should be renamed to myTuple2.


Section 6.6 - page 101 (near the end)

In the last code section, the opening, closing and delimeter character for entering a tuple using cin, are changed to square brackets and a comma. Thus the text displayed in the cout statement must be changed from:

  cout << "Enter a tuple in form (long double), e.g. [1, 2.2]: ";

to:

  cout << "Enter a tuple in form [long, double], e.g. [1, 2.2]: ";


Section 7.5 - page 108 (halfway)

The implementation of the PropertyThing::operator = () function is missing. It becomes:

  PropertyThing<Name>& operator = (const PropertyThing<Name>& source) { return *this; }

 

Section 7.5 - page 109 (figure 7.1)

Figure 7.1 is the wrong one. Figure 7.1 should be:

 

Section 8.4 - page 117 (halfway)

Retrieving the wrong type from a variant will throw an exception. So the line that is commented out will compile but will throw an exception at run-time. It can be uncommented:

  stA = boost::get<string>(myVariant); // Wrong type, throws exception

 

Section 8.5 - page 118 (last code section)

The variant is initialized with a string but the comment says "default long". The comment should of course say "string"

variant<long, string> myFirst("Hello you all"); // Variant with string

 

Section 9.2 - page 120 (near the end)

The line:

  complex<double> z4 = 2.0 + z4;

initializes z4 with 2.0 + z4 which leads to an undefined value. Instead it should be initialized with 2.0 + z3. Thus it should read:

  complex<double> z4 = 2.0 + z3;

 

Also the line:

  complex<double> z8 = z2 * 2.0;

should be a division since the multiplication is already done for z6. Thus it should read:

  complex<double> z8 = z2 / 2.0;


Section 9.4 - page 126 (last code section)

The variable R8 was already defined in the previous code section. Thus the variables in the last code section should be renamed:

  rational<long> R9 = rational<long>(2, 5);    // Classic

  rational<long> R10;
  R10.assign(2,5);                             // In-place assignment

 

Section 9.7 - page 132 (last line)

The comment of the ToType() function reverses the input and output. Instead of:

  { // Attempt to convert a T object to a string

it should read:

  { // Attempt to convert a string to a T object

 

Section 10.2 - page 135 (at the beginning)

The code part that makes trimmed copies of variable s1 has no effect since s1 is already trimmed in the modifying trim functions part above. Thus the order of the modifying and trimmed copies parts should be reversed.


Section 10.6 - page 139 (near the end)

The provide string to find matches in does not show the difference between first and last finders because there is only one match. The following changed code is a more clear example:

  // First and last finders example
  string str="the quick fox jumps over the lazy dog.";
 
cout<<"Source string: "<<str<<endl;

  // The iterator range used for the result of the algorithms.
  iterator_range<string::iterator> r;

  // Find a range in an input string and then do something with it
  r=find(str, first_finder("o", is_equal()) );
  to_upper(r);
  cout<<endl<<"First 'o' (case sensitive) converted to upper case:"<<endl<<str<<endl;

  r=find(str, last_finder("the", is_equal()) );
  to_upper(r);
  cout<<endl<<"Last 'the' (case sensitive) converted to upper case:"<<endl<<str<<endl;

 

Section 10.10 - page 145 (near the end)

The function begin() is called on the variable myintersect (lower-case i) while the variable was declared as myIntersect (upper-case I). Thus the line:

  set<double>::iterator i=myintersect.begin();

should become:

  set<double>::iterator i=myIntersect.begin();

 

Section 11.3.4 - page 150

Two C-style strings can't be appended using the plus operator. The should first be converted to an std::string type before adding. Thus the lines:

  string strB = "Field 1,Field 2 with \"embedded quote\",Field 3 Field 1," +
                "Field 2 with \n new line,Field 3";

become:

  string strB = string("Field 1,Field 2 with \"embedded quote\",") +
                string("Field 3 Field 1,Field 2 with \n new line,Field 3");

 

Section 11.3.4 - page 150 (near the end)

The text claims that the output is on one line. But one of the fields tokenized contains a new line (\n) character. Thus the output is on two lines as follows:

  Field 1 Field 2 with embedded quote Field 3 Field 1 Field 2 with
   new line Field 3

 

Section 12.5 - page 156 (halfway)

The regular expresion variable rA is defined twice. These variables should be renamed to rA1 and rA2. Thus the lines:

  regex rA("[1-5]*");   // Zero or more of any digit in range [1..5]
  regex rA("[^1-5]*");  // NOT in range([1..5])

become:

  regex rA1("[1-5]*");   // Zero or more of any digit in range [1..5]
  regex rA2("[^1-5]*");  // NOT in range([1..5])

 

Section 12.8 - page 159 (near the end)

The regular expression to match different spellings of color/colour does not work with the spaces. So the spaces should be removed. The line:

  regex myReg8("(Colo) (u) (r)", boost::regex::icase|boost::regex::perl);

becomes:

  regex myReg8("(Colo)(u)(r)", boost::regex::icase|boost::regex::perl);

 

Section 12.10 - page 161 (halfway)

The variable tmp is not declared and is unneccessary. Thus the lines:

  tmp = atoi(value[1].str().c_str());
  sum += tmp;

should replaced by:

  sum += atoi(value[1].str().c_str());

 

Section 13.9 - page 178 (halfway)

In the example set refers to boost::expressive::set. Since this can conflict with std::set because of the using namespace statements, it should refer to set with the complete namespace. Thus the line:

  >> (delim= (set= '/','-')) // followed by a delimiter

becomes:

  >> (delim= (boost::xpressive::set= '/','-')) // followed by a delimiter

 

Section 14.2 - page 181

The code example uses constants NZ, NX and NY. These are undefined and should be replaced by NDIM, NT and SIM respectively who are defined earlier this section. Thus the code becomes:

  // Accessing elements using () with a collection of indices
  typedef boost::array<Tensor::index, dim> Indices;

  Indices indices;
  for (indices[2]=0; indices[2] < NDIM; indices[2]++)
  {
    for (indices[0]=0; indices[0] < NT; indices[0]++)
    {
      for (indices[1]=0; indices[1] < NSIM; indices[1]++)
      {
        tensor(indices) = 0.0;
      }
    }
  }

Section 14.7 - page 184 (halfway)

The fortran_storage_order object passed to the IntegerCube constructor is missing the constructor brackets. Thus the line:

  IntegerCube myFortranCube(extents[rows][columns][layers], boost::fortran_storage_order);

becomes:

  IntegerCube myFortranCube(extents[rows][columns][layers], boost::fortran_storage_order());

 

Further in the code, there is a GeneralStorage object created with the default constructor which is later used as argument when constructing an IntegerCube by calling the () operator on the GeneralStorage object. This does not compile.
The IntegerCube constructor should get a GeneralStorage object as argument created with the ordering and ascending parameters. The line that created the GeneralStorage object can be removed and later created when creating the IntegerCube object. The code thus becomes:

  // User-defined storage order
  const int N = 3;
  typedef boost::general_storage_order<N> GeneralStorage;

  // last, then first, then second dimension
  IntegerCube::size_type ordering[] = {2,0,1};

  bool ascendingYN[] = {false, true, true};

  IntegerCube myStoredCube(extents[rows][columns][layers], GeneralStorage(ordering, ascendingYN));

 

Section 14.9 - page 188 (halfway)

Template arguments must be literals or constant variables. So the lines:

  int NROWS = 3; int NCOLUMNS = 3;
  Matrix<double, NROWs, NCOLUMNS> myMatrix;

must be:

  const int NROWS = 3; const int NCOLUMNS = 3;
  Matrix<double, NROWS, NCOLUMNS> myMatrix;

Also note the NROWS argument was misspelled with a lowercase 's' the 2nd line.


Section 15.6.3 - page 207 (at the begining)

The variable 'N' is defined as:

  const long N = 2000;

 

Section 15.6.3 - page 207 (halfway)

The line declaring variable 'a' in the GaussianKernal() function is missing. So add the following line at the beginning of this function:

  const double a = 1.0 / sqrt(2.0 * 3.1415);

 

Section 15.6.3 - page 207 (halfway)

The definitions of variables M, Lower, Upper, h and increment were missing. So add the following lines at the start of the last code section of the page:

  // Calculate the bandwidth
  long M = 60;                     // Number of mesh points in x direction
  double Lower = -3.0;
  double Upper = 3.0;
  double h = (Upper - Lower) / M;
  double increment = h;

 

Appendix A - page 257 (at the beginning)

The operator [] of the std::map being used in the print() fucntion is non-const. Thus the sm input argument must be passed as non-const. Thus the line:

  template <int N> void print(const SparseMatrix<N>& sm)

becomes:

  template <int N> void print(SparseMatrix<N>& sm)

 

Appendix A - page 262 (near the end)

The type MyHeapPointMgr was not defined. Before the main() function there must be the following typedef:

  typedef ObjectManager<HeapFactory<Point> > MyHeapPointMgr;

 

Appendix A - page 264 (at the beginning)

Directly calling the implementation() function on the derived class does not show the use of the CRTP pattern. It should call the interface() function of the base class that in its turn calls the implementation() function of the derived class. Thus the lines:

  Derived_A d1;
  d1.implementation(); d1.static_func();

  Derived_B d2;
  d2.implementation(); d2.static_func();

becomes:

  Derived_A d1;
  d1.interface(); d1.static_func();

  Derived_B d2;
  d2.interface(); d2.static_func();

 

Appendix A - page 265 (at the beginning)

Class Base_A is the same as defined at the begining of the CRTP section. Class Base_B is defined as:

  template <typename Derived> struct Base_B
  {
    void calculate()
    {
      // ...
      static_cast<Derived*>(this)->calculate();
      // ...
    }
  };

 

Further, to use the CRTP pattern, the line calling implementation() on the derived class must call the interface() base class function instead. Thus the line:

  d12.calculate(); d12.implementation(); d12.static_func();

becomes:

  d12.calculate(); d12.interface(); d12.static_func();

 

Errata second printing (and first printing)

Section 1.9.1 - page 20

Taking the absolute value of a square is unneccessary since a square is already positive. Thus the line:

  result += fabs(vec[i] * vec[i]);

Could be rewritten as:

  result += vec[i] * vec[i];

 

Section 5.10.4 - page 96

The serialize() function is split for the bus_stop_destination class. The save() function should be const. Also when loading, first the base class data should be loaded and then the name member. Thus the code becomes:

  // Separate save and load operations
  template <class Archive>
  void save(Archive& ar, const unsigned int version) const
  {
    ar & boost::serialization::base_object<bus_stop>(*this) & name;
  }

  template <class Archive>
  void load(Archive& ar, const unsigned int version)
  {
    ar & boost::serialization::base_object<bus_stop>(*this);

    if (version > 0)
    {
      ar & name;
    }

  }

  BOOST_SERIALIZATION_SPLIT_MEMBER()

 

 Finally the class version must be set for the bus_stop_destination class:

  // The current version
  BOOST_CLASS_VERSION(bus_stop_destination, 1)

Share this page