[glibmm] Gio::SocketControlMessage: Add add_deserialize_func()



commit 7fc586a6d2aa9298ecb00fd0773a7daa7f277a2d
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Mon Sep 2 13:46:29 2019 +0200

    Gio::SocketControlMessage: Add add_deserialize_func()
    
    The deserialize vfunc in GLib is a class virtual function (not associated
    with an instance). Such functions don't exist in C++. But it must be wrapped
    in one way or another. g_socket_control_message_deserialize() assumes that
    all subclasses of GSocketControlMessage override this vfunc. A user-program
    can crash, if any subclass does not. Fixes #52

 gio/src/socketcontrolmessage.ccg | 40 +++++++++++++++++++++++++
 gio/src/socketcontrolmessage.hg  | 64 +++++++++++++++++++++++++++++++++++-----
 2 files changed, 96 insertions(+), 8 deletions(-)
---
diff --git a/gio/src/socketcontrolmessage.ccg b/gio/src/socketcontrolmessage.ccg
index b921f775..ff98610c 100644
--- a/gio/src/socketcontrolmessage.ccg
+++ b/gio/src/socketcontrolmessage.ccg
@@ -19,5 +19,45 @@
 
 namespace Gio
 {
+// static
+std::set<SocketControlMessage::DeserializeFunc> SocketControlMessage::m_deserialize_funcs;
+
+GSocketControlMessage* SocketControlMessage_Class::deserialize_vfunc_callback(
+  int level, int type, gsize size, gpointer data)
+{
+  // Loop through all registered deserialize functions.
+  // Accept the returned message from the first function that has been able to
+  // deserialize the message. g_socket_control_message_deserialize() loops
+  // through all subclasses of GSocketControlMessage in this way.
+  for (auto deserialize_func : CppObjectType::m_deserialize_funcs)
+  {
+    try // Trap C++ exceptions which would normally be lost because this is a C callback.
+    {
+      // Call the function which has been registered with add_deserialize_func().
+      Glib::RefPtr<SocketControlMessage> msg = deserialize_func(level, type, size, data);
+      if (msg)
+      {
+        msg->reference(); // Give the caller a reference.
+        return msg->gobj();
+      }
+    }
+    catch (...)
+    {
+      Glib::exception_handlers_invoke();
+    }
+  }
+
+  // Don't call the original underlying C function (GSocketControlMessage.deserialize()).
+  // Let g_socket_control_message_deserialize() do that as a last resort,
+  // if it's appropriate.
+  return nullptr;
+}
+
+// static
+void SocketControlMessage::add_deserialize_func(DeserializeFunc func)
+{
+  // std::set never contains duplicates.
+  m_deserialize_funcs.insert(func);
+}
 
 } // namespace Gio
diff --git a/gio/src/socketcontrolmessage.hg b/gio/src/socketcontrolmessage.hg
index 9f9845c4..620e8d8c 100644
--- a/gio/src/socketcontrolmessage.hg
+++ b/gio/src/socketcontrolmessage.hg
@@ -15,6 +15,7 @@
  */
 
 #include <glibmm/object.h>
+#include <set>
 
 _DEFS(giomm,gio)
 _PINCLUDE(glibmm/private/object_p.h)
@@ -22,8 +23,8 @@ _PINCLUDE(glibmm/private/object_p.h)
 namespace Gio
 {
 
-/** SocketControlMessage - A Socket control message.
- * A SocketControlMessage is a special-purpose utility message that can be
+/** A Socket control message.
+ * A %SocketControlMessage is a special-purpose utility message that can be
  * sent to or received from a Socket. These types of messages are often
  * called "ancillary data".
  *
@@ -35,11 +36,13 @@ namespace Gio
  * Gio::Socket::receive().
  *
  * To extend the set of control message that can be sent, subclass this class
- * and override the get_size, get_level, get_type and serialize methods.
+ * and override the get_size_vfunc(), get_level_vfunc(), get_type_vfunc() and
+ * serialize_vfunc() methods.
  *
  * To extend the set of control messages that can be received, subclass this
- * class and implement the deserialize method. Also, make sure your class is
- * registered with the GType typesystem before calling
+ * class and implement a DeserializeFunc function. Typically it would be a
+ * static class method. Also, make sure you register the DeserializeFunc
+ * function with a call to add_deserialize_func() before calling
  * Gio::Socket::receive() to read such a message.
  *
  * @ingroup NetworkIO
@@ -60,14 +63,59 @@ public:
   _WRAP_METHOD(void serialize(gpointer data), g_socket_control_message_serialize)
 
 protected:
-  //TODO: The deserialize vfunc does not have a GSocketControlMessage for its
-  //first parameter so it is difficult to wrap.
-  //_WRAP_VFUNC(Glib::RefPtr<SocketControlMessage> deserialize(int level, int type, gsize size, gpointer 
data), "deserialize")
+  // The deserialize vfunc in GLib is a class virtual function (not associated
+  // with an instance). Such functions don't exist in C++.
+  // But it must be wrapped in one way or another. g_socket_control_message_deserialize()
+  // assumes that all subclasses of GSocketControlMessage override this vfunc.
+  // A user-program can crash, if any subclass does not.
+  // https://gitlab.gnome.org/GNOME/glibmm/issues/52
+#m4begin
+  _PUSH(SECTION_PCC_CLASS_INIT_VFUNCS)
+  klass->deserialize = &deserialize_vfunc_callback;
+  _SECTION(SECTION_PH_VFUNCS)
+  static GSocketControlMessage* deserialize_vfunc_callback(
+    int level, int type, gsize size, gpointer data);
+  _POP()
+#m4end
+
+  /** Pointer to a function that can be called from deserialize() or
+   * g_socket_control_message_deserialize().
+   *
+   * For instance,
+   * @code
+   * Glib::RefPtr<SocketControlMessage> my_deserialize_func(
+   *   int level, int type, gsize size, gpointer data);
+   * @endcode
+   *
+   * @param level A socket level.
+   * @param type A socket control message type for the given @a level.
+   * @param size The size of the data in bytes.
+   * @param data Pointer to the message data (element-type guint8).
+   * @return The deserialized message or an empty Glib::RefPtr.
+   *         The returned message can be a subclass of %SocketControlMessage.
+   */
+  using DeserializeFunc = Glib::RefPtr<SocketControlMessage> (*)
+    (int level, int type, gsize size, gpointer data);
+
+  /** Register a deserialize function.
+   *
+   * If the same function is registered multiple times, only the first
+   * registration has an effect.
+   *
+   * In GLib, %deserialize() is a class virtual function (not associated
+   * with an instance). Such functions don't exist in C++. A function registered
+   * with %add_deserialize_func() is a kind of replacement.
+   */
+  static void add_deserialize_func(DeserializeFunc func);
 
   _WRAP_VFUNC(gsize get_size() const, "get_size")
   _WRAP_VFUNC(int get_level() const, "get_level")
   _WRAP_VFUNC(int get_type() const, "get_type")
   _WRAP_VFUNC(void serialize(gpointer data), "serialize")
+
+private:
+  // Functions registered with add_deserialize_func(). 
+  static std::set<DeserializeFunc> m_deserialize_funcs;  
 };
 
 } // namespace Gio


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]