Archive

Posts Tagged ‘C++0x’

std::thread and std::future in C++11

December 12th, 2014 No comments

This is a quick note to chapter 4 of C++ Concurrency in Action.

1. std::thread

In C++11, It’s quite simple to create a separate thread using std::thread. Following code will simply output “hello world” or “world hello”:

#include <iostream>
#include <thread>
using namespace std;

void foo(const char *s) {
    cout << s << endl;
}

int main() {
    thread t(foo, "hello");
    cout << "world" << endl;
    /* destructor of std::thread calls std::terminate(), so we should call join() manually. */
    t.join();
    return 0;
}

2. std::mutex and std::condition_variable

If you need synchronization between threads, there are std::mutex and std::condition_variable. The semantics are the same with that in pthread library. Here’s a simple producer/consumer demo:

#include <iostream>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
using namespace std;

queue<int> q;
mutex m;
condition_variable c;
const chrono::milliseconds ms(1000);

void producer() {
    static int i = 0;
    while (true) {
        m.lock();
        cout << "pushing " << i << endl;
        q.push(i++);
        c.notify_one();
        m.unlock();
        this_thread::sleep_for(ms);
    }
}

void consumer() {
    while (true) {
        unique_lock<mutex> lk(m);
        c.wait(lk, [](){ return !q.empty(); });
        int i = q.front();
        cout << "popping " << i << endl;
        q.pop();
        lk.unlock();
        this_thread::sleep_for(ms);
    }
}

int main() {
    thread t(consumer);
    thread t2(producer);
    t.join();
    t2.join();
    return 0;
}

3. std::future with std::async()

C++11 also simplifies our work with one-off events with std::future. std::future provides a mechanism to access the result of asynchronous operations. It can be used with std::async(), std::packaged_task and std::promise. Starting with std::async():

#include <iostream>
#include <future>
using namespace std;

void foo(const char *s) {
    cout << s << endl;
}

int bar(int a, int b) {
    return a + b;
}

int main() {
    /* auto will be simpler */
    future<void> f = std::async(foo, "hello");
    future<int> f2 = std::async(launch::async, bar, 1, 2);
    /* f.get() is required if f is deferred by the library */
    //f.get();
    /* std::async() can return a value */
    cout << "1 + 2 = " << f2.get() << endl;
    /* threads created by std::async() are joined automatically */
    return 0;
}

std::async() gives two advantages over the direct usage of std::thread. Threads created by it are automatically joined. And we can now have a return value. std::async() decides whether to run the callback function in a separate thread or just in the current thread. But there’s a chance to specify a control flag(launch::async or launch::deferred) to tell the library, what approach we want it to run the callback.

When testing With gcc-4.8, foo() is not called. But with VC++2013, it does output “hello”.

4. std::future with std::packaged_task

With std::async(), we cannot control when our callback function is invoked. That’s what std::packaged_task is designed to deal with. It’s just a wrapper to callables. We can request an associated std::future from it. And when a std::packaged_task is invoked and finished, the associated future will be ready:

#include <iostream>
#include <future>
using namespace std;

void foo() {
    cout << "in pt.." << endl;
}

int bar(int a, int b) {
    cout << "in pt2.." << endl;
    return a + b;
}

/* associate with tasks */
packaged_task<void()> pt(foo);
packaged_task<int(int,int)> pt2(bar);

void waiter() {
    /* get associated future */
    auto f = pt.get_future();
    /* wait here */
    f.get();
    cout << "after f.get().." << endl;
}

void waiter2() {
    auto f2 = pt2.get_future();
    f2.get();
    cout << "after f2.get().." << endl;
}

int main() {
    auto t = std::async(launch::async, waiter);
    auto t2 = std::async(launch::async, waiter2);
    /* associated futures will be ready when the packaged tasks complete */
    pt();
    pt2(1, 2);
    return 0;
}

In waiter() and waiter2(), future::get() blocks until the associating std::packaged_task completes. You will always get “in pt” before “after f.get()” and “in pt2” before “after f2.get()”. They are synchronized.

5. std::future with std::promise

You may also need to get notified in the middle of a task. std::promise can help you. It works like a lightweight event.

Future and Promise are the two separate sides of an asynchronous operation. std::promise is used by the “producer/writer”, while std::future is used by the “consumer/reader”. The reason it is separated into these two interfaces is to hide the “write/set” functionality from the “consumer/reader”:

#include <iostream>
#include <future>
using namespace std;

promise<bool> p;
promise<int> p2;

void waiter() {
    /* get associated future */
    auto f = p.get_future();
    /* wait here */
    f.get();
    cout << "after f.get().." << endl;
}

