C++ 在c++;
我正在使用一些API来获取通知。比如:C++ 在c++;,c++,c++11,callback,synchronization,raii,C++,C++11,Callback,Synchronization,Raii,我正在使用一些API来获取通知。比如: NOTIF_HANDLE register_for_notif(CALLBACK func, void* context_for_callback); void unregister_for_notif(NOTIF_HANDLE notif_to_delete); class NotifHandle { public: NotifHandle(void (*callback_fn)(void *), void * context)
NOTIF_HANDLE register_for_notif(CALLBACK func, void* context_for_callback);
void unregister_for_notif(NOTIF_HANDLE notif_to_delete);
class NotifHandle {
public:
NotifHandle(void (*callback_fn)(void *), void * context)
: _handle(register_for_notif(callback_fn, context)) {}
~NotifHandle() { unregister_for_notif(_handle); }
private:
NOTIF_HANDLE _handle;
};
class NotifClass {
public:
NotifClass(std::shared_ptr<MyEvent> event)
: _event(event),
_handle(my_notif_callback, (void*)this) {}
~NotifClass() {}
static void my_notif_callback(void* context) {
((NotifClass*)context)->_event->set_event();
}
private:
std::shared_ptr<MyEvent> _event;
NotifHandle _handle;
};
我想把它封装在一个像样的RAII类中,该类将在收到通知时设置一个事件。我的问题是如何同步它。我写了这样的东西:
class NotifClass
{
public:
NotifClass(std::shared_ptr<MyEvent> event):
_event(event),
_notif_handle(register_for_notif(my_notif_callback, (void*)this))
// initialize some other stuff
{
// Initialize some more stuff
}
~NotifClass()
{
unregister_for_notif(_notif_handle);
}
void my_notif_callback(void* context)
{
((NotifClass*)context)->_event->set_event();
}
private:
std::shared_ptr<MyEvent> _event;
NOTIF_HANDLE _notif_handle;
};
类NotifClass
{
公众:
NotifClass(标准::共享事件):
_事件(事件),,
_notif句柄(为notif(我的notif回调,(void*)this)注册)
//初始化一些其他东西
{
//初始化更多的东西
}
~NotifClass()
{
为\u notif(\u notif\u handle)注销\u;
}
void my_notif_回调(void*上下文)
{
((NotifClass*)上下文)->事件->设置事件();
}
私人:
std::共享的ptr事件;
NOTIF_句柄_NOTIF_句柄;
};
但我担心在构造\销毁过程中调用回调(可能在这个特定示例中,shared_ptr可以使用它,但在其他构造类中可能不一样)
我要再说一遍-我不想为这个非常特定的类提供一个非常特定的解决方案,但在传递回调时,我想为RAII提供一个更通用的解决方案。AFAICT,您担心
my\u notif\u回调
可以与析构函数并行调用,并且上下文
可以是一个悬空指针。这是一个合理的担忧,我认为你不能用一个简单的锁定机制来解决它
相反,您可能需要使用共享指针和弱指针的组合来避免此类悬空指针。例如,为了解决您的问题,您可以将事件存储在小部件中,它是共享的\u ptr
,然后您可以创建弱\u ptr
到小部件中,并将其作为上下文传递到注册\u notif
换句话说,NotifClass
对Widget
具有asshare\ptr
,而上下文是Widget
的弱\ptr
。如果无法锁定弱\u ptr
类,则该类已被破坏:
类NotifClass
{
公众:
NotifClass(const std::shared_ptr&event):
_小部件(std::make_shared(event)),
_notif\u句柄(注册notif(我的notif回调,(void*)新std::弱ptr(\u小部件)))
//初始化一些其他东西
{
//初始化更多的东西
}
~NotifClass()
{
为\u notif(\u notif\u handle)注销\u;
}
静态void my\u notif\u回调(void*上下文)
{
自动ptr=((std::weak_ptr*)上下文)->lock();
//如果已销毁,则不要设置事件。
如果(!ptr){
返回;
}
ptr->_事件->设置_事件();
}
私人:
结构小部件{
小部件(const std::shared_ptr&event)
:_事件(事件){}
std::共享的ptr事件;
};
std::共享的ptr小部件;
NOTIF_句柄_NOTIF_句柄;
};
请注意,您想要添加到NotifClass
的任何功能实际上都应该进入小部件中。如果没有此类额外功能,可以跳过小部件
间接寻址,并使用弱ptr
到事件
作为上下文:
类NotifClass
{
公众:
NotifClass(const std::shared_ptr&event):
_事件(事件),,
_notif句柄(为notif(我的notif回调,(void*)新std::弱ptr(事件))注册)
//初始化一些其他东西
{
//初始化更多的东西
}
~NotifClass()
{
为\u notif(\u notif\u handle)注销\u;
}
静态void my\u notif\u回调(void*上下文)
{
自动ptr=((std::weak_ptr*)上下文)->lock();
//如果已销毁,则不要设置事件。
如果(!ptr){
返回;
}
ptr->设置_事件();
}
私人:
std::共享的ptr事件;
NOTIF_句柄_NOTIF_句柄;
};
您可以通过对静态容器的线程安全访问来实现这一点,该容器包含指向活动实例的指针。RAII类构造函数将此添加到容器中,析构函数将其删除。回调函数根据容器检查上下文,如果不存在则返回。它看起来像这样(未测试):
版主警告:为了请求我,删除这篇文章,只需编辑它
在注册回调对象之前,请确保回调对象已完全构造。也就是说,将回调对象设为单独的类,将注册/注销包装设为单独的类。
然后可以将这两个类链接到成员或基类关系中
struct A
{ CCallBackObject m_sCallback;
CRegistration m_sRegistration;
A(void)
:m_sCallback(),
m_sRegistration(&m_sCallback)
{
}
};
结构A
{CCallBackObject m_sCallback;
证券登记;证券登记;
A(无效)
:m_sCallback(),
m_S注册(&m_sCallback)
{
}
};
另外一个好处是,您可以重用注册/取消注册包装器
如果回调可能发生在另一个线程中,我会重新设计这个软件以避免这种情况。
例如,可以让主线程关闭(例如销毁此对象)等待所有工作线程关闭/完成。您对同步的担忧有点放错地方了
为了总结您的问题,您有一些可以注册回调函数的库,以及(通过void*指针或类似的)一些资源,函数通过register()
函数作用于这些资源。这个库还提供了一个unregister()
函数
在您的代码中,您既不能也不应该试图防止库在通过unregister()注销回调函数后调用回调函数的可能性
功能:库有责任确保回调在注册时或注销后不会触发。库应该担心同步
struct A
{ CCallBackObject m_sCallback;
CRegistration m_sRegistration;
A(void)
:m_sCallback(),
m_sRegistration(&m_sCallback)
{
}
};
class NotifHandle {
public:
NotifHandle(void (*callback_fn)(void *), void * context)
: _handle(register_for_notif(callback_fn, context)) {}
~NotifHandle() { unregister_for_notif(_handle); }
private:
NOTIF_HANDLE _handle;
};
class NotifClass {
public:
NotifClass(std::shared_ptr<MyEvent> event)
: _event(event),
_handle(my_notif_callback, (void*)this) {}
~NotifClass() {}
static void my_notif_callback(void* context) {
((NotifClass*)context)->_event->set_event();
}
private:
std::shared_ptr<MyEvent> _event;
NotifHandle _handle;
};
class Observer
{
public:
virtual ~Observer() {}
virtual void Callback1() = 0;
virtual void Callback2() = 0;
};
class MyEvent
{
public:
void SignalCallback1()
{
const auto lock = m_spListener.lock();
if (lock) lock->Callback1();
}
void SignalCallback2()
{
const auto lock = m_spListener.lock();
if (lock) lock->Callback2();
}
void RegisterCallbacks(std::shared_ptr<Observer> spListener)
{
m_spListener = spListener;
}
private:
std::weak_ptr<Observer> m_spListener;
};
class NotifClass : public Observer
{
public:
void Callback1() { std::cout << "NotifClass 1" << std::endl; }
void Callback2() { std::cout << "NotifClass 2" << std::endl; }
};
MyEvent source;
{
auto notif = std::make_shared<NotifClass>();
source.RegisterCallbacks(notif);
source.SignalCallback1(); // Prints NotifClass 1
}
source.SignalCallback2(); // Doesn't print NotifClass 2
class MyEvent
{
public:
void SignalCallback()
{
const auto lock = m_spListener.lock();
if (lock) (*lock)();
}
void RegisterCallback(std::shared_ptr<std::function<void(void)>> spListener)
{
m_spListener = spListener;
}
private:
std::weak_ptr<std::function<void(void)>> m_spListener;
};
class NotifClass
{
public:
void Callback() { std::cout << "NotifClass 1" << std::endl; }
};
MyEvent source;
// This doesn't need to be a smart pointer.
auto notif = std::make_shared<NotifClass>();
{
auto callback = std::make_shared<std::function<void(void)>>(
[notif]()
{
notif->Callback();
});
notif = nullptr; // note the callback already captured notif and will keep it alive
source.RegisterCallback(callback);
source.SignalCallback(); // Prints NotifClass 1
}
source.SignalCallback(); // Doesn't print NotifClass 1