Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return std::lock_guard in a std::pair

Tags:

c++

c++17

When I go to return a std::lock_guard in a std::pair from a function I get horrible errors. But when I package it in a class I have no problems (compiles and works as expected). I can't understand why. Details follow:

I devised a small template class to enable convenient locking and unlocking of shared objects. It's not particularly innovative, but C++17 allows it to be very compact and code read/write friendly:

template <typename T> class Locked {
public:
    Locked(T& _object, std::mutex& _mutex)
        : object(_object)
        , lock(_mutex)
    {
    }

    T&                          object;
    std::lock_guard<std::mutex> lock;
};

template <typename T> class Lockable {
public:
    Locked<T>       borrow() { return Locked(object, mutex); }
    Locked<const T> borrow() const { return Locked(object, mutex); }

private:
    T                  object;
    mutable std::mutex mutex;
};

It can be used like:

int main()
{
    Lockable<std::vector<int>> lv;

    auto [vec, lock] = lv.borrow();

    std::cout << vec.size() << std::endl;
}

My question is this. The Locked class is very thin. I thought I could use std::pair instead of a formal class, like so:

#include <iostream>
#include <mutex>
#include <utility>
#include <vector>

template <typename T> class Lockable {
public:
    std::pair<T&, std::lock_guard<std::mutex>>       borrow()
    { return std::pair(object, std::lock_guard<std::mutex>(mutex)); }
    std::pair<const T&, std::lock_guard<std::mutex>> borrow() const
    { return std::pair(object, std::lock_guard<std::mutex>(mutex)); }

private:
    T                  object;
    mutable std::mutex mutex;
};

int main()
{
    Lockable<std::vector<int>> lv;

    auto [vec, lock] = lv.borrow();

    std::cout << vec.size() << std::endl;
}

But this throws up horrible, hard to parse errors. I think this has to do with std::lock_guard not being movable, but to me, it looks exactly like my working code. Why does this second version not work?

like image 992
Kaushik Ghose Avatar asked Aug 25 '18 11:08

Kaushik Ghose


2 Answers

With some massaging Lockable does compile:

template <typename T>
class Lockable {
public:
    auto borrow()       { return std::pair<      T&, std::lock_guard<std::mutex>>{object, mutex}; }
    auto borrow() const { return std::pair<const T&, std::lock_guard<std::mutex>>{object, mutex}; }

private:
    T                  object;
    mutable std::mutex mutex;
};

Live example.

The idea is to explicitly specify std::lock_guard as a template argument in std::pair, but feed mutex as the corresponding constructor argument (indeed, the second version doesn't work because std::lock_guard isn't movable). Overload (3) of std::pair::pair will be used in this case.

(Also, since it is C++17, I would suggest to use std::scoped_lock instead of std::lock_guard).

like image 150
Dev Null Avatar answered Sep 28 '22 13:09

Dev Null


Why does this second version not work?

Among the many overloads for constructing std::pair, your code cannot resolve to any specific one. Now, besides Dev Null's correct and straight-forward solution here, I'm leaving this for further reference: You can forward-construct your std::lock_guard alongside passing your T& the way you want to by using the piecewise_construct_t version of std::pair()'s constructor:

template <typename T> class Lockable {
public:
    auto borrow()
    {
        return std::pair<T&, std::lock_guard<std::mutex>>(
            std::piecewise_construct,
            std::forward_as_tuple(object), std::forward_as_tuple(mutex));
    }

    auto borrow() const
    {
        return std::pair<T&, std::lock_guard<std::mutex>>(
                    std::piecewise_construct,
                    std::forward_as_tuple(object), std::forward_as_tuple(mutex));
    }

private:
    T                  object;
    mutable std::mutex mutex;
};

Note: In addition, I changed return type of borrow() to auto, as we're being enough explicit inside of it in terms of what is being returned.

like image 30
Geezer Avatar answered Sep 28 '22 13:09

Geezer