Играюсь с c++11 и появилась идея модернизировать асинхронные вызовы в своем проекте с использованием лямбд. Проблема в следующем: когда делаем асинхронный запрос и в качестве функции обратного вызова указываем лямбду, которая меняет состояние вызывающего объекта, к тому моменту, когда будет ответ готов, вызывающий объект может быть удален. Пример который это демонстрирует:
#include <iostream>
#include <vector>
#include <string>
#include <functional>
#include <memory>
#include <algorithm>
// Имитатор асинхронного объекта
class object
{
public:
typedef std::function<void(const std::string&) > callback;
// Асинхронный метод
virtual void async_method(const std::string& str, callback&& clb)
{
// Запоминаем аргумент и функцию обратного вызова
calls.push_back( std::make_pair(str, std::move(clb) ) );
}
// Выполняем все запрос пачкой
void doit()
{
for (auto& c : calls)
{
std::reverse(c.first.begin(), c.first.end() );
c.second(c.first);
}
calls.clear();
}
private:
std::vector< std::pair<std::string, callback> > calls;
};
// Имитатор вызывающего объекта
class subject
{
public:
subject(object* obj)
: _obj(obj)
{
}
void method(const std::string& str)
{
// вызываем асинхронный метод
_obj->async_method(str, [this](const std::string& str)
{
// ответ готов, вызываем метод который меняет состояние объекта
this->ready(str);
});
}
void ready(const std::string& str)
{
// для примера просто выводим строку
std::cout << "ready " << str << std::endl;
}
private:
object* _obj;
};
int main(int argc, char **argv)
{
object* obj = new object;
subject* s1 = new subject(obj);ю
subject* s2 = new subject(obj);
s1->method("1234");
s2->method("5678");
// удаляем один из вызывающих объектов
delete s1;
// выполняем все запросы
obj->doit();
delete obj;
delete s2;
return 0;
}
Т.к. у вызывающего объекта нет состояния этот пример сработает и выведет:
ready 4321
ready 8765
но по идее должен свалиться при выводе первой строки (если бы имел состояние), т.к. объект s1 был удален.
Решение пришло следующее (на умных указателях):
class subject
{
public:
subject(object* obj)
: _obj(obj)
, _pthis( std::make_shared<subject*>(this) ) // subject**
{
}
void method(const std::string& str)
{
std::weak_ptr<subject*> wthis = _pthis;
_obj->async_method(str, [wthis](const std::string& str)
{
// если объект был удален, то условие не сработает
if (auto pthis = wthis.lock())
{
(*pthis)->ready(str);
}
});
}
void ready(const std::string& str)
{
std::cout << " ready " << str << std::endl;
}
private:
object* _obj;
std::shared_ptr<subject*> _pthis;
};
Вопросы:
1. Есть-ли тут подводные камни?
2. Есть-ли стандартный кейс решения этой проблемы? ткните носом плз