Как обмануть boost :: asio, чтобы разрешить обработчики только для перемещения

В протоколе связи RPC после вызова метода I 'м отправкасделанный" сообщения обратно звонящему. Поскольку методы вызываются одновременно, буфер, содержащий ответ (std::string) должен быть защищен мьютексом. Что я'Я пытаюсь достичь следующего:

void connection::send_response()
{
    // block until previous response is sent
    std::unique_lock locker(response_mutex_);

    // prepare response
    response_ = "foo";

    // send response back to caller. move the unique_lock into the binder
    // to keep the mutex locked until asio is done sending.
    asio::async_write(stream_,
                      asio::const_buffers_1(response_.data(), response_.size()),
                      std::bind(&connection::response_sent, shared_from_this(),
                                _1, _2, std::move(locker))
                      );
}

void connection::response_sent(const boost::system::error_code& err, std::size_t len)
{
    if (err) handle_error(err);
    // the mutex is unlocked when the binder is destroyed
}

Однако это не компилируется, так какboost::asio требуетобработчики должны быть CopyConstructible.

Эту проблему можно обойти (хотя и не очень элегантно), используя вместо этого следующий общий класс locker:unique_lock

template 
class shared_lock
{
public:
    shared_lock(Mutex& m)
    : p_(&m, std::mem_fn(&Mutex::unlock))
    { m.lock(); }

private:
    std::shared_ptr p_;
};

В чем причинаboost::asio не разрешать обработчики только для перемещения?

 marton7824 июн. 2013 г., 12:36
@SamMiller: я использую Clang, который идет с Apple 's новейший Xcode (4.6.2) и Boost 1.53. На самой странице, на которую вы ссылаетесь, говорится следующее:Тем не менее, типы обработчиков по-прежнему должны быть копируемыми. " Это как раз мой вопрос: почему?
 R. Martinho Fernandes20 июн. 2013 г., 12:32
Привет маrton;) С первого взгляда я бы догадалсяпросто потому, что это неОн был обновлен до C ++ 11, как и многие другие библиотеки повышения. Печальное положение вещей :(
 Sam Miller20 июн. 2013 г., 16:38
какой компилятор и какая версия boost? Более поздние версии Boost.Asio действительно поддерживаютподвижные погрузчики.
 marton7824 июн. 2013 г., 12:38
@Massa I 'Мы подали ошибку, вот она для справки:svn.boost.org/trac/boost/ticket/8714
 Massa20 июн. 2013 г., 12:31
Это'наверное потому чтостарше C ++ 11, и они неу меня нет времени / внимания, чтобы это исправить. Посмотрите, так ли это в самой последнейboost и, если это так, сообщите об ошибке!

Ответы на вопрос(2)

Решение Вопроса

Пока Крис Колхофф не ответит на ошибку, ямы подали здесьПростой обходной путь:

template 
struct move_wrapper : F
{
    move_wrapper(F&& f) : F(std::move(f)) {}

    move_wrapper(move_wrapper&&) = default;
    move_wrapper& operator=(move_wrapper&&) = default;

    move_wrapper(const move_wrapper&);
    move_wrapper& operator=(const move_wrapper&);
};

template 
auto move_handler(T&& t) -> move_wrapper
{
    return std::move(t);
}

Оболочка объявляет конструктор копирования, обманывая asio 's механизм в представлении, но никогда не определяет его, так что копирование приведет к ошибке компоновки.

Теперь можно наконец сделать это:

std::packaged_task pt([] {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
});
std::future fu = pt.get_future();

boost::asio::io_service io;
io.post(move_handler(pt));
std::thread(&boost::asio::io_service::run, &io).detach();

int result = fu.get();
assert(result == 42);
 rustyx07 февр. 2016 г., 16:36
Есть ли какое-либо обновление структурного исправления для этого в Boost? Если нет, можете ли вы опубликовать ссылку на открытый дефект?
 marton7807 февр. 2016 г., 16:41
смотретьВот а такжеВот

Вот'Это более простое решение:

shared_ptr lock(mutex & m)
{
    m.lock();
    return shared_ptr(&m, mem_fn(&mutex::unlock));
}

Не нужно писать пользовательские обертки.

Ссылаясь наМетоды программирования Smart Pointer Вы даже можете использовать:

class shared_lock    {
private:    
    shared_ptr pv;    
public:    
    template explicit shared_lock(Mutex & m): pv((m.lock(), &m), mem_fn(&Mutex::unlock)) {}
};

shared_lock теперь можно использовать как:

shared_lock lock(m);

Обратите внимание, чтоshared_lock не на основеmutex типа, спасибоshared_ptrспособность скрывать информацию о типе.

Это может стоить дороже, но у него тоже есть кое-что (приемник может взятьshared_lock и вы можете передать ему обновляемую, совместно используемую, уникальную блокировку, scope_guard, в основном любого BasicLockable или "лучше"

 marton7830 янв. 2015 г., 15:56
Правда, это короче моего оригиналаshared_lock обертка, хотя и с большими накладными расходами, как вы и написали. Тем не менее, лучшим решением является IMHO просто притвориться, что ваш обработчик копируемый, объявив конструктор копирования и оператор присваивания копии. Поскольку asio никогда не использует их, связывание происходит успешно.
 Arne Vogel11 мая 2015 г., 14:06
В любой версии, если конструктор shared_ptr выбрасывает (например, std :: bad_alloc из-за выделения блока управления), мьютекс не будет разблокирован. Было бы лучше использовать unique_lock. Я'Я хотел бы перенести это непосредственно в Deleter, но глупо и бессмысленно, Deleters должен быть CopyConstructible, поэтому вместо этого: std :: unique_lock <мьютекс> л (м); shared_ptr <мьютекс> результат{&m, mem_fun (&Mutex :: разблокирование)}; l.release (); вернуть результат;
 sehe11 мая 2015 г., 14:16
@ArneVogel Нет распределения. Посмотри на это? Также в твоем наброске тыделает то же самоено Вы разблокируете мьютекс перед возвращением. Вы полностью упустили суть вопроса?

Ваш ответ на вопрос