void waiter2() {
    auto f2 = p2.get_future();
    try {
        f2.get();
    } catch (...) {
        /* caught exception */
        cout << "caught exception in f2.get().." << endl;
    }
    cout << "after f2.get().." << endl;
}

int main() {
    auto t = std::async(launch::async, waiter);
    auto t2 = std::async(launch::async, waiter2);
    /* associated futures will be ready after a value is set */
    cout << "setting p.." << endl;
    p.set_value(true);
    /* exceptions can also be set */
    cout << "setting p2.." << endl;
    p2.set_exception(std::exception_ptr(nullptr));
    return 0;
}

Again in waiter() and waiter2(), future::get() blocks until a value or an exception is set into the associating std::promise. So “setting p” is always before “f.get()” and “setting p2” is always before “f2.get()”. They are synchronized.

NOTE: std::future seems to be not correctly implemented in VC++2013. So the last two code snippet do not work with it. But you can try the online VC++2015 compiler(still in preview as this writing), it works.

Categories: C/C++ Tags: ,

Smart Pointers in C++0x and Boost (2)

August 14th, 2011 No comments

1. Environment

– windows xp
– gcc-4.4
– boost-1.43

2. auto_ptr

A smart pointer is an abstract data type that simulates a pointer while providing additional features, such as automatic garbage collection or bounds checking. There’s auto_ptr in C++03 library for general use. But it’s not so easy to deal with it. You may encounter pitfalls or limitations. The main drawback of auto_ptr is that it has the transfer-of-ownership semantic. I just walk through it. Please read comments in code carefully:

int *test_auto_ptr_exp() {
    auto_ptr<int> p(new int(1));
    throw runtime_error("auto_ptr test exception.");
    /* exception-safe, p is free even when an exception is thrown. */
    return p.get();
}

void test_auto_ptr_basic() {
    auto_ptr<int> p1(new int(1));
    auto_ptr<int> p2(new int(2));
    auto_ptr<int> p3(p1);
    auto_ptr<int> p4;
    p4 = p2;
    if (p1.get()) {  /* NULL */
        cout << "*p1=" << *p1 << endl;
    }
    if (p2.get()) {  /* NULL */
        cout << "*p2=" << *p2 << endl;
    }
    if (p3.get()) {  /* ownership already transferred from p1 to p3 */
        cout << "*p3=" << *p3 << endl;
    }
    if (p4.get()) {  /* ownership already transferred from p2 to p4 */
        cout << "*p4=" << *p4 << endl;
    }
    /* ERROR: void is a type of template specialization */
    //auto_ptr<void> ptr5(new int(3));
}

void test_auto_ptr_errors() {
    /* ERROR: statically allocated object */
    const char *str = "Hello";
    auto_ptr<const char> p1(str);
    /* ERROR: two auto_ptrs refer to the same object */
    int *pi = new int(5);
    auto_ptr<int> p2(pi);
    auto_ptr<int> p3(p2.get());
    p2.~auto_ptr();  /* now p3 is not available too */
    /* ERROR: hold a pointer to a dynamically allocated array */
    /* When destroyed, it only deletes first single object. */
    auto_ptr<int> (new int[10]);
    /* ERROR: store an auto_ptr in a container */
    //vector<auto_ptr<int> > vec;
    //vec.push_back(auto_ptr<int>(new int(1)));
    //vec.push_back(auto_ptr<int>(new int(2)));
    //auto_ptr<int> p4(vec[0]);  /* vec[0] is assigned NULL */
    //auto_ptr<int> p5;
    //p5 = vec[1];  /* vec[1] is assigned NULL */
}

3. unique_ptr

To resolve the drawbacks, C++0x deprecates usage of auto_ptr, and unique_ptr is the replacement. unique_ptr makes use of a new C++ langauge feature called rvalue reference which is similar to our current (left) reference (&), but spelled (&&). GCC implemented this feature in 4.3, but unique_ptr is only available begin from 4.4.

What is rvalue?

rvalues are temporaries that evaporate at the end of the full-expression in which they live (“at the semicolon”). For example, 1729, x + y, std::string(“meow”), and x++ are all rvalues.

While, lvalues name objects that persist beyond a single expression. For example, obj, *ptr, ptr[index], and ++x are all lvalues.

NOTE: It’s important to remember: lvalueness versus rvalueness is a property of expressions, not of objects.

We may have another whole post to address the rvalue feature. Now, let’s take a look of the basic usage. Please carefully reading the comments:

unique_ptr<int> get_unique_ptr(int i) {
    return unique_ptr<int> (new int(i));
}

