C++ 在c++;

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)

我正在使用一些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)
       : _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
具有as
share\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