Home Cloud & Networking Boost C++ Application Development Cookbook - Second Edition

Boost C++ Application Development Cookbook - Second Edition

By Anton Polukhin Alekseevic
books-svg-icon Book
eBook $43.99 $29.99
Print $54.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $43.99 $29.99
Print $54.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Starting to Write Your Application
About this book
If you want to take advantage of the real power of Boost and C++ and avoid the confusion about which library to use in which situation, then this book is for you. Beginning with the basics of Boost C++, you will move on to learn how the Boost libraries simplify application development. You will learn to convert data such as string to numbers, numbers to string, numbers to numbers and more. Managing resources will become a piece of cake. You’ll see what kind of work can be done at compile time and what Boost containers can do. You will learn everything for the development of high quality fast and portable applications. Write a program once and then you can use it on Linux, Windows, MacOS, Android operating systems. From manipulating images to graphs, directories, timers, files, networking – everyone will find an interesting topic. Be sure that knowledge from this book won’t get outdated, as more and more Boost libraries become part of the C++ Standard.
Publication date:
August 2017
Publisher
Packt
Pages
438
ISBN
9781787282247

 

Chapter 1. Starting to Write Your Application

In this chapter, we will cover:

  • Getting configuration options
  • Storing any value in a container/variable
  • Storing multiple chosen types in a container/variable
  • Using a safer way to work with a container that stores multiple chosen types
  • Returning a value or flag where there is no value
  • Returning an array from a function
  • Combining multiple values into one
  • Binding and reordering function parameters
  • Getting a human-readable type name
  • Using the C++11 move emulation
  • Making a noncopyable class
  • Making a noncopyable but movable class
  • Using C++14 and C++11 algorithms
 

Introduction


Boost is a collection of C++ libraries. Each library has been reviewed by many professional programmers before being accepted by Boost. Libraries are tested on multiple platforms using many compilers and many C++ standard library implementations. While using Boost, you can be sure that you are using one of the most portable, fast, and reliable solutions that is distributed under a license suitable for commercial and open source projects.

Many parts of Boost have been included into C++11, C++14, and C++17. Furthermore, Boost libraries will be included in the next standard of C++. You will find C++ standard-specific notes in each recipe of this book.

Without a long introduction, let's get started!

In this chapter, we will see some recipes for everyday use. We'll see how to get configuration options from different sources and what can be cooked up using some of the data types introduced by Boost library authors.

 

Getting configuration options


Take a look at some of the console programs, such as cp in Linux. They all have a fancy help; their input parameters do not depend on any position and have a human-readable syntax. For example:

$ cp --help
Usage: cp [OPTION]... [-T] SOURCE DEST
  -a, --archive           same as -dR --preserve=all
  -b                      like --backup but does not accept an argument

You can implement the same functionality for your program in 10 minutes. All you need is the Boost.ProgramOptions library.

Getting ready

Basic knowledge of C++ is all you need for this recipe. Remember that this library is not only a header, so your program has to link against the libboost_program_options library.

How to do it...

Let's start with a simple program that accepts the count of apples and oranges as input and counts the total number of fruits. We want to achieve the following result:

$ ./our_program.exe --apples=10 --oranges=20
Fruits count: 30

Perform the following steps:

  1. Include the boost/program_options.hpp header and make an alias for the boost::program_options namespace (it is too long to type it!). We would also need an <iostream> header:
#include <boost/program_options.hpp> 
#include <iostream> 

namespace opt = boost::program_options; 
  1. Now, we are ready to describe our options in the main() function:
