[glibmm/glibmm-2-60] Object construction: Add custom class init and instance init functions



commit 40c36dbd9c38d8e9f61771a12a93c96d735f8501
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Mon Mar 18 13:30:31 2019 +0100

    Object construction: Add custom class init and instance init functions
    
    Make it possible for named custom types to register additions to the
    class init function and to register an instance init function.
    Fixes #33

 glib/glibmm/class.cc          | 60 ++++++++++++++++++++++-----
 glib/glibmm/class.h           | 26 ++++++++++++
 glib/glibmm/extraclassinit.cc | 32 +++++++++++++++
 glib/glibmm/extraclassinit.h  | 96 +++++++++++++++++++++++++++++++++++++++++++
 glib/glibmm/filelist.am       |  2 +
 glib/glibmm/interface.cc      |  7 ++--
 glib/glibmm/object.cc         | 34 ++++-----------
 glib/glibmm/objectbase.cc     | 51 +++++++++++++++++++++++
 glib/glibmm/objectbase.h      | 45 +++++++++++++-------
 9 files changed, 298 insertions(+), 55 deletions(-)
---
diff --git a/glib/glibmm/class.cc b/glib/glibmm/class.cc
index 29219a67..057abeda 100644
--- a/glib/glibmm/class.cc
+++ b/glib/glibmm/class.cc
@@ -86,12 +86,20 @@ Class::register_derived_type(GType base_type, GTypeModule* module)
 GType
 Class::clone_custom_type(const char* custom_type_name) const
 {
-  return clone_custom_type(custom_type_name, interface_class_vector_type());
+  return clone_custom_type(custom_type_name, nullptr, nullptr, nullptr);
 }
 
 GType
 Class::clone_custom_type(
   const char* custom_type_name, const interface_class_vector_type& interface_classes) const
