Chapter 69. Boost.Utility

The library Boost.Utility is a conglomeration of miscellaneous, useful classes and functions that are too small to justify being maintained in stand-alone libraries. While the utilities are small and can be learned quickly, they are completely unrelated. Unlike the examples in other chapters, the code samples here do not build on each other, since they are independent utilities.

While most utilities are defined in boost/utility.hpp, some have their own header files. The following examples include the appropriate header file for the utility being introduced.

Example 69.1. Using boost::checked_delete()

  1. #include <boost/checked_delete.hpp>
  2. #include <boost/intrusive/list.hpp>
  3. #include <string>
  4. #include <utility>
  5. #include <iostream>
  6. struct animal : public boost::intrusive::list_base_hook<>
  7. {
  8. std::string name_;
  9. int legs_;
  10. animal(std::string name, int legs) : name_{std::move(name)},
  11. legs_{legs} {}
  12. };
  13. int main()
  14. {
  15. animal *a = new animal{"cat", 4};
  16. typedef boost::intrusive::list<animal> animal_list;
  17. animal_list al;
  18. al.push_back(*a);
  19. al.pop_back_and_dispose(boost::checked_delete<animal>);
  20. std::cout << al.size() << '\n';
  21. }

Example 69.1 passes the function boost::checked_delete() as a parameter to the member function pop_back_and_dispose(), which is provided by the class boost::intrusive::list from Boost.Intrusive. boost::intrusive::list and pop_back_and_dispose() are introduced in Chapter 18, while boost::checked_delete() is provided by Boost.Utility and defined in boost/checked_delete.hpp.

boost::checked_delete() expects as its sole parameter a pointer to the object that will be deleted by delete. Because pop_back_and_dispose() expects a function that takes a pointer to destroy the corresponding object, it makes sense to pass in boost::checked_delete() – that way, you don’t need to define a similar function.

Unlike delete, boost::checked_delete() ensures that the type of the object to be destroyed is complete. delete will accept a pointer to an object with an incomplete type. While this concerns a detail of the C++ standard that you can usually ignore, you should note that boost::checked_delete() is not completely identical to a call to delete because it puts higher demands on its parameter.

Boost.Utility also provides boost::checked_array_delete(), which can be used to destroy arrays. It calls delete[] rather than delete.

Additionally, two classes, boost::checked_deleter and boost::checked_array_deleter, are available to create function objects that behave like boost::checked_delete() and boost::checked_array_delete(), respectively.

Example 69.2. Using BOOST_CURRENT_FUNCTION

  1. #include <boost/current_function.hpp>
  2. #include <iostream>
  3. int main()
  4. {
  5. const char *funcname = BOOST_CURRENT_FUNCTION;
  6. std::cout << funcname << '\n';
  7. }

Example 69.2 uses the macro BOOST_CURRENT_FUNCTION, defined in boost/current_function.hpp, to return the name of the surrounding function as a string.

BOOSTCURRENTFUNCTION provides a platform-independent way to retrieve the name of a function. Starting with C++11, you can do the same thing with the standardized macro func. Before C++11, compilers like Visual C++ and GCC supported the macro __FUNCTION as an extension. BOOST_CURRENT_FUNCTION uses whatever macro is supported by the compiler.

If compiled with Visual C++ 2013, Example 69.2 displays int __cdecl main(void).

Example 69.3. Using boost::prior() and boost::next()

  1. #include <boost/next_prior.hpp>
  2. #include <array>
  3. #include <algorithm>
  4. #include <iostream>
  5. int main()
  6. {
  7. std::array<char, 4> a{{'a', 'c', 'b', 'd'}};
  8. auto it = std::find(a.begin(), a.end(), 'b');
  9. auto prior = boost::prior(it, 2);
  10. auto next = boost::next(it);
  11. std::cout << *prior << '\n';
  12. std::cout << *it << '\n';
  13. std::cout << *next << '\n';
  14. }

Boost.Utility provides two functions, boost::prior() and boost::next(), that return an iterator relative to another iterator. In Example 69.3, it points to “b” in the array, prior points to “a”, and next to “d”.

Unlike std::advance(), boost::prior() and boost::next() return a new iterator and do not modify the iterator that was passed in.

In addition to the iterator, both functions accept a second parameter that indicates the number of steps to move forward or backward. In Example 69.3, the iterator is moved two steps backward in the call to boost::prior() and one step forward in the call to boost::next().

The number of steps is always a positive number, even for boost::prior(), which moves backwards.

To use boost::prior() and boost::next(), include the header file boost/next_prior.hpp.

Both functions were added to the standard library in C++11, where they are called std::prev() and std::next(). They are defined in the header file iterator.