int main(int argc, char *argv[])
{
    // Constructing an options describing variable and giving 
    // it a textual description "All options". 
    opt::options_description desc("All options"); 

    // When we are adding options, first parameter is a name
    // to be used in command line. Second parameter is a type
    // of that option, wrapped in value<> class. Third parameter
    // must be a short description of that option.
    desc.add_options()
        ("apples", opt::value<int>(), "how many apples do 
                                       you have")
        ("oranges", opt::value<int>(), "how many oranges do you 
                                        have")
        ("help", "produce help message")
    ;
  1. Let's parse the command line:
    // Variable to store our command line arguments.
    opt::variables_map vm; 

    // Parsing and storing arguments.
    opt::store(opt::parse_command_line(argc, argv, desc), vm); 

    // Must be called after all the parsing and storing.
    opt::notify(vm);
  1. Let's add some code for processing the help option:
    if (vm.count("help")) {
        std::cout << desc << "\n";
        return 1;
    }
  1. Final step. Counting fruits may be implemented in the following way:
    std::cout << "Fruits count: "
        << vm["apples"].as<int>() + vm["oranges"].as<int>()
        << std::endl;

} // end of `main`

Now, if we call our program with the help parameter, we'll get the following output:

All options: 
--apples arg          how many apples do you have
--oranges arg        how many oranges do you have 
--help                    produce help message 

As you can see, we do not provide a type for the help option's value, because we do not expect any values to be passed to it.

How it works...

This example is pretty simple to understand from code and comments. Running it produces the expected result:

$ ./our_program.exe --apples=100 --oranges=20
Fruits count: 120

There's more...

The C++ standard adopted many Boost libraries; however, you won't find Boost.ProgramOptions even in C++17. Currently, there's no plan to adopt it into C++2a.

The ProgramOptions library is very powerful and has many features. Here's how to:

  • Parse configuration option values directly into a variable and make that option a required one:
    int oranges_var = 0;
    desc.add_options()
        // ProgramOptions stores the option value into 
        // the variable that is passed by pointer. Here value of 
        // "--oranges" option will be stored into 'oranges_var'.
        ("oranges,o", opt::value<int>(&oranges_var)->required(), 
                                                "oranges you have")
  • Get some mandatory string option:
        // 'name' option is not marked with 'required()',
        // so user may not provide it.
        ("name", opt::value<std::string>(), "your name")
  • Add short name for apple, set 10 as a default value for apples:
        // 'a' is a short option name for apples. Use as '-a 10'.
        // If no value provided, then the default value is used.
        ("apples,a", opt::value<int>()->default_value(10),
                                   "apples that you have");
  • Get the missing options from the configuration file:
    opt::variables_map vm;

    // Parsing command line options and storing values to 'vm'.
    opt::store(opt::parse_command_line(argc, argv, desc), vm);

    // We can also parse environment variables. Just use
    // 'opt::store with' 'opt::parse_environment' function.

    // Adding missing options from "apples_oranges.cfg" config file.
    try {
        opt::store(
            opt::parse_config_file<char>("apples_oranges.cfg", desc),
            vm
        );
    } catch (const opt::reading_file& e) {
        std::cout << "Error: " << e.what() << std::endl;
    }

Note

The configuration file syntax differs from the command-line syntax. We do not need to place minuses before the options. So, our apples_oranges.cfg file must look like this:oranges=20

  • Validate that all the required options were set:
    try {
        // `opt::required_option` exception is thrown if
        // one of the required options was not set.
        opt::notify(vm);

    } catch (const opt::required_option& e) {
        std::cout << "Error: " << e.what() << std::endl;
        return 2;
    }

If we combine all the mentioned tips into a single executable, then its help command will produce this output:

$ ./our_program.exe --help
 All options:
   -o [ --oranges ] arg          oranges that you have
   --name arg                       your name
   -a [ --apples ] arg (=10)  apples that you have
   --help                              produce help message

Running it without a configuration file will produce the following output:

$ ./our_program.exe
 Error: can not read options configuration file 'apples_oranges.cfg'
 Error: the option '--oranges' is required but missing 

Running the program with oranges=20 in the configuration file will generate ++, because the default value for apples is 10:

$ ./our_program.exe
 Fruits count: 30

See also

 

Storing any value in a container/variable


If you have been programming in Java, C#, or Delphi, you will definitely miss the ability of creating containers with the Object value type in C++. The Object class in those languages is a basic class for almost all types, so you are able to assign almost any value to it at any time. Just imagine how great it would be to have such a feature in C++:

typedef std::unique_ptr<Object> object_ptr; 

std::vector<object_ptr> some_values; 
some_values.push_back(new Object(10)); 
some_values.push_back(new Object("Hello there")); 
some_values.push_back(new Object(std::string("Wow!"))); 

std::string* p = dynamic_cast<std::string*>(some_values.back().get()); 
assert(p); 
(*p) += " That is great!\n"; 
std::cout << *p; 

Getting ready

We'll be working with the header-only library. The basic knowledge of C++ is all you need for this recipe.

How to do it...

Boost offers a solution, the Boost.Any library, that has an even better syntax:

#include <boost/any.hpp> 
#include <iostream> 
#include <vector> 
#include <string> 

int main() { 
    std::vector<boost::any> some_values; 
    some_values.push_back(10); 
    some_values.push_back("Hello there!"); 
    some_values.push_back(std::string("Wow!"));

    std::string& s = boost::any_cast<std::string&>(some_values.back()); 
    s += " That is great!"; 
    std::cout << s; 
} 

Great, isn't it? By the way, it has an empty state, which could be checked using the empty() member function (just like in standard library containers).

You can get the value from boost::any using two approaches:

void example() {
    boost::any variable(std::string("Hello world!"));

    // Following method may throw a boost::bad_any_cast exception
    // if actual value in variable is not a std::string.
    std::string s1 = boost::any_cast<std::string>(variable);

    // Never throws. If actual value in variable is not a std::string
    // will return an NULL pointer.
    std::string* s2 = boost::any_cast<std::string>(&variable);
}

How it works...

The boost::any class just stores any value in it. To achieve this, it uses the type erasure technique (close to what Java or C# does with all types). To use this library you do not really need to know its internal implementation in detail, but here's a quick glance at the type erasure technique for the curious.

On the assignment of some variable of type T, Boost.Any instantiates a holder<T> type that may store a value of the specified type T and is derived from some base-type placeholder:

template<typename ValueType>
struct holder : public placeholder {
    virtual const std::type_info& type() const {
         return typeid(ValueType);
    }
     ValueType held;
};

A placeholder type has virtual functions for getting std::type_info of a stored type T and for cloning a stored type:

struct placeholder {
    virtual ~placeholder() {}
    virtual const std::type_info& type() const = 0;
};

boost::any stores ptr-- a pointer to placeholder. When any_cast<T>() is used, boost::any checks that calling ptr->type() gives std::type_info equal to typeid(T) and returns static_cast<holder<T>*>(ptr)->held.

There's more...

Such flexibility never comes without any cost. Copy constructing, value constructing, copy assigning, and assigning values to instances of boost::any do dynamic memory allocation; all the type casts do RunTime Type Information (RTTI) checks; boost::any uses virtual functions a lot. If you are keen on performance, the next recipe will give you an idea of how to achieve almost the same results without dynamic allocations and RTTI usage.

boost::any makes use of rvalue references but can not be used in constexpr.

The Boost.Any library was accepted into C++17. If your compiler is C++17 compatible and you wish to avoid using Boost for any, just replace the boost namespace with namespace std and include <any> instead of <boost/any.hpp>. Your standard library implementation may work slightly faster if you are storing tiny objects in std::any.

Note

std::any has the reset() function instead of clear() and has_value() instead of empty(). Almost all exceptions in Boost derived from the std::exception class or from its derivatives, for example, boost::bad_any_cast is derived from std::bad_cast. It means that you can catch almost all Boost exceptions using catch (const std::exception& e).

See also

  • Boost's official documentation may give you some more examples; it can be found at http://boost.org/libs/any
  • The Using a safer way to work with a container that stores multiple chosen types recipe for more info on the topic
 

Storing multiple chosen types in a container/variable


C++03 unions can only hold extremely simple types called Plain Old Data (POD). For example in C++03, you cannot store std::string or std::vector in a union.

Are you aware of the concept of unrestricted unions in C++11? Let me tell you about it briefly. C++11 relaxes requirements for unions, but you have to manage the construction and destruction of non POD types by yourself. You have to call in-place construction/destruction and remember what type is stored in a union. A huge amount of work, isn't it?

Can we have an unrestricted union like variable in C++03 that manages the object lifetime and remembers the type it has?

Getting ready

We'll be working with the header-only library, which is simple to use. Basic knowledge of C++ is all you need for this recipe.

How to do it...

Let me introduce the Boost.Variant library to you.

  1. The Boost.Variant library can store any of the types specified at compile time. It also manages in-place construction/destruction and even does not even require the C++11 standard:
#include <boost/variant.hpp>
#include <iostream>
#include <vector>
#include <string>

int main() {
    typedef boost::variant<int, const char*, std::string> my_var_t;
    std::vector<my_var_t> some_values;
    some_values.push_back(10);
    some_values.push_back("Hello there!");
    some_values.push_back(std::string("Wow!"));

    std::string& s = boost::get<std::string>(some_values.back());
    s += " That is great!\n";
    std::cout << s;
} 

Great, isn't it?

  1. Boost.Variant has no empty state, but has an empty() function which is useless and always returns false. If you need to represent an empty state, just add some simple type at the first position of the types supported by the Boost.Variant library. When Boost.Variant contains that type, interpret it as an empty state. Here is an example in which we will use a boost::blank type to represent an empty state:
void example1() {
    // Default constructor constructs an instance of boost::blank.
    boost::variant<
        boost::blank, int, const char*, std::string
    > var;

    // 'which()' method returns an index of a type
    // currently held by variant.
    assert(var.which() == 0); // boost::blank

    var = "Hello, dear reader";
    assert(var.which() != 0);
}
  1. You can get a value from a variant using two approaches:
void example2() {
    boost::variant<int, std::string> variable(0);

    // Following method may throw a boost::bad_get
    // exception if actual value in variable is not an int.
    int s1 = boost::get<int>(variable);

    // If actual value in variable is not an int will return NULL.
    int* s2 = boost::get<int>(&variable);
}

How it works...

The boost::variant class holds an array of bytes and stores values in that array. The size of the array is determined at compile time by applying sizeof() and functions to get alignment to each of the template types. On assignment, or construction of boost::variant, the previous values are in-place destructed and new values are constructed on top of the byte array, using the placement new.

There's more...

The Boost.Variant variables usually do not dynamically allocate memory, and they do not require RTTI to be enabled. Boost.Variant is extremely fast and used widely by other Boost libraries. To achieve maximum performance, make sure that there is a simple type in the list of supported types at the first position. boost::variant takes advantage of C++11 rvalue references if they are available on your compiler.

Boost.Variant is part of the C++17 standard. std::variant differs slightly from theboost::variant:

  • std::variant is declared in the <variant> header file rather than in <boost.variant.hpp>
  • std::variant never ever allocates memory
  • std::variant is usable with constexpr
  • Instead of writing boost::get<int>(&variable), you have to write std::get_if<int>(&variable) for std::variant
  • std::variant can not recursively hold itself and misses some other advanced techniques
  • std::variant can in-place construct objects
  • std::variant has index() instead of which()

See also

 

Using a safer way to work with a container that stores multiple chosen types


Imagine that you are creating a wrapper around some SQL database interface. You decided that boost::any will perfectly match the requirements for a single cell of the database table.

Some other programmer will use your classes, and his/her task would be to get a row from the database and count the sum of the arithmetic types in a row.

This is what such a code would look like:

 

#include <boost/any.hpp> 
#include <vector> 
#include <string> 
#include <typeinfo> 
#include <algorithm> 
#include <iostream> 

// This typedefs and methods will be in our header, 
// that wraps around native SQL interface.
typedef boost::any cell_t; 
typedef std::vector<cell_t> db_row_t; 

// This is just an example, no actual work with database. 
db_row_t get_row(const char* /*query*/) { 
    // In real application 'query' parameter shall have a 'const 
    // char*' or 'const std::string&' type? See recipe "Type  
    // 'reference to string'" for an answer. 
    db_row_t row; 
    row.push_back(10); 
    row.push_back(10.1f); 
    row.push_back(std::string("hello again")); 
    return row; 
} 

// This is how a user will use your classes 
struct db_sum { 
private: 
    double& sum_; 
public: 
    explicit db_sum(double& sum) 
        : sum_(sum) 
    {} 

    void operator()(const cell_t& value) { 
        const std::type_info& ti = value.type(); 
        if (ti == typeid(int)) { 
            sum_ += boost::any_cast<int>(value); 
        } else if (ti == typeid(float)) { 
            sum_ += boost::any_cast<float>(value); 
        } 
    } 
}; 

int main() { 
    db_row_t row = get_row("Query: Give me some row, please."); 
    double res = 0.0; 
    std::for_each(row.begin(), row.end(), db_sum(res)); 
    std::cout << "Sum of arithmetic types in database row is: "
              << res << std::endl; 
} 

If you compile and run this example, it will output a correct answer:

Sum of arithmetic types in database row is: 20.1

Do you remember what your own thoughts were when reading the implementation of operator()? I guess they were, "And what about double, long, short, unsigned, and other types?" The same thoughts will come into the head of a programmer who will use your interface. So, you need to carefully document values stored by your cell_t or use a more elegant solution as described in the following sections.

Getting ready

Reading the previous two recipes is highly recommended if you are not already familiar with the Boost.Variant and Boost.Any libraries.

How to do it...

The Boost.Variant library implements a visitor programming pattern for accessing the stored data, which is much safer than getting values via boost::get<>. This pattern forces the programmer to take care of each type in variant, otherwise the code will fail to compile. You can use this pattern via the boost::apply_visitor function, which takes a visitor functional object as the first parameter and a variant as the second parameter. If you are using a pre C++14 compiler, then visitor functional objects must derive from the boost::static_visitor<T> class, where T is a type being returned by a visitor. A visitor object must have overloads of operator() for each type stored by a variant.

Let's change the cell_t type to boost::variant<int, float, string> and modify our example:

#include <boost/variant.hpp> 
#include <vector> 
#include <string> 
#include <iostream> 

// This typedefs and methods will be in header, 
// that wraps around native SQL interface. 
typedef boost::variant<int, float, std::string> cell_t; 
typedef std::vector<cell_t> db_row_t; 

// This is just an example, no actual work with database. 
db_row_t get_row(const char* /*query*/) { 
    // See recipe "Type 'reference to string'" 
    // for a better type for 'query' parameter. 
    db_row_t row; 
    row.push_back(10); 
    row.push_back(10.1f); 
    row.push_back("hello again"); 
    return row; 
} 

// This is a code required to sum values. 
// We can provide no template parameter 
// to boost::static_visitor<> if our visitor returns nothing. 
struct db_sum_visitor: public boost::static_visitor<double> { 
    double operator()(int value) const { 
        return value; 
    } 
    double operator()(float value) const { 
        return value; 
    } 
    double operator()(const std::string& /*value*/) const { 
        return 0.0; 
    } 
}; 

int main() { 
    db_row_t row = get_row("Query: Give me some row, please."); 
    double res = 0.0; 
    for (auto it = row.begin(), end = row.end(); it != end; ++it) { 
        res += boost::apply_visitor(db_sum_visitor(), *it); 
    } 

    std::cout << "Sum of arithmetic types in database row is: "
              << res << std::endl;
}

How it works...

At compile time, the Boost.Variant library generates a big switch statement, each case of which calls a visitor for a single type from the variant's list of types. At runtime, the index of the stored type is retrieved using which() and jumps to a correct case inswitch statement is made. Something like this will be generated for boost::variant<int, float, std::string>:

switch (which()) 
{ 
case 0 /*int*/: 
    return visitor(*reinterpret_cast<int*>(address())); 
case 1 /*float*/: 
    return visitor(*reinterpret_cast<float*>(address())); 
case 2 /*std::string*/: 
    return visitor(*reinterpret_cast<std::string*>(address())); 
default: assert(false); 
} 

Here, the address() function returns a pointer to the internal storage of boost::variant<int, float, std::string>.

There's more...

If we compare this example with the first example in this recipe, we'll see the following advantages of boost::variant:

  • We know what types a variable can store
  • If a library writer of the SQL interface adds or modifies a type held by a variant, we'll get a compile-time error instead of incorrect behavior

std::variant from C++17 also supports visitation. Just write std::visit instead of boost::apply_visitor and you're done.

Note

You can download the example code files for all Packt books that you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support, and register to have the files emailed directly to you.

See also

  • After reading some recipes from Chapter 4, Compile-Time Tricks, you'll be able to make generic visitor objects that work correctly even if underlying types change
  • Boost's official documentation contains more examples and a description of some other features of Boost.Variant; it is available at the following link: http://boost.org/libs/variant
 

Returning a value or flag where there is no value


Imagine that we have a function that does not throw an exception and returns a value or indicates that an error has occurred. In Java or C# programming languages, such cases are handled by comparing a return value from a function value with a null pointer. If the function returned null, then an error has occurred. In C++, returning a pointer from a function confuses library users and usually requires slow dynamic memory allocation.

Getting ready

Only basic knowledge of C++ is required for this recipe.

How to do it...

Ladies and gentlemen, let me introduce you to the Boost.Optional library using the following example:

The try_lock_device() function tries to acquire a lock for a device and may succeed or not, depending on different conditions (in our example it depends on some try_lock_device_impl() function call):

#include <boost/optional.hpp>
#include <iostream>

class locked_device {
    explicit locked_device(const char* /*param*/) {
        // We have unique access to device.
        std::cout << "Device is locked\n";
    }

    static bool try_lock_device_impl();

public:
    void use() {
        std::cout << "Success!\n";
    }

    static boost::optional<locked_device> try_lock_device() {
        if (!try_lock_device_impl()) {
            // Failed to lock device.
            return boost::none;
        }

        // Success!
        return locked_device("device name");
    }

    ~locked_device(); // Releases device lock.
};

The function returns the boost::optional variable that can be converted to a bool. If the returned value is equal to true, then the lock is acquired and an instance of a class to work with the device can be obtained by dereferencing the returned optional variable:

int main() { 
    for (unsigned i = 0; i < 10; ++i) { 
        boost::optional<locked_device> t
            = locked_device::try_lock_device(); 

        // optional is convertible to bool.
        if (t) { 
            t->use(); 
            return 0; 
        } else { 
            std::cout << "...trying again\n"; 
        } 
    } 

    std::cout << "Failure!\n"; 
    return -1; 
} 

This program will output the following:

...trying again
...trying again
Device is locked
Success!

Note

The default constructed optional variable is convertible to false and must not be dereferenced, because such an optional does not have an underlying type constructed.

How it works...

boost::optional<T> under the hood has a properly aligned array of bytes where the object of type T can be an in-place constructed. It also has a bool variable to remember the state of the object (is it constructed or not?).

There's more...

The Boost.Optional class does not use dynamic allocation and it does not require a default constructor for the underlying type. The current boost::optional implementation can work with C++11 rvalue references but is not usable with constexpr.

Note

If you have a class T that has no empty state but your program logic requires an empty state or uninitialized T, then you have to come up with some workaround. Traditionally, users create some smart pointer to the class T, keep a nullptr in it, and dynamically allocate T if non empty state is required. Stop doing that! Use boost::optional<T> instead. It's a much faster and more reliable solution.

The C++17 standard includes the std::optional class. Just replace <boost/optional.hpp> with <optional> and boost:: with std:: to use the standard version of this class. std::optional is usable with constexpr.

See also

Boost's official documentation contains more examples and describes advanced features of Boost.Optional (like in-place construction). The documentation is available at the following link: http://boost.org/libs/optional.

 

Returning an array from a function


Let's play a game of guessing! What can you tell about the following function?

char* vector_advance(char* val); 

Should return values be deallocated by the programmer or not? Does the function attempt to deallocate the input parameter? Should the input parameter be zero-terminated, or should the function assume that the input parameter has a specified width?

Now, let's make the task harder! Take a look at the following line:

char ( &vector_advance( char (&val)[4] ) )[4];

Do not worry. I've also been scratching my head for half an hour before getting an idea of what is happening here. vector_advance is a function that accepts and returns an array of four elements. Is there a way to write such a function clearly?

Getting ready

Only basic knowledge of C++ is required for this recipe.

How to do it...

We can rewrite the function like this:

#include <boost/array.hpp>

typedef boost::array<char, 4> array4_t;
array4_t& vector_advance(array4_t& val);

Here, boost::array<char, 4> is just a simple wrapper around an array of four char elements.

This code answers all the questions from our first example and is much more readable than the code from the second example.

How it works...

boost::array is a fixed-size array. The first template parameter of boost::array is the element type and the second one is the size of an array. If you need to change the array size at runtime, use std::vector , boost::container::small_vector, boost::container::stack_vector, or boost::container::vector instead.

The boost::array<> class has no handwritten constructors and all its members are public, so the compiler will treat it as a POD type.

There's more...

Let's see some more examples of the usage of boost::array:

#include <boost/array.hpp> 
#include <algorithm> 

typedef boost::array<char, 4> array4_t; 

array4_t& vector_advance(array4_t& val) {
    // C++11 lambda function
    const auto inc = [](char& c){ ++c; };

    // boost::array has begin(), cbegin(), end(), cend(),
    // rbegin(), size(), empty() and other functions that are
    // common for standard library containers.
    std::for_each(val.begin(), val.end(), inc);
    return val;
}

int main() { 
    // We can initialize boost::array just like an array in C++11: 
    // array4_t val = {0, 1, 2, 3}; 
    // but in C++03 additional pair of curly brackets is required. 
    array4_t val = {{0, 1, 2, 3}}; 

    array4_t val_res;               // it is default constructible
    val_res = vector_advance(val);  // it is assignable

    assert(val.size() == 4); 
    assert(val[0] == 1); 
    /*val[4];*/ // Will trigger an assert because max index is 3 

    // We can make this assert work at compile-time. 
    // Interested? See recipe 'Check sizes at compile-time' 
    assert(sizeof(val) == sizeof(char) * array4_t::static_size); 
} 

One of the biggest advantages of boost::array is that it does not allocated dynamic memory and provides exactly the same performance as a usual C array. People from the C++ Standard committee also liked it, so it was accepted to the C++11 standard. Try to include the <array> header and check for the availability of std::array. std::array has a better support for usage with constexpr since C++17.

See also

  • Boost's official documentation gives a complete list of the Boost.Array methods with a description of the method's complexity and throw behavior. It is available at the following link: http://boost.org/libs/array.
  • The boost::array function is widely used across recipes; for example, refer to the Binding a value as a function parameter recipe.
 

Combining multiple values into one


There is a very nice present for those who like std::pair. Boost has a library called Boost.Tuple. It is just like std::pair, but it can also work with triples, quads, and even bigger collections of types.

Getting ready

Only basic knowledge of C++ and a standard library is required for this recipe.

How to do it...

Perform the following steps to combine multiple values into one:

  1. To start working with tuples, you need to include a proper header and declare a variable:
#include <boost/tuple/tuple.hpp> 
#include <string> 

boost::tuple<int, std::string> almost_a_pair(10, "Hello");
boost::tuple<int, float, double, int> quad(10, 1.0f, 10.0, 1);
  1. Getting a specific value is implemented via the boost::get<N>() function, where N is a zero-based index of a required value:
#include <boost/tuple/tuple.hpp>

void sample1() {
    const int i = boost::get<0>(almost_a_pair); 
    const std::string& str = boost::get<1>(almost_a_pair); 
    const double d = boost::get<2>(quad);
}

The boost::get<> function has many overloads and is used widely across Boost. We already saw how it can be used with other libraries in the Storing multiple chosen types in a container/variable recipe.

  1. You can construct tuples using the boost::make_tuple() function, which is shorter to write, because you do not need to fully qualify the tuple type:
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <set>

void sample2() {
    // Tuple comparison operators are
    // defined in header "boost/tuple/tuple_comparison.hpp"
    // Don't forget to include it!
    std::set<boost::tuple<int, double, int> > s;
    s.insert(boost::make_tuple(1, 1.0, 2));
    s.insert(boost::make_tuple(2, 10.0, 2));
    s.insert(boost::make_tuple(3, 100.0, 2));

    // Requires C++11
    const auto t = boost::make_tuple(0, -1.0, 2);
    assert(2 == boost::get<2>(t));
    // We can make a compile time assert for type
    // of t. Interested? See chapter 'Compile time tricks'
}
  1. Another function that makes life easier is boost::tie(). It works almost as make_tuple, but adds a nonconst reference for each of the passed types. Such a tuple can be used to get values to a variable from another tuple. It can be better understood from the following example:
#include <boost/tuple/tuple.hpp>
#include <cassert>

void sample3() {
    boost::tuple<int, float, double, int> quad(10, 1.0f, 10.0, 1); 
    int i; 
    float f; 
    double d; 
    int i2; 

    // Passing values from 'quad' variables 
    // to variables 'i', 'f', 'd', 'i2'.
    boost::tie(i, f, d, i2) = quad; 
    assert(i == 10); 
    assert(i2 == 1); 
}

How it works...

Some readers may wonder why we need a tuple when we can always write our own structures with better names; for example, instead of writing boost::tuple<int, std::string>, we can create a structure:

struct id_name_pair { 
    int id; 
    std::string name; 
}; 

Well, this structure is definitely clearer than boost::tuple<int, std::string>. The main idea behind the tuple's library is to simplify template programming.

There's more...

A tuple works as fast as std::pair (it does not allocate memory on a heap and has no virtual functions). The C++ committee found this class to be very useful and it was included in the standard library. You can find it in a C++11 compatible implementation in the header file <tuple> (don't forget to replace all the boost:: namespaces with std::).

The standard library version of tuple must have multiple micro optimizations and typically provides a slightly better user experience. However, there is no guarantee on the order of construction of tuple elements, so, if you need a tuple that constructs its elements starting from the first, you have to use the boost::tuple:

#include <boost/tuple/tuple.hpp>
#include <iostream>

template <int I>
struct printer {
    printer() { std::cout << I; }
};

int main() {
    // Outputs 012
    boost::tuple<printer<0>, printer<1>, printer<2> > t;
}

The current Boost implementation of a tuple does not use variadic templates, does not support rvalue references, does not support C++17 structured bindings, and is not usable with constexpr.

See also

  • Boost's official documentation contains more examples, information about performance, and abilities of Boost.Tuple. It is available at the link http://boost.org/libs/tuple.
  • The Converting all tuple elements to a string recipe in Chapter 8, Metaprogramming, shows some advanced usages of tuples.
 

Binding and reordering function parameters


If you work with the standard library a lot and use the <algorithm> header, you definitely write a lot of functional objects. In C++14, you can use generic lambdas for that. In C++11, you only have non generic lambdas. In the earlier versions of the C++ standard, you can construct functional objects using adapter functions such as bind1st, bind2nd, ptr_fun, mem_fun, mem_fun_ref, or you can write them by hand (because adapter functions look scary). Here is some good news: Boost.Bind can be used instead of ugly adapter functions, and it provides a more human-readable syntax.

Getting ready

A knowledge of standard library functions and algorithms will be helpful.

How to do it...

Let's see some examples of the usage of Boost.Bind along with C++11 lambda classes:

  1. All the samples require the following headers:

 

// Contains boost::bind and placeholders.
#include <boost/bind.hpp>

// Utility stuff required by samples.
#include <boost/array.hpp>
#include <algorithm>
#include <functional>
#include <string>
#include <cassert>
  1. Count values greater than 5 as shown in the following code:

 

void sample1() {
    const boost::array<int, 12> v = {{
        1, 2, 3, 4, 5, 6, 7, 100, 99, 98, 97, 96
    }};

    const std::size_t count0 = std::count_if(v.begin(), v.end(),
        [](int x) { return 5 < x; }
    );
    const std::size_t count1 = std::count_if(v.begin(), v.end(), 
        boost::bind(std::less<int>(), 5, _1)
    ); 
    assert(count0 == count1); 
}
  1. This is how we may count empty strings:
void sample2() {
    const boost::array<std::string, 3> v = {{
        "We ", "are", " the champions!"
    }}; 

    const std::size_t count0 = std::count_if(v.begin(), v.end(),
        [](const std::string& s) { return s.empty(); }
    );
    const std::size_t count1 = std::count_if(v.begin(), v.end(), 
        boost::bind(&std::string::empty, _1)
    ); 
    assert(count0 == count1); 
} 
  1. Now, let's count strings with a length less than 5:
void sample3() {
    const boost::array<std::string, 3> v = {{
        "We ", "are", " the champions!"
    }};

    const std::size_t count0 = std::count_if(v.begin(), v.end(), 
        [](const std::string& s) {  return s.size() < 5; }
    ); 
    const std::size_t count1 = std::count_if(v.begin(), v.end(), 
        boost::bind(
            std::less<std::size_t>(),
            boost::bind(&std::string::size, _1),
            5
        )
    ); 
    assert(count0 == count1);  
} 
  1. Compare the strings:
void sample4() {
    const boost::array<std::string, 3> v = {{
        "We ", "are", " the champions!"
    }}; 
    std::string s(
        "Expensive copy constructor is called when binding"
    );

    const std::size_t count0 = std::count_if(v.begin(), v.end(),
        [&s](const std::string& x) {  return x < s; }
    ); 
    const std::size_t count1 = std::count_if(v.begin(), v.end(), 
        boost::bind(std::less<std::string>(), _1, s)
    ); 
    assert(count0 == count1); 
} 

How it works...

The boost::bind function returns a functional object that stores a copy of bound values and a copy of the original functional object. When the actual call to operator() is performed, the stored parameters are passed to the original functional object along with the parameters passed at the time of call.

There's more...

Take a look at the previous examples. When we are binding values, we copy a value into a functional object. For some classes this operation is expensive. Is there a way to bypass copying?

Yes, there is! Boost.Ref library will help us here! It contains two functions, boost::ref() and boost::cref(), the first of which allows us to pass a parameter as a reference, and the second one passes the parameter as a constant reference. The ref() and cref() functions just construct an object of type reference_wrapper<T> or reference_wrapper<const T>, which is implicitly convertible to a reference type. Let's change our last examples:

#include <boost/ref.hpp> 

void sample5() {
    const boost::array<std::string, 3> v = {{
        "We ", "are", " the champions!"
    }}; 
    std::string s(
        "Expensive copy constructor is NOT called when binding"
    );  

    const std::size_t count1 = std::count_if(v.begin(), v.end(), 
        boost::bind(std::less<std::string>(), _1, boost::cref(s))
    ); 
    // ...
} 

You can also reorder, ignore, and duplicate function parameters using bind:

void sample6() {
    const auto twice = boost::bind(std::plus<int>(), _1, _1);
    assert(twice(2) == 4);

    const auto minus_from_second = boost::bind(std::minus<int>(), _2, _1);
    assert(minus_from_second(2, 4) == 2);

    const auto sum_second_and_third = boost::bind(
        std::plus<int>(), _2, _3
    );
    assert(sum_second_and_third(10, 20, 30) == 50);
}

The functions ref , cref, and bind are accepted to the C++11 standard and are defined in the <functional> header in the std:: namespace. All these functions do not dynamically allocate memory and do not use virtual functions. The objects returned by them are easy to optimize for a good compiler.

Standard library implementations of those functions may have additional optimizations to reduce compilation time or just compiler-specific optimizations. You may use the standard library versions of bind, ref, cref functions with any Boost library or even mix Boost and standard library versions.

If you are using the C++14 compiler, then use generic lambdas instead of std::bind and boost::bind, as they are less obscure and simpler to understand. C++17 lambdas are usable with constexpr, unlike std::bind and boost::bind.

See also

The official documentation contains many more examples and a description of advanced features at http://boost.org/libs/bind.

 

 

Getting a human-readable type name


There is often a need to get a readable type name at runtime:

#include <iostream>
#include <typeinfo>

template <class T>
void do_something(const T& x) {
    if (x == 0) {
        std::cout << "Error: x == 0. T is " << typeid(T).name() 
        << std::endl;
    }
    // ...
}

However, the example from earlier is not very portable. It does not work when RTTI is disabled, and it does not always produce a nice human-readable name. On some platforms, code from earlier will output just i or d.

Things get worse if we need a type name without stripping the const, volatile, and references:

void sample1() {
    auto&& x = 42;
    std::cout << "x is "
              << typeid(decltype(x)).name()
              << std::endl;
}

Unfortunately, the preceding code outputs int in the best case, which is not what we were expecting.

Getting ready

Basic knowledge of C++ is required for this recipe.

How to do it

In the first case, we need a human-readable type name without qualifiers. The Boost.TypeIndex library will help us out:

#include <iostream>
#include <boost/type_index.hpp>

template <class T>
void do_something_again(const T& x) {
    if (x == 0) {
        std::cout << "x == 0. T is " << boost::typeindex::type_id<T>()
                  << std::endl;
    }
    // ...
}

In the second case, we need to keep the qualifiers, so we need to call a slightly different function from the same library:

#include <boost/type_index.hpp>

void sample2() {
    auto&& x = 42;
    std::cout << "x is "
              << boost::typeindex::type_id_with_cvr<decltype(x)>()
              << std::endl;
}

How it works...

The Boost.TypeIndex library has a lot of workarounds for different compilers and knows the most efficient way to produce a human-readable name for the type. If you provide a type as a template parameter, the library guarantees that all the possible type related computations will be performed at compile time and code will work even if RTTI is disabled.

cvr in boost::typeindex::type_id_with_cvr stands for const, volatile, and reference. That makes sure that the type won't be decayed.

There's more...

All the boost::typeindex::type_id* functions return instances of boost::typeindex::type_index. It is very close to std::type_index; however, it additionally, it has a raw_name() method for getting a raw type name, and pretty_name() for getting human-readable type name.

Even in C++17, std::type_index and std::type_info return platform-specific type names representations that are rather hard to decode or use portably.

Unlike the standard library's typeid(), some classes from Boost.TypeIndex are usable with constexpr. It means that you can get a textual representation of your type at compile time if you use a specific boost::typeindex::ctti_type_index class.

Users can invent their own RTTI implementations using the Boost.TypeIndex library. This could be useful for embedded developers and for applications that require extremely efficient RTTI tuned for particular types.

See also

Documentation on advanced features and more examples are available at http://boost.org/libs/type_index.

 

Using the C++11 move emulation


One of the greatest features of the C++11 standard is rvalue references. This feature allows us to modify temporary objects, stealing resources from them. As you can guess, the C++03 standard has no rvalue references, but using the Boost.Move library, you can write a portable code that emulates them.

Getting ready

It is highly recommended that you are at least familiar with the basics of C++11 rvalue references.

How to do it...

  1. Imagine that you have a class with multiple fields, some of which are standard library containers:
namespace other { 
    class characteristics{}; 
} 

struct person_info {
    std::string name_; 
    std::string second_name_; 
    other::characteristics characteristic_; 
    // ...
}; 
  1. It is time to add the move assignment and move constructors to it! Just remember that in the C++03 standard library, containers have neither move operators nor move constructors.
  2. The correct implementation of the move assignment is the same move constructing an object and swapping it with this. The correct implementation of the move constructor is close to the default construct and swap. So, let's start with the swap member function:
#include <boost/swap.hpp> 

void person_info::swap(person_info& rhs) {
    name_.swap(rhs.name_);
    second_name_.swap(rhs.second_name_);
    boost::swap(characteristic_, rhs.characteristic_);
} 
  1. Now, put the following macro in the private section:
    BOOST_COPYABLE_AND_MOVABLE(person_info) 
  1. Write a copy constructor.
  2. Write a copy assignment, taking the parameter as: BOOST_COPY_ASSIGN_REF(person_info).

 

  1. Write a move constructor and a move assignment, taking the parameter as BOOST_RV_REF(person_info):
struct person_info {
    // Fields declared here
    // ...
private:
    BOOST_COPYABLE_AND_MOVABLE(person_info)
public:
    // For the simplicity of example we will assume that
    // person_info default constructor and swap are very
    // fast/cheap to call.
    person_info();

    person_info(const person_info& p)
        : name_(p.name_)
        , second_name_(p.second_name_)
        , characteristic_(p.characteristic_)
    {}

    person_info(BOOST_RV_REF(person_info) person) {
        swap(person);
    }

    person_info& operator=(BOOST_COPY_ASSIGN_REF(person_info) person) {
        person_info tmp(person);
        swap(tmp);
        return *this;
    }

    person_info& operator=(BOOST_RV_REF(person_info) person) {
        person_info tmp(boost::move(person));
        swap(tmp);
        return *this;
    }

    void swap(person_info& rhs);
};
  1. Now, we have a portable fast implementation of the move assignment and move construction operators of the person_info class.

How it works...

Here is an example of how the move assignment can be used:

int main() {
    person_info vasya;
    vasya.name_ = "Vasya";
    vasya.second_name_ = "Snow"; 

    person_info new_vasya(boost::move(vasya)); 
    assert(new_vasya.name_ == "Vasya"); 
    assert(new_vasya.second_name_ == "Snow"); 
    assert(vasya.name_.empty()); 
    assert(vasya.second_name_.empty()); 

    vasya = boost::move(new_vasya); 
    assert(vasya.name_ == "Vasya"); 
    assert(vasya.second_name_ == "Snow"); 
    assert(new_vasya.name_.empty()); 
    assert(new_vasya.second_name_.empty()); 
}

The Boost.Move library is implemented in a very efficient way. When the C++11 compiler is used, all the macros for rvalues emulation are expanded to C++11-specific features otherwise (on C++03 compilers), rvalues are emulated.

There's more...

Have you noticed the boost::swap call? It is a really helpful utility function, which first searches for a swap function in the namespace of a variable (in our example, it's namespace other::), and if there is no matching swap function, it uses the std::swap.

See also

  • More information about emulation implementation can be found on the Boost website and in the sources of the Boost.Move library at http://boost.org/libs/move.
  • The Boost.Utility library is the one that contains boost::swap, and it has many useful functions and classes. Refer to http://boost.org/libs/utility for its documentation.
  • The Initializing a base class by the member of derived recipe in Chapter 2, Managing Resources.
  • The Making a noncopyable class recipe.
  • In the Making a noncopyable but movable class recipe, there is more info about Boost.Move and some examples on how we can use the movable objects in containers in a portable and efficient way.
 

Making a noncopyable class


You have almost certainly encountered certain situations, where a class owns some resources that must not be copied for technical reasons:

class descriptor_owner { 
    void* descriptor_; 

public: 
    explicit descriptor_owner(const char* params); 

    ~descriptor_owner() { 
        system_api_free_descriptor(descriptor_); 
    } 
}; 

The C++ compiler in the preceding example generates a copy constructor and an assignment operator, so the potential user of the descriptor_owner class will be able to create the following awful things:

void i_am_bad() {
    descriptor_owner d1("O_o");   
    descriptor_owner d2("^_^"); 

    // Descriptor of d2 was not correctly freed 
    d2 = d1; 

    // destructor of d2 will free the descriptor 
    // destructor of d1 will try to free already freed descriptor 
}

Getting ready

Only very basic knowledge of C++ is required for this recipe.

How to do it...

To avoid such situations, the boost::noncopyable class was invented. If you derive your own class from it, the copy constructor and assignment operator won't be generated by the C++ compiler:

#include <boost/noncopyable.hpp> 

class descriptor_owner_fixed : private boost::noncopyable { 
    // ... 

Now, the user won't be able to do bad things:

void i_am_good() {
    descriptor_owner_fixed d1("O_o"); 
    descriptor_owner_fixed d2("^_^"); 

    // Won't compile 
    d2 = d1; 

    // Won't compile either 
    descriptor_owner_fixed d3(d1); 
}

How it works...

A refined reader will note that we can achieve exactly the same result by:

  • Making a copy constructor and an assignment operator of descriptor_owning_fixed private
  • Defining them without actual implementation
  • Explicitly deleting them using C++11 syntax = delete;

Yes, you are correct. Depending on the abilities of your compiler, boost::noncopyable class chooses the best way to make the class noncopyable.

boost::noncopyable also serves as a good documentation for your class. It never raises questions such as "Is the copy constructor body defined elsewhere?" or "Does it have a nonstandard copy constructor (with a non-const referenced parameter)?"

See also

  • The Making a noncopyable, but movable class recipe will give you ideas on how to allow unique owning of a resource in C++03 by moving it
  • You may find a lot of helpful functions and classes in the Boost.Core library's official documentation at http://boost.org/libs/core
  • The Initializing a base class by the member of derived recipe in Chapter 2, Managing Resources
  • The Using C++11 move emulation recipe
 

Making a noncopyable but movable class


Now, imagine the following situation: we have a resource that cannot be copied, which should be correctly freed in a destructor, and we want to return it from a function:

descriptor_owner construct_descriptor() 
{ 
    return descriptor_owner("Construct using this string"); 
} 

Actually, you can work around such situations using the swap method:

void construct_descriptor1(descriptor_owner& ret) 
{ 
    descriptor_owner("Construct using this string").swap(ret); 
} 

However, such a workaround does not allow us to use descriptor_owner in containers. By the way, it looks awful!

Getting ready

It is highly recommended that you are at least familiar with the basics of C++11 rvalue references. Reading the Using C++11 move emulation recipe is also recommended.

How to do it...

Those readers who use C++11, already know about the move-only classes (like std::unique_ptr or std::thread). Using such an approach, we can make a move-only descriptor_owner class:

class descriptor_owner1 {
    void* descriptor_;

public:
    descriptor_owner1()
        : descriptor_(nullptr)
    {}

    explicit descriptor_owner1(const char* param);

    descriptor_owner1(descriptor_owner1&& param)
        : descriptor_(param.descriptor_)
    {
        param.descriptor_ = nullptr;
    }

    descriptor_owner1& operator=(descriptor_owner1&& param) {
        descriptor_owner1 tmp(std::move(param));
        std::swap(descriptor_, tmp.descriptor_);
        return *this;
    }

    void clear() {
        free(descriptor_);
        descriptor_ = nullptr;
    }

    bool empty() const {
        return !descriptor_;
    }

    ~descriptor_owner1() {
        clear();
    }
};

// GCC compiles the following in C++11 and later modes.
descriptor_owner1 construct_descriptor2() {
    return descriptor_owner1("Construct using this string");
}

void foo_rv() {
    std::cout << "C++11n";
    descriptor_owner1 desc;
    desc = construct_descriptor2();
    assert(!desc.empty());
} 

This will work only on the C++11 compatible compilers. That is the right moment for Boost.Move! Let's modify our example, so it can be used on C++03 compilers.

According to the documentation, to write a movable but noncopyable type in portable syntax, we need to follow these simple steps:

  1. Put the BOOST_MOVABLE_BUT_NOT_COPYABLE(classname) macro in the private section:
#include <boost/move/move.hpp>

class descriptor_owner_movable {
    void* descriptor_;

    BOOST_MOVABLE_BUT_NOT_COPYABLE(descriptor_owner_movable
  1. Write a move constructor and a move assignment, taking the parameter as BOOST_RV_REF(classname):
public:
    descriptor_owner_movable()
        : descriptor_(NULL)
    {}

    explicit descriptor_owner_movable(const char* param)
        : descriptor_(strdup(param))
    {}

    descriptor_owner_movable(
        BOOST_RV_REF(descriptor_owner_movable) param
    ) BOOST_NOEXCEPT
        : descriptor_(param.descriptor_)
    {
        param.descriptor_ = NULL;
    }

    descriptor_owner_movable& operator=(
        BOOST_RV_REF(descriptor_owner_movable) param) BOOST_NOEXCEPT
    {
        descriptor_owner_movable tmp(boost::move(param));
        std::swap(descriptor_, tmp.descriptor_);
        return *this;
    }

    // ...
};

descriptor_owner_movable construct_descriptor3() {
    return descriptor_owner_movable("Construct using this string");
} 

How it works...

Now, we have a movable, but non copyable, class that can be used even on C++03 compilers and in Boost.Containers:

#include <boost/container/vector.hpp> 
#include <your_project/descriptor_owner_movable.h>

int main() {
    // Following code will work on C++11 and C++03 compilers 
    descriptor_owner_movable movable; 
    movable = construct_descriptor3(); 
    boost::container::vector<descriptor_owner_movable> vec; 
    vec.resize(10); 
    vec.push_back(construct_descriptor3()); 

    vec.back() = boost::move(vec.front()); 
}

Unfortunately, C++03 standard library containers still won't be able to use it (that is why we used a vector from Boost.Containers in the previous example).

There's more...

If you want to use Boost.Containers on C++03 compilers, but standard library containers on C++11 compilers, you can do the following simple trick. Add the header file to your project with the following content:

// your_project/vector.hpp 
// Copyright and other stuff goes here 

// include guards 
#ifndef YOUR_PROJECT_VECTOR_HPP 
#define YOUR_PROJECT_VECTOR_HPP 

// Contains BOOST_NO_CXX11_RVALUE_REFERENCES macro.
#include <boost/config.hpp>

#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 
// We do have rvalues 
#include <vector> 

namespace your_project_namespace { 
  using std::vector; 
} // your_project_namespace 

#else 
// We do NOT have rvalues 
#include <boost/container/vector.hpp> 

namespace your_project_namespace { 
  using boost::container::vector; 
} // your_project_namespace 

#endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 
#endif // YOUR_PROJECT_VECTOR_HPP 

Now, you can include <your_project/vector.hpp> and use a vector from the namespace your_project_namespace:

int main() {
    your_project_namespace::vector<descriptor_owner_movable> v; 
    v.resize(10); 
    v.push_back(construct_descriptor3()); 
    v.back() = boost::move(v.front()); 
}

However, beware of compiler and standard library implementation-specific issues! For example, this code will compile on GCC 4.7 in C++11 mode only if you mark the move constructor, destructor, and move assignment operators with noexcept or BOOST_NOECEPT.

See also

  • The Reducing code size and increasing performance of user-defined type in C++11 recipe in Chapter 10, Gathering Platform and Compiler Information, provides more info on noexcept and BOOST_NOEXCEPT.
  • More information about Boost.Move can be found on Boost's website http://boost.org/libs/move.
 

Using C++14 and C++11 algorithms


C++11 has a bunch of new cool algorithms in <algorithm> header. C++14 has even more algorithms. If you're stuck with the pre-C++11 compiler, you have to write those from scratch. For example, if you wish to output characters from 65 to 125 code points, you have to write the following code on a pre-C++11 compiler:

#include <boost/array.hpp>

boost::array<unsigned char, 60> chars_65_125_pre11() {
    boost::array<unsigned char, 60> res;

    const unsigned char offset = 65;
    for (std::size_t i = 0; i < res.size(); ++i) {
        res[i] = i + offset;
    }

    return res;
}

Getting ready

Basic knowledge of C++ is required for this recipe along with basic knowledge of Boost.Array library.

How to do it...

The Boost.Algorithm library has all the new C++11 and C++14 algorithms. Using it, you can rewrite the previous example in the following manner:

#include <boost/algorithm/cxx11/iota.hpp>
#include <boost/array.hpp>

boost::array<unsigned char, 60> chars_65_125() {
    boost::array<unsigned char, 60> res;
    boost::algorithm::iota(res.begin(), res.end(), 65);
    return res;
}

How it works...

As you are probably aware, Boost.Algorithm has a header file for each algorithm. Just include the header file and use the required function.

There's more...

It's boring to have a library that just implements algorithms from C++ standard. That's not innovative; that's not the Boost way! That's why you can find in Boost.Algorithm, functions that are not part of C++. Here, for example, is a function that converts input into hexadecimal representation:

#include <boost/algorithm/hex.hpp>
#include <iterator>
#include <iostream>

void to_hex_test1() {
    const std::string data = "Hello word";
    boost::algorithm::hex(
        data.begin(), data.end(),
        std::ostream_iterator<char>(std::cout)
    );
}

The preceding code outputs the following:

48656C6C6F20776F7264

What's more interesting, all the functions have additional overloads that accept a range as a first parameter instead of two iterators. Range is a concept from Ranges TS. Arrays and containers that have .begin() and .end() functions satisfy the range concept. With that knowledge the previous example could be shortened:

#include <boost/algorithm/hex.hpp>
#include <iterator>
#include <iostream>

void to_hex_test2() {
    const std::string data = "Hello word";
    boost::algorithm::hex(
        data,
        std::ostream_iterator<char>(std::cout)
    );
}

C++17 will have searching algorithms from Boost.Algorithm. Boost.Algorithm library will be soon extended with new algorithms and C++20 features like constexpr usable algorithms. Keep an eye on that library, as some day, it may get an out-of-the-box solution for a problem that you're dealing with.

See also

About the Author
  • Anton Polukhin Alekseevic

    Antony Polukhin If you are wondering who is Antony Polukhin and could he be trusted to teach about C++ and Boost libraries, then here are some facts: • Antony Polukhin currently represents Russia in international C++ standardization committee • He is the author of multiple Boost libraries and maintains (keeps an eye on) some of the old Boost libraries • He is a perfectionist: all the source codes from the book are auto tested on multiple platforms using different C++ standards. But let's start from the beginning. Antony Polukhin was born in Russia. As a child, he could speak the Russian and Hungarian languages and learned English at school. Since his school days, he was participating in different mathematics, physics, and chemistry competitions and winning them. He was accepted into University twice: once for taking part in a city mathematics competition and again for gaining high score in an University's mathematics and physics challenge. In his university life, there was a year when he did not participate in exams at all: he gained A's in all disciplines by writing highly difficult programs for each teacher. He met his future wife in university and graduated with honors. For more than three years, he worked in a VoIP company developing business logic for a commercial alternative to Asterisc. During those days he started contributing to Boost and became a maintainer of the Boost.LexicalCast library. He also started making translations to Russian for Ubuntu Linux at that time. Today, he works for Yandex Ltd., helps Russian speaking people with C++ standardization proposals, continues to contribute to the open source and to the C++ language in general. You may find his code in Boost libraries such as Any, Conversion, DLL, LexicalCast, Stacktrace, TypeTraits, Variant, and others. He has been happily married for more than five years.

    Browse publications by this author
Latest Reviews (2 reviews total)
Only 3 chapters in. The content has been generally good and I have already learned a few things I didn't know before (I have been using C++ and Boost for 10+ years professionally). The editing could be better but it hasn't been to distracting. One real criticism so far is that a lot of time is being spent on basically OBE Boost functionality _if_ you are using a C++11/14/17 conforming compiler: I wish more time was being spent on functionality that there is not already a standard based solution for provided you can use a modern enough compiler.
The best to start using boost
Boost C++ Application Development Cookbook - Second Edition
Unlock this book and the full library FREE for 7 days
Start now