+{
+  return clone_custom_type(custom_type_name, &interface_classes, nullptr, nullptr);
+}
+
+GType
+Class::clone_custom_type(
+  const char* custom_type_name, const interface_class_vector_type* interface_classes,
+  const class_init_funcs_type* class_init_funcs, GInstanceInitFunc instance_init_func) const
 {
   std::string full_name("gtkmm__CustomObject_");
   Glib::append_canonical_typename(full_name, custom_type_name);
@@ -117,30 +125,43 @@ Class::clone_custom_type(
     // GTypeQuery::instance_size is guint but GTypeInfo::instance_size is guint16.
     const guint16 instance_size = (guint16)base_query.instance_size;
 
+    // Let the wrapper's class_init_function() be the first one to call.
+    auto all_class_init_funcs = new class_init_funcs_type(
+      1, std::tuple<GClassInitFunc, void*>(class_init_func_, nullptr));
+    if (class_init_funcs)
+      all_class_init_funcs->insert(all_class_init_funcs->end(),
+        class_init_funcs->begin(), class_init_funcs->end());
+
     const GTypeInfo derived_info = {
       class_size,
       nullptr, // base_init
       &Class::custom_class_base_finalize_function, // base_finalize
       &Class::custom_class_init_function,
       nullptr, // class_finalize
-      this, // class_data
+      all_class_init_funcs, // class_data
       instance_size,
       0, // n_preallocs
-      nullptr, // instance_init
+      instance_init_func, // instance_init
       nullptr, // value_table
     };
 
+    // custom_class_init_function() is called when the first object of the custom
+    // class is created, which is after clone_custom_type() has returned.
+    // Let custom_class_init_function() delete all_class_init_funcs.
+
     custom_type =
       g_type_register_static(base_type, full_name.c_str(), &derived_info, GTypeFlags(0));
 
     // Add derived versions of interfaces, if the C type implements any interfaces.
     // For instance, TreeModel_Class::add_interface().
-    for (interface_class_vector_type::size_type i = 0; i < interface_classes.size(); i++)
+    if (interface_classes)
     {
-      const Interface_Class* interface_class = interface_classes[i];
-      if (interface_class)
+      for (auto interface_class : *interface_classes)
       {
-        interface_class->add_interface(custom_type);
+        if (interface_class)
+        {
+          interface_class->add_interface(custom_type);
+        }
       }
     }
   }
@@ -176,19 +197,36 @@ Class::custom_class_base_finalize_function(void* g_class)
 void
 Class::custom_class_init_function(void* g_class, void* class_data)
 {
-  // The class_data pointer is set to 'this' by clone_custom_type().
-  const Class* const self = static_cast<Class*>(class_data);
+  // clone_custom_type() sets the class data pointer to a pointer to a vector
+  // of pointers to functions to be called.
+  const class_init_funcs_type& class_init_funcs =
+    *static_cast<class_init_funcs_type*>(class_data);
 
-  g_return_if_fail(self->class_init_func_ != nullptr);
+  g_return_if_fail(!class_init_funcs.empty() && std::get<0>(class_init_funcs[0]) != nullptr);
 
   // Call the wrapper's class_init_function() to redirect
   // the vfunc and default signal handler callbacks.
-  (*self->class_init_func_)(g_class, nullptr);
+  GClassInitFunc init_func = std::get<0>(class_init_funcs[0]);
+  (*init_func)(g_class, nullptr);
 
   GObjectClass* const gobject_class = static_cast<GObjectClass*>(g_class);
   gobject_class->get_property = &Glib::custom_get_property_callback;
   gobject_class->set_property = &Glib::custom_set_property_callback;
 
+  // Call extra class init functions, if any.
+  for (std::size_t i = 1; i < class_init_funcs.size(); ++i)
+  {
+    if (GClassInitFunc extra_init_func = std::get<0>(class_init_funcs[i]))
+    {
+      void* extra_class_data = std::get<1>(class_init_funcs[i]);
+      (*extra_init_func)(g_class, extra_class_data);
+    }
+  }
+
+  // Assume that this function is called exactly once for each type.
+  // Delete the class_init_funcs_type that was created in clone_custom_type().
+  delete static_cast<class_init_funcs_type*>(class_data);
+
   // Override the properties of implemented interfaces, if any.
   const GType object_type = G_TYPE_FROM_CLASS(g_class);
 
diff --git a/glib/glibmm/class.h b/glib/glibmm/class.h
index 3ffc51e8..41e19ce4 100644
--- a/glib/glibmm/class.h
+++ b/glib/glibmm/class.h
@@ -25,6 +25,7 @@
 #include <glibmmconfig.h> //Include this here so that the /private/*.h classes have access to 
GLIBMM_VFUNCS_ENABLED
 
 #include <vector> //For interface properties that custom types might override.
+#include <tuple>
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 
@@ -68,6 +69,13 @@ public:
   /// The type that holds pointers to the interfaces of custom types.
   using interface_class_vector_type = std::vector<const Interface_Class*>;
 
+  /** The type that holds pointers to extra class init functions of custom types.
+   * The std::tuple contains a function pointer and a pointer to class data.
+   * The class data pointer can be nullptr, if the function does not need it.
+   */
+  using class_init_funcs_type = std::vector<std::tuple<GClassInitFunc, void*>>;
+
+  // TODO: Remove this method at the next ABI/API break.
   /** Register a static custom GType, derived from the parent of this class's type.
    * The parent type of the registered custom type is the same C class as the parent
    * of the get_type() type. If a type with the specified name is already registered,
@@ -81,6 +89,24 @@ public:
   GType clone_custom_type(
     const char* custom_type_name, const interface_class_vector_type& interface_classes) const;
 
+  /** Register a static custom GType, derived from the parent of this class's type.
+   * The parent type of the registered custom type is the same C class as the parent
+   * of the get_type() type. If a type with the specified name is already registered,
+   * nothing is done. register_derived_type() must have been called.
+   * @param custom_type_name The name of the registered type is
+   *        "gtkmm__CustomObject_" + canonic(custom_type_name), where canonic()
+   *        replaces special characters with '+'.
+   * @param interface_classes Interfaces that the custom type implements (can be nullptr).
+   * @param class_init_funcs Extra class init functions (can be nullptr). These
+   *        functions, if any, are called after the class init function of this
+   *        class's type, e.g. Gtk::Widget.
+   * @param instance_init_func Instance init function (can be nullptr).
+   * @return The registered type.
+   */
+  GType clone_custom_type(
+    const char* custom_type_name, const interface_class_vector_type* interface_classes,
+    const class_init_funcs_type* class_init_funcs, GInstanceInitFunc instance_init_func) const;
+
 protected:
   GType gtype_;
   GClassInitFunc class_init_func_;
diff --git a/glib/glibmm/extraclassinit.cc b/glib/glibmm/extraclassinit.cc
new file mode 100644
index 00000000..343fc6f1
--- /dev/null
+++ b/glib/glibmm/extraclassinit.cc
@@ -0,0 +1,32 @@
+/* Copyright (C) 2017 The glibmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glibmm/extraclassinit.h>
+
+namespace Glib
+{
+
+ExtraClassInit::ExtraClassInit(GClassInitFunc class_init_func, void* class_data,
+  GInstanceInitFunc instance_init_func)
+{
+  if (class_init_func)
+    add_custom_class_init_function(class_init_func, class_data);
+
+  if (instance_init_func)
+    set_custom_instance_init_function(instance_init_func);
+}
+
+} // namespace Glib
diff --git a/glib/glibmm/extraclassinit.h b/glib/glibmm/extraclassinit.h
new file mode 100644
index 00000000..ec5740f5
--- /dev/null
+++ b/glib/glibmm/extraclassinit.h
@@ -0,0 +1,96 @@
+#ifndef _GLIBMM_EXTRACLASSINIT_H
+#define _GLIBMM_EXTRACLASSINIT_H
+/* Copyright (C) 2017 The glibmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glibmm/objectbase.h>
+
+namespace Glib
+{
+
+/** A convenience class for named custom types.
+ *
+ * Use it if you need to add code to GType's class init function and/or
+ * need an instance init function.
+ * Example:
+ * @code
+ * #include <glibmm/extraclassinit.h>
+ *
+ * class MyExtraInit : public Glib::ExtraClassInit
+ * {
+ * public:
+ *   MyExtraInit(const Glib::ustring& css_name)
+ *   :
+ *   Glib::ExtraClassInit(my_extra_class_init_function, &m_css_name, my_instance_init_function),
+ *   m_css_name(css_name)
+ *   { }
+ *
+ * private:
+ *   static void my_extra_class_init_function(void* g_class, void* class_data)
+ *   {
+ *     const auto klass = static_cast<GtkWidgetClass*>(g_class);
+ *     const auto css_name = static_cast<Glib::ustring*>(class_data);
+ *     gtk_widget_class_set_css_name(klass, css_name->c_str());
+ *   }
+ *   static void my_instance_init_function(GTypeInstance* instance, void* g_class)
+ *   {
+ *     gtk_widget_set_has_surface(GTK_WIDGET(instance), true);
+ *   }
+ *
+ *   Glib::ustring m_css_name;
+ * };
+ *
+ * class MyWidget : public MyExtraInit, public Gtk::Widget
+ * {
+ * public:
+ *   MyWidget()
+ *   :
+ *   // The GType name will be gtkmm__CustomObject_MyWidget
+ *   Glib::ObjectBase("MyWidget"), // Unique class name
+ *   MyExtraInit("my-widget"),
+ *   Gtk::Widget()
+ *   {
+ *     // ...
+ *   }
+ *   // ...
+ * };
+ * @endcode
+ *
+ * @note Classes derived from %ExtraClassInit (MyExtraInit in the example)
+ * must be listed before Glib::Object or a class derived from
+ * %Glib::Object (Gtk::Widget in the example) in the list of base classes.
+ *
+ * @newin{2,60}
+ */
+class ExtraClassInit : virtual public ObjectBase
+{
+protected:
+  /** Constructor.
+   *
+   * @param class_init_func Pointer to an extra class init function.
+   *        nullptr, if no extra class init function is needed.
+   * @param class_data Class data pointer, passed to the class init function.
+   *        Can be nullptr, if the class init function does not need it.
+   * @param instance_init_func Pointer to an instance init function.
+   *        nullptr, if no instance init function is needed.
+   */
+  explicit ExtraClassInit(GClassInitFunc class_init_func, void* class_data = nullptr,
+    GInstanceInitFunc instance_init_func = nullptr);
+};
+
+} // namespace Glib
+
+#endif /* _GLIBMM_EXTRACLASSINIT_H */
diff --git a/glib/glibmm/filelist.am b/glib/glibmm/filelist.am
index 32d5e84d..793abd3c 100644
--- a/glib/glibmm/filelist.am
+++ b/glib/glibmm/filelist.am
@@ -13,6 +13,7 @@ glibmm_files_extra_cc =                       \
        error.cc                        \
        exception.cc                    \
        exceptionhandler.cc             \
+       extraclassinit.cc \
        init.cc                         \
        interface.cc                    \
        main.cc                         \
@@ -51,6 +52,7 @@ glibmm_files_extra_h =                        \
        error.h                         \
        exception.h                     \
        exceptionhandler.h              \
+       extraclassinit.h \
        helperlist.h                    \
        i18n-lib.h                      \
        i18n.h                          \
diff --git a/glib/glibmm/interface.cc b/glib/glibmm/interface.cc
index d13c5d8a..793e767a 100644
--- a/glib/glibmm/interface.cc
+++ b/glib/glibmm/interface.cc
@@ -95,10 +95,9 @@ Interface::Interface(const Interface_Class& interface_class)
     }
     else // gobject_ == nullptr
     {
-      // The GObject is not instantiated yet. Add to the custom_interface_classes
-      // and add the interface in the Glib::Object constructor.
-      std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
-      extra_object_base_data[this].custom_interface_classes.emplace_back(&interface_class);
+      // The GObject is not instantiated yet. Add to the stored custom interface
+      // classes, and add the interface to the GType in the Glib::Object constructor.
+      add_custom_interface_class(&interface_class);
     }
   }
 }
