Defined in <stlab/scope.hpp>
template <typename T, typename... Args>
inline auto scope(Args&&... args)
(1)
  1. Scopes the lifetime of an instance of T. All but the last parameters are used to construct T, while the last parameter is assumed to be a nullary function, and is called. After the nullary function goes out of scope, T is destroyed.

Parameters

[ args[0], args[N-1] )

Range of parameters forwarded to the constructor of T

args[N-1]

A callable nullary function

Oftentimes developers will add scopes to code where they want to limit the lifetime of an object. For example:

void pop_and_run_task() {
    std::function<void()> task;

    {
    std::lock_guard<std::mutex> lock(m);
    task = pop_front_unsafe(task_queue);
    }

    task();
}

The challenge is discerning the developer’s intent by adding the scope. scope allows construction of any object and binds its lifetime to the duration of a passed function. Using scope, the example above becomes:

void pop_and_run_task() {
    std::function<void()> task = stlab::scope<std::lock_guard<std::mutex>>(m, [&](){
        return pop_front_unsafe(task_queue);
    });

    task();
}

With scope, the developer’s intent is clear: they want the lifetime of the lock to be bound to the scope that encloses it.

Example 1

#include <iostream>
#include <mutex>
#include <thread>
#include <stlab/scope.hpp>

using namespace stlab;
using namespace std;

mutex m;

void scoped() {
    stlab::scope<lock_guard<mutex>>(m, [&](){
        cout << "Hello, world!\n";
    });
}

int main(int, char**) {
    auto t0 = thread(&scoped);
    auto t1 = thread(&scoped);
    auto t2 = thread(&scoped);

    t0.join();
    t1.join();
    t2.join();
}
/*
    Result:

        Hello, world!
        Hello, world!
        Hello, world!
*/

Example 2

#include <iostream>
#include <mutex>
#include <thread>
#include <deque>

#include <stlab/scope.hpp>
#include <stlab/concurrency/future.hpp>
#include <stlab/concurrency/serial_queue.hpp>
#include <stlab/concurrency/default_executor.hpp>

using namespace stlab;
using namespace std;

auto pop_fn() {
    return []{
        static deque<int> q{1, 2, 3, 4};
        static mutex      m;
        return scope<lock_guard<mutex>>(m, []{
            auto v = move(q.front());
            q.pop_front();
            return v;
        });
    };
}

int main(int, char**) {
    executor_t           ioq(serial_queue_t(default_executor).executor());
    vector<future<void>> futures;
    auto                 popper(pop_fn());

    for (std::size_t i(0); i < 4; ++i)
        futures.emplace_back(stlab::async(default_executor, popper)
            .then(ioq, [](int x) {
                cout << x << '\n';
            }));

    auto done = when_all(default_executor,
                         []{},
                         std::make_pair(begin(futures), end(futures)));

    while (!done.get_try()) ;
}
/*
    Result:

        1
        2
        3
        4
*/