#c #callback #c 17 #smart-pointers
#c #обратный вызов #c 17 #интеллектуальные указатели
Вопрос:
До C 17
Я хотел бы убедиться, что объект является живым на время выполнения его обратных вызовов.
Это был важный момент. Рассмотрим функцию обратного вызова, которая запрашивает данные от объекта, передающего обратный вызов. Теперь я хочу сохранить излучающий объект живым до тех пор, пока указанная функция обратного вызова не получит то, что ей нужно. Это может быть не для всех обратных вызовов, не все обратные вызовы имеют дело с объектами, которые могут быть удалены в следующий раз.
Это то, что я придумал. Используя идиому именованного конструктора, я могу убедиться, что созданный объект является shared_ptr , а затем я могу использовать для этого weak_ptr и так далее.
Интересно, есть ли лучший способ обойти это?
// Example program
#include <memory>
#include <functional>
#include <iostream>
class Callback
{
public:
using t_callback_func = std::function<void(std::shared_ptr < Callback>)>;
using t_shared = std::shared_ptr<Callback>;
std::vector<t_callback_func> callbacks;
std::weak_ptr<Callback> uglyhax;
static std::shared_ptr<Callback> make()
{
auto shared = std::shared_ptr<Callback>(new Callback());
shared->uglyhax = shared;
return shared;
}
void register_callback(std::function<void(std::shared_ptr<Callback>)> amp;amp; p_callback)
{
callbacks.emplace_back(std::forward<t_callback_func>(p_callback));
}
void notify()
{
if (auto shared = uglyhax.lock())
{
for (auto amp; callback : callbacks)
{
callback(shared);
}
}
}
void remove_callbacks()
{
callbacks.clear();
}
void hello()
{
std::cout << "hello " << this << "n";
}
~Callback() { std::cout << "dtor " << this << "n"; }
protected:
Callback() { std::cout << "ctor " << this << "n"; }
};
class EventList
{
std::vector<std::function<void()>> events;
public:
void call_events()
{
for (autoamp; event : events)
{
event();
}
events.clear();
}
template<class ...Args>
auto wrap_as_event(std::function<void(Args...)> amp;amp; p_func)
{
return [this, p_func](Args... args)
{
std::cout << "adding eventn";
this->events.emplace_back(std::move(std::bind(std::move(p_func), std::forward<Args>(args)...)));
};
}
};
int main()
{
EventList list;
{
{
auto thing = Callback::make(); // I want to keep the thing allocated here alive until all its callbacks have been processed
std::function<void(Callback::t_shared)> function = [](Callback::t_shared ptr) {ptr->hello(); ptr->remove_callbacks(); };
thing->register_callback(list.wrap_as_event(std::move(function)));
thing->notify();
}
std::cout << "dtor should be called after thisn";
list.call_events();
}
std::cout << "dtor should be called before thisn";
}
Вывод:
ctor 013E81C0
adding event
dtor should be called after this
hello 013E81C0
dtor 013E81C0
dtor should be called before this
Комментарии:
1. Так много запутанных аспектов этого. В моей голове есть много лучших способов сделать это… но я предполагаю, что сложность связана с требованиями, которые, возможно, неясны из вопроса?
2. «Используя идиому именованного конструктора, я могу убедиться, что созданный объект является
shared_ptr
» . Вы также должны защитить от конструктора копирования.3. Возможно, вам следует показать некоторый код исходной проблемы, которую вы пытаетесь обойти. Это далеко не ясно. Совершенно ясно, что это большой беспорядок. 🙂
4. Суть в том, что я хочу сохранить исходный объект живым до тех пор, пока не будут обработаны его обратные вызовы. Так что любой, кто интересуется его данными, все равно может получить к нему доступ.
5.
uglyhax
Используется, чтобы избежатьstd::enable_shared_from_this
?
Ответ №1:
Вы можете отделить логику выполнения события от события и объекта полезной нагрузки события.
Это Callable
наследование enable_shared_from_this<Callable>
, которое позволяет безопасно создавать дополнительные shared_ptr
. Заводской метод getCallable создаст экземпляр Callable в куче, что является необходимым условием для enible_shared_from_this
работы.
Callable
Класс знает, как генерировать события и как зарегистрировать их shared_ptr
в CallableEvent
специализированном классе Event
. Затем Event
он отправляется на EventHandler
который будет обрабатывать событие.
// Example program
#include <memory>
#include <functional>
#include <iostream>
#include <vector>
class Event;
class Callable;
class CallableEvent;
class Event {
public:
virtual void trigger();
};
class EventHandler {
public:
std::vector<std::shared_ptr<Event>> eventList;
static EventHandleramp; getInstance() {
static EventHandler instance = EventHandler();
return instance;
}
void registerEvent(std::shared_ptr<Event> event) {
eventList.push_back(event);
}
void processEvents() {
for (auto event : eventList) {
event->trigger();
}
}
};
class CallableEvent : public Event, public std::enable_shared_from_this<CallableEvent> {
public:
using t_callback = std::function<void(std::shared_ptr<Callable>)>;
std::shared_ptr<Callable> callable;
t_callback callback;
void trigger() override;
CallableEvent() { std::cout << "Callable Event Created " << this << "n"; }
~CallableEvent() { std::cout << "Callable Event Destroyed " << this << "n"; }
};
class Callable : public std::enable_shared_from_this<Callable> {
public:
int _payload;
void registerEvent(std::function<void(std::shared_ptr<Callable>)> lambda) {
std::shared_ptr<CallableEvent> ev = std::shared_ptr<CallableEvent>(new CallableEvent());
ev->callable = shared_from_this();
ev->callback = std::move(lambda);
EventHandler::getInstance().registerEvent(ev);
}
static std::shared_ptr<Callable> createCallable(int payload) {
return std::shared_ptr<Callable>(new Callable(payload));
}
void sayThis() {
std::cout << "Hello from Callable "<< this << " my payload is " << _payload << "n";
}
~Callable() {
std::cout << "Callable destroyed " << this << " with payload " << _payload << "n";
}
private:
Callable(int payload) {
_payload = payload;
std::cout << "Callable created " << this << " with payload " << _payload << "n";
}
};
void Event::trigger() {
std::cout << "generic event n";
}
void CallableEvent::trigger() {
callback(callable);
}
int main()
{
{
{
auto cb = Callable::createCallable(15);
CallableEvent::t_callback function = [](std::shared_ptr<Callable> ptr) {
std::cout << "Calling from lambdan";
ptr->sayThis();
};
cb->registerEvent(function);
}
std::cout << "Callback should be called after thisn";
EventHandler::getInstance().processEvents();
}
std::cout << "Callback should be called before thisn";
}
При запуске примера выводится следующий вывод:
Callable created 0x7f89f4c057d0 with payload 15
Callable Event Created 0x7f89f4c05810
Callback should be called after this
Calling from lambda
Hello from Callable 0x7f89f4c057d0 my payload is 15
Callback should be called before this
Callable Event Destroyed 0x7f89f4c05810
Callable destroyed 0x7f89f4c057d0 with payload 15
Process finished with exit code 0
Теперь вы можете переместить обработчик в другой поток, и события будут выполняться асинхронно.
Комментарии:
1. а? Я спрашивал, как сохранить объект, отправляющий обратный вызов, я не понимаю этого ответа. Я проясню вопрос.
2. Да, это идея, к которой я стремлюсь. Пример просто упрощен. Это уже слишком сложно.
3. @0xbaadf00d Я думаю, что у меня есть идея, но позвольте мне уточнить цель: у вас будет некоторый код, который будет генерировать события и связанные объекты обратного вызова. Вы хотите, чтобы события обрабатывались до освобождения обратного вызова. Вам нужно, чтобы они обрабатывались до определенного времени, и вам нужны результаты обратного вызова в контексте, в котором вы сгенерировали обработчик обратного вызова?
4. Я бы хотел, чтобы создатель обратного вызова мог принимать решения. Вот почему я использовал shared_ptr, поскольку он сам поточно-безопасный. Контекст может измениться, обратный вызов имеет возвращаемый тип void, поэтому исходный компонент не может получить через него никакого результата. По сути, я хочу, чтобы объекты обратного вызова сохраняли исходный объект в живых. До тех пор, пока объект обратного вызова сам по себе активен.
5. @0xbaadf00d Нет. вы создали лямбда-выражение, которое принимает a
std::shared_ptr
в качестве входного аргумента. Вместо этого просто захватите значениеstd::shared_ptr
by, и обратный вызов будет поддерживать его до тех пор, пока он существует.