diff --git a/glib/glibmm/object.cc b/glib/glibmm/object.cc
index 91698461..c8370d7d 100644
--- a/glib/glibmm/object.cc
+++ b/glib/glibmm/object.cc
@@ -193,21 +193,12 @@ Object::Object()
 
   if (custom_type_name_ && !is_anonymous_custom_())
   {
-    Class::interface_class_vector_type custom_interface_classes;
-
-    {
-      std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
-      const extra_object_base_data_type::iterator iter = extra_object_base_data.find(this);
-      if (iter != extra_object_base_data.end())
-      {
-        custom_interface_classes = iter->second.custom_interface_classes;
-        extra_object_base_data.erase(iter);
-      }
-    }
-
     object_class_.init();
     // This creates a type that is derived (indirectly) from GObject.
-    object_type = object_class_.clone_custom_type(custom_type_name_, custom_interface_classes);
+    object_type = object_class_.clone_custom_type(custom_type_name_,
+      get_custom_interface_classes(), get_custom_class_init_functions(),
+      get_custom_instance_init_function());
+    custom_class_init_finished();
   }
 
   void* const new_object = g_object_new(object_type, nullptr);
@@ -226,20 +217,11 @@ Object::Object(const Glib::ConstructParams& construct_params)
 
   if (custom_type_name_ && !is_anonymous_custom_())
   {
-    Class::interface_class_vector_type custom_interface_classes;
-
-    {
-      std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
-      const extra_object_base_data_type::iterator iter = extra_object_base_data.find(this);
-      if (iter != extra_object_base_data.end())
-      {
-        custom_interface_classes = iter->second.custom_interface_classes;
-        extra_object_base_data.erase(iter);
-      }
-    }
-
     object_type =
-      construct_params.glibmm_class.clone_custom_type(custom_type_name_, custom_interface_classes);
+      construct_params.glibmm_class.clone_custom_type(custom_type_name_,
+      get_custom_interface_classes(), get_custom_class_init_functions(),
+      get_custom_instance_init_function());
+    custom_class_init_finished();
   }
 
   // Create a new GObject with the specified array of construct properties.
