Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Boost C++ Application Development  Cookbook

You're reading from   Boost C++ Application Development Cookbook Recipes to simplify your application development

Arrow left icon
Product type Paperback
Published in Aug 2017
Publisher Packt
ISBN-13 9781787282247
Length 438 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Anton Polukhin Alekseevic Anton Polukhin Alekseevic
Author Profile Icon Anton Polukhin Alekseevic
Anton Polukhin Alekseevic
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Starting to Write Your Application FREE CHAPTER 2. Managing Resources 3. Converting and Casting 4. Compile-Time Tricks 5. Multithreading 6. Manipulating Tasks 7. Manipulating Strings 8. Metaprogramming 9. Containers 10. Gathering Platform and Compiler Information 11. Working with the System 12. Scratching the Tip of the Iceberg

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.
You have been reading a chapter from
Boost C++ Application Development Cookbook - Second Edition
Published in: Aug 2017
Publisher: Packt
ISBN-13: 9781787282247
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime