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

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.