void use_unique_ptr(unique_ptr<int> p) {
    /* p is deleted when finish running this function. */
}

void test_unique_ptr_basic() {
    unique_ptr<int> p(new int(1));
    /*
     * One can make a copy of an rvalue unique_ptr.
     * But one can not make a copy of an lvalue unique_ptr.
     * Note the defaulted and deleted functions usage in source code(c++0x).
     */
    //unique_ptr<int> p2 = p;  /* error */
    //use_unique_ptr(p);       /* error */
    use_unique_ptr(move(p));
    use_unique_ptr(get_unique_ptr(3));
}

One can ONLY make a copy of an rvalue unique_ptr. This confirms no ownership issues occur like that of auto_ptr. Since temporary values cannot be referenced after the current expression, it is impossible for two unique_ptr to refer to a same pointer. You may also noticed the move function. We will also discuss it in a later post.

Some more snippet:

struct aclass {
    aclass() { cout << "in aclass::ctor()" << endl; }
    ~aclass() { cout << "in aclass::dtor()" << endl; }
};

struct aclass_deleter {
    void operator()(void *p) {
        delete static_cast<aclass *> (p);
    }
};

template<class T>
struct array_deleter {
    void operator()(T *array) {
        delete[] array;
    }
};

typedef array_deleter<aclass> aclass_array_deleter;
typedef unique_ptr<aclass, aclass_array_deleter> aclass_array_ptr;

void test_unique_ptr_custom_deleter() {
    /*
     * Hold a pointer to a dynamically allocated array.
     * aaptr & aaptr2 are deleted when finish running this function.
     */
    aclass_array_ptr aaptr(new aclass[3]);
    unique_ptr<aclass[]> aaptr2(new aclass[3]);  /* default_deleter<T[]> */
    /* allow void pointer, but a custom deleter must be used. */
    unique_ptr<void, aclass_deleter> p3(new aclass);
}

unique_ptr can hold pointers to an array. unique_ptr defines deleters to free memory of its internal pointer. There are pre-defined default_deleter using delete and delete[](array) for general deallocation. You can also define your customized ones. In addition, a void type can be used.

NOTE: To compile the code, you must specify the -std=c++0x flag.

4. shared_ptr

A shared_ptr is used to represent shared ownership; that is, when two pieces of code needs access to some data but neither has exclusive ownership (in the sense of being responsible for destroying the object). A shared_ptr is a kind of counted pointer where the object pointed to is deleted when the use count goes to zero.

Following snippet shows the use count changes when using shared_ptr. The use count changes from 0 to 3, then changes back to 0:

struct bclass {
    int i;
    bclass(int i) { this->i = i; }
    virtual ~bclass() { cout << "in bclass::dtor() with i=" << i << endl; }
};

struct cclass: bclass {
    cclass(int i) : bclass(i) { }
    virtual ~cclass() { cout << "in cclass::dtor() with i=" << i << endl; }
};

void use_shared_ptr(shared_ptr<int> p) {
    cout << "count=" << p.use_count() << endl;
}

void test_shared_ptr_basic() {
    shared_ptr<int> p;
    cout << "count=" << p.use_count() << endl;
    p.reset(new int(1));
    cout << "count=" << p.use_count() << endl;
    shared_ptr<int> p2 = p;
    cout << "count=" << p.use_count() << endl;
    use_shared_ptr(p2);
    cout << "count=" << p.use_count() << endl;
    p2.~shared_ptr();
    cout << "count=" << p.use_count() << endl;
    p2.~shared_ptr();
    cout << "count=" << p.use_count() << endl;
}

Snippets showing pointer type conversion:

void test_shared_ptr_convertion() {
    /* p is deleted accurately without custom deleter */
    shared_ptr<void> p(new aclass);
    /* use parent type to hold child object */
    shared_ptr<bclass> p2(new cclass(10));
    shared_ptr<cclass> p3 = static_pointer_cast<cclass> (p2);
    cout << "p3->i=" << p3->i << endl;
    p3->i = 20;
    cout << "p2->i=" << p2->i << endl;
}

The void type can be used directly without a custom deleter, which is required in unique_ptr. Actually, shared_ptr has already save the exact type info in its constructor. Refer to source code for details :). And static_pointer_cast function is used to convert between pointer types.

Unlike auto_ptr, Since shared_ptr can be shared, it can be used in STL containers:

typedef shared_ptr<bclass> bclass_ptr;

struct bclass_ops {
    void operator()(const bclass_ptr&amp; p) {
        cout << p->i << endl;
    }
    bool operator()(const bclass_ptr& a, const bclass_ptr& b) {
        return a->i < b->i;
    }
};