diff --git a/glib/glibmm/objectbase.cc b/glib/glibmm/objectbase.cc
index 19fda475..19edd8ac 100644
--- a/glib/glibmm/objectbase.cc
+++ b/glib/glibmm/objectbase.cc
@@ -401,6 +401,57 @@ ObjectBase::thaw_notify()
   g_object_thaw_notify(gobj());
 }
 
+void ObjectBase::add_custom_interface_class(const Interface_Class* iface_class)
+{
+  // The GObject is not instantiated yet. Add to the custom_interface_classes
+  // and add the interface in the Glib::Object constructor.
+  std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
+  extra_object_base_data[this].custom_interface_classes.emplace_back(iface_class);
+}
+
+void ObjectBase::add_custom_class_init_function(GClassInitFunc class_init_func, void* class_data)
+{
+  std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
+  extra_object_base_data[this].custom_class_init_functions.emplace_back(
+    std::make_tuple(class_init_func, class_data));
+}
+
+void ObjectBase::set_custom_instance_init_function(GInstanceInitFunc instance_init_func)
+{
+  std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
+  extra_object_base_data[this].custom_instance_init_function = instance_init_func;
+}
+
+const Class::interface_class_vector_type* ObjectBase::get_custom_interface_classes() const
+{
+  std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
+  const auto iter = extra_object_base_data.find(this);
+  return (iter != extra_object_base_data.end()) ? &iter->second.custom_interface_classes : nullptr;
+}
+
+const Class::class_init_funcs_type* ObjectBase::get_custom_class_init_functions() const
+{
+  std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
+  const auto iter = extra_object_base_data.find(this);
+  return (iter != extra_object_base_data.end()) ? &iter->second.custom_class_init_functions : nullptr;
+}
+
+GInstanceInitFunc ObjectBase::get_custom_instance_init_function() const
+{
+  std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
+  const auto iter = extra_object_base_data.find(this);
+  return (iter != extra_object_base_data.end()) ? iter->second.custom_instance_init_function : nullptr;
+}
+
+void ObjectBase::custom_class_init_finished()
+{
+  std::lock_guard<std::mutex> lock(extra_object_base_data_mutex);
+  const auto iter = extra_object_base_data.find(this);
+  if (iter != extra_object_base_data.end())
+    extra_object_base_data.erase(iter);
+}
+
+/**** Global function *****************************************************/
 bool
 _gobject_cppinstance_already_deleted(GObject* gobject)
 {
diff --git a/glib/glibmm/objectbase.h b/glib/glibmm/objectbase.h
index 198f2951..bba6bdc9 100644
--- a/glib/glibmm/objectbase.h
+++ b/glib/glibmm/objectbase.h
@@ -219,20 +219,15 @@ protected:
 
   bool is_anonymous_custom_() const;
 
-  // TODO: At the next ABI break, replace extra_object_base_data by a non-static
-  // data member.
-  // This is a new data member that can't be added as instance data to
-  // ObjectBase now, because it would break ABI.
-  struct ExtraObjectBaseData
-  {
-    Class::interface_class_vector_type custom_interface_classes;
-  };
-
-  using extra_object_base_data_type = std::map<const ObjectBase*, ExtraObjectBaseData>;
-  static extra_object_base_data_type extra_object_base_data;
-  // ObjectBase instances may be used in different threads.
-  // Accesses to extra_object_base_data must be thread-safe.
-  static std::mutex extra_object_base_data_mutex;
+  // The following 7 methods are used by Glib::ExtraClassInit, Glib::Interface
+  // and Glib::Object during construction of a named custom type.
+  void add_custom_interface_class(const Interface_Class* iface_class);
+  void add_custom_class_init_function(GClassInitFunc class_init_func, void* class_data = nullptr);
+  void set_custom_instance_init_function(GInstanceInitFunc instance_init_func);
+  const Class::interface_class_vector_type* get_custom_interface_classes() const;
+  const Class::class_init_funcs_type* get_custom_class_init_functions() const;
+  GInstanceInitFunc get_custom_instance_init_function() const;
+  void custom_class_init_finished();
 
 public:
   //  is_derived_() must be public, so that overridden vfuncs and signal handlers can call it
@@ -261,6 +256,28 @@ protected:
 private:
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   virtual void set_manage(); // calls g_error()
+
+  // TODO: At the next ABI break, replace extra_object_base_data by a non-static
+  // data member.
+  // Private part of implementation.
+  // Used only during construction of named custom types.
+  // This is a new data member that can't be added as instance data to
+  // ObjectBase now, because it would break ABI.
+  struct ExtraObjectBaseData
+  {
+    // Pointers to the interfaces of custom types.
+    Class::interface_class_vector_type custom_interface_classes;
+    // Pointers to extra class init functions.
+    Class::class_init_funcs_type custom_class_init_functions;
+    // Pointer to the instance init function.
+    GInstanceInitFunc custom_instance_init_function = nullptr;
+  };
+
+  using extra_object_base_data_type = std::map<const ObjectBase*, ExtraObjectBaseData>;
+  static extra_object_base_data_type extra_object_base_data;
+  // ObjectBase instances may be used in different threads.
+  // Accesses to extra_object_base_data must be thread-safe.
+  static std::mutex extra_object_base_data_mutex;
 #endif // DOXYGEN_SHOULD_SKIP_THIS
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS


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