Example 69.4. Using boost::noncopyable

  1. #include <boost/noncopyable.hpp>
  2. #include <string>
  3. #include <utility>
  4. #include <iostream>
  5. struct animal : boost::noncopyable
  6. {
  7. std::string name;
  8. int legs;
  9. animal(std::string n, int l) : name{std::move(n)}, legs{l} {}
  10. };
  11. void print(const animal &a)
  12. {
  13. std::cout << a.name << '\n';
  14. std::cout << a.legs << '\n';
  15. }
  16. int main()
  17. {
  18. animal a{"cat", 4};
  19. print(a);
  20. }

Boost.Utility provides the class boost::noncopyable, which is defined in boost/noncopyable.hpp. This class makes it impossible to copy (and move) objects.

The same effect can be achieved by defining the copy constructor and assignment operator as private member functions or – since C++11 – by removing the copy constructor and assignment operator with delete. However, deriving from boost::noncopyable explicitly states the intention that objects of a class should be non-copyable.

Note

Some developers prefer boost::noncopyable while others prefer to remove member functions explicitly with delete. You will find arguments for both approaches at Stack Overflow, among other places.

Example 69.4 can be compiled and executed. However, if the signature of the print() function is modified to take an object of type animal by value rather than by reference, the resulting code will no longer compile.

Example 69.5. Using boost::addressof()

  1. #include <boost/utility/addressof.hpp>
  2. #include <string>
  3. #include <iostream>
  4. struct animal
  5. {
  6. std::string name;
  7. int legs;
  8. int operator&() const { return legs; }
  9. };
  10. int main()
  11. {
  12. animal a{"cat", 4};
  13. std::cout << &a << '\n';
  14. std::cout << boost::addressof(a) << '\n';
  15. }

To retrieve the address of a particular object, even if operator& has been overloaded, Boost.Utility provides the function boost::addressof(), which is defined in boost/utility/addressof.hpp (see Example 69.5). With C++11, this function became part of the standard library and is available as std::addressof() in the header file memory.

Example 69.6. Using BOOST_BINARY

  1. #include <boost/utility/binary.hpp>
  2. #include <iostream>
  3. int main()
  4. {
  5. int i = BOOST_BINARY(1001 0001);
  6. std::cout << i << '\n';
  7. short s = BOOST_BINARY(1000 0000 0000 0000);
  8. std::cout << s << '\n';
  9. }

The macro BOOST_BINARY lets you create numbers in binary form. Standard C++ only supports hexadecimal and octal forms, using the prefixes 0x and 0. C++11 introduced user-defined literals, which allows you to define custom suffixes, but there still is no standard way of using numbers in binary form in C++11.

Example 69.6 displays 145 and -32768. The bit sequence stored in s represents a negative number because the 16-bit type short uses the 16th bit – the most significant bit in short – as the sign bit.

BOOST_BINARY simply offers another option to write numbers. Because, in C++, the default type for numbers is int, BOOST_BINARY also uses int. To define a number of type long, use the macro BOOST_BINARY_L, which generates the equivalent of a number suffixed with the letter L.

Boost.Utility includes additional macros such as BOOST_BINARY_U, which initializes a variable without a sign bit. All of these macros are defined in the header file boost/utility/binary.hpp.

Example 69.7. Using boost::string_ref

  1. #include <boost/utility/string_ref.hpp>
  2. #include <iostream>
  3. boost::string_ref start_at_boost(boost::string_ref s)
  4. {
  5. auto idx = s.find("Boost");
  6. return (idx != boost::string_ref::npos) ? s.substr(idx) : "";
  7. }
  8. int main()
  9. {
  10. boost::string_ref s = "The Boost C++ Libraries";
  11. std::cout << start_at_boost(s) << '\n';
  12. }

Example 69.7 introduces the class boost::string_ref, which is a reference to a string that only supports read access. To a certain extent, the reference is comparable with const std::string&. However, const std::string& requires the existence of an object of type std::string. boost::string_ref can also be used without std::string. The benefit of boost::string_ref is that, unlike std::string, it requires no memory to be allocated.

Example 69.7 looks for the word “Boost” in a string. If found, a string starting with that word is displayed. If the word “Boost” isn’t found, an empty string is displayed. The type of the string s in main() isn’t std::string, it’s boost::stringref. Thus no memory is allocated with new and no copy is created. _s points to the literal string “The Boost C++ Libraries” directly.

The type of the return value of start_at_boost() is boost::string_ref, not std::string. The function doesn’t return a new string, it returns a reference. This reference is to either a substring of the parameter or an empty string. start_at_boost() requires that the original string remains valid as long as references of type boost::string_ref are in use. If this is guaranteed, as in Example 69.7, memory allocations can be avoided.

Additional utilities are also available, but they are beyond the scope of this book because they are mostly used by the developers of Boost libraries or for template meta programming. The documentation of Boost.Utility provides a fairly comprehensive overview of these additional utilities and can serve as a starting point if you are interested.