Book Image

Boost C++ Application Development Cookbook - Second Edition

By : Anton Polukhin Alekseevic
Book Image

Boost C++ Application Development Cookbook - Second Edition

By: Anton Polukhin Alekseevic

Overview of 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.
Table of Contents (19 chapters)
Title Page
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

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.