在C ++中使用RAII进行回调注册

用户972014

我正在使用一些API来获取通知。就像是:

NOTIF_HANDLE register_for_notif(CALLBACK func, void* context_for_callback);
void unregister_for_notif(NOTIF_HANDLE notif_to_delete);

我想将其包装在一些不错的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;
};

但是我担心构造/销毁期间会调用回调(也许在此特定示例中,shared_ptr可以很好地使用它,但也许与其他构造的类不一样)。

我会再说一遍-我不希望为这个非常特殊的类提供一个非常具体的解决方案,而是在传递回调时为RAII提供一个更通用的解决方案。

邓肯

您对同步的关注有点放错了地方。

总结一下您的问题,您可以使用一些库来注册一个回调函数,并(通过void *指针或类似的东西)注册一些资源,这些资源可通过一个register()函数作用于该函数。该库也提供了一个unregister()功能。

在您的代码中,您既不能也不应尝试避免库在通过unregister()函数注销之后或之后可以调用您的回调函数的可能性库的责任是确保在回调过程中不能触发该回调正在取消注册。图书馆应该担心同步化,互斥和其他小问题,而不是您。

您的代码的两个职责是:

  • 确保在注册回调之前构造用于回调的资源,并且
  • 在销毁回调所作用的资源之前,请确保注销注销。

C的构造与破坏的相反顺序正是C ++对其成员变量所做的,以及为什么编译器在以“错误”顺序初始化它们时会警告您。

就您的示例而言,您需要确保1)register_for_notif()在初始化共享指针之后被调用,并且2)unregister_for_notif()在std :: shared_ptr(或其他任何东西)被销毁之前被调用。

后者的关键是了解析构函数中破坏的顺序。作为回顾,请查看以下cppreference.com页面的“销毁序列”部分

  • 首先,执行析构函数的主体;
  • 然后,编译器将以声明的相反顺序为该类的所有非静态非变量成员调用析构函数。

因此,示例代码是“安全的”(或尽可能安全),因为unregister_for_notif()在销毁成员变量之前在析构函数主体中对其进行了调用std::shared_ptr<MyEvent> _event

进行此操作的另一种方法(从某种意义上来说更明确地遵循RAII的方式)是将通知句柄与回调函数所运行的资源分开,方法是将其拆分为自己的类。例如:

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;
};

重要的是成员变量声明顺序:NotifHandle _handle在资源之后声明std::shared_ptr<MyEvent> _event,因此可以确保在销毁资源之前取消通知的注册。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章