void test_shared_ptr_containers() {
    vector<bclass_ptr> vec1, vec2;
    bclass_ptr ptr(new bclass(1));
    vec1.push_back(ptr);
    vec2.push_back(ptr);
    ptr.reset(new bclass(2));
    vec1.push_back(ptr);
    vec2.push_back(ptr);
    ptr.reset(new bclass(3));
    vec1.push_back(ptr);
    vec2.push_back(ptr);
    for_each(vec1.begin(), vec1.end(), bclass_ops());
    reverse(vec2.begin(), vec2.end());
    for_each(vec2.begin(), vec2.end(), bclass_ops());
}

NOTE: shared_ptr is available in both TR1 and Boost library. You can use either of them, for their interfaces are compatible. In addition, there are dual C++0x and TR1 implementation. The TR1 implementation is considered relatively stable, so is unlikely to change unless bug fixes require it.

5. weak_ptr

weak_ptr objects are used for breaking cycles in data structures. See snippet:

struct mynode {
    int i;
    shared_ptr<mynode> snext;
    weak_ptr<mynode> wnext;
    mynode(int i) { this->i = i; }
    ~mynode() { cout << "in mynode::dtor() with i=" < i < endl; }
};

void test_weak_ptr() {
    shared_ptr<mynode> head(new mynode(1));
    head->snext = shared_ptr<mynode>(new mynode(2));
    /* use weak_ptr to solve cyclic dependency */
    //head->snext = head;
    head->wnext = head;
}

If we use uncomment to use shared_ptr, head is not freed since there still one reference to it when exiting the function. By using weak_ptr, this code works fine.

6. scoped_ptr

scoped_ptr template is a simple solution for simple needs. It supplies a basic “resource acquisition is initialization” facility, without shared-ownership or transfer-of-ownership semantics.

This class is only available in Boost. Since unique_ptr is already there in C++0x, this class may be thought as redundant. Snippet is also simple:

void test_scoped_ptr() {
    /* simple solution for simple needs */
    scoped_ptr<aclass> p(new aclass);
}

Complete and updated code can be found on google code host here. I use conditional compilation to swith usage between TR1 and Boost implementation in code. Hope you find it useful.

Categories: C/C++ Tags: ,

Smart Pointers in C++0x and Boost

August 14th, 2011 No comments

Let clarify some concepts first. What is C++0x? Wikipedia gives some overview here:

C++0x is intended to replace the existing C++ standard, ISO/IEC 14882, which was published in 1998 and updated in 2003. These predecessors are informally but commonly known as C++98 and C++03. The new standard will include several additions to the core language and will extend the C++ standard library, incorporating most of the C++ Technical Report 1 (TR1) libraries — with the exception of the library of mathematical special functions.

Then why it is called C++0x? As Bjarne Stroustrup addressed here:

The aim is for the ‘x’ in C++0x to become ‘9’: C++09, rather than (say) C++0xA (hexadecimal :-).

You may also noticed TR1, also refer here in Wikipedia:

C++ Technical Report 1 (TR1) is the common name for ISO/IEC TR 19768, C++ Library Extensions, which is a document proposing additions to the C++ standard library. The additions include regular expressions, smart pointers, hash tables, and random number generators. TR1 is not a standard itself, but rather a draft document. However, most of its proposals are likely to become part of the next official standard.

You got the relationship? C++0x is the standard adding features to both language and standard library. A large set of TR1 libraries and some additional libraries. For instance, unique_ptr is not defined in TR1, but is included in C++0x.

As of 12 August 2011, the C++0x specification has been approved by the ISO.

Another notable concept is the Boost library. It can be regarded as a portable, easy-to-use extension to the current C++03 standard library. And some libraries like smart pointers, regular expressions have already been included in TR1. You can find license headers regarding the donation of the boost code in libstdc++ source files. While in TR2, some more boost code are to be involved.

TR1 libraries can be accessed using std::tr1 namespace. More info on Wikipedia here:

Various full and partial implementations of TR1 are currently available using the namespace std::tr1. For C++0x they will be moved to namespace std. However, as TR1 features are brought into the C++0x standard library, they are upgraded where appropriate with C++0x language features that were not available in the initial TR1 version. Also, they may be enhanced with features that were possible under C++03, but were not part of the original TR1 specification.

The committee intends to create a second technical report (called TR2) after the standardization of C++0x is complete. Library proposals which are not ready in time for C++0x will be put into TR2 or further technical reports.

The article seems to be a bit too long so far, I decide to give my snippets in a later post.

Categories: C/C++ Tags: ,