[glibmm] Object construction: Add custom class init and instance init functions



commit 7e7b6b2a32559df6e08d351a6370a29200baaced
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Wed Feb 15 10:51:46 2017 +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.
    An extra class init function is useful in Gtk::WidgetCustomDraw and
    Gtk::WidgetCustomSnapshot. Bug 775348

 glib/glibmm/class.cc          |   52 ++++++++++++++++++----
 glib/glibmm/class.h           |   21 ++++++---
 glib/glibmm/extraclassinit.cc |   32 +++++++++++++
 glib/glibmm/extraclassinit.h  |   97 +++++++++++++++++++++++++++++++++++++++++
 glib/glibmm/filelist.am       |    2 +
 glib/glibmm/interface.cc      |    7 +--
 glib/glibmm/object.cc         |   18 ++++----
 glib/glibmm/objectbase.cc     |   54 +++++++++++++++++++++++
 glib/glibmm/objectbase.h      |   22 +++++++---
 9 files changed, 269 insertions(+), 36 deletions(-)
---
diff --git a/glib/glibmm/class.cc b/glib/glibmm/class.cc
index 696bf79..29ab8cf 100644
--- a/glib/glibmm/class.cc
+++ b/glib/glibmm/class.cc
@@ -86,7 +86,8 @@ Class::register_derived_type(GType base_type, GTypeModule* module)
 
 GType
 Class::clone_custom_type(
-  const char* custom_type_name, const interface_class_list_type& interface_classes) const
+  const char* custom_type_name, const interface_classes_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);
@@ -112,29 +113,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 (auto interface_class : interface_classes)
+    if (interface_classes)
     {
-      if (interface_class)
+      for (auto interface_class : *interface_classes)
       {
-        interface_class->add_interface(custom_type);
+        if (interface_class)
+        {
+          interface_class->add_interface(custom_type);
+        }
       }
     }
   }
@@ -170,19 +185,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<GClassInitFunc>(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);
+  auto init_func = std::get<GClassInitFunc>(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 (auto extra_init_func = std::get<GClassInitFunc>(class_init_funcs[i]))
+    {
+      auto extra_class_data = std::get<void*>(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 849065b..e790715 100644
--- a/glib/glibmm/class.h
+++ b/glib/glibmm/class.h
@@ -23,7 +23,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 <forward_list> //For interface classes that custom types might implement.
+#include <tuple>
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 
@@ -52,11 +52,13 @@ public:
 
   inline GType get_type() const;
 
-  /** The type that holds pointers to the interfaces of custom types.
-   * It's usually empty and never long. It's a std::forward_list to minimize
-   * storage requirement.
+  /// The type that holds pointers to the interfaces of custom types.
+  using interface_classes_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 interface_class_list_type = std::forward_list<const Interface_Class*>;
+  using class_init_funcs_type = std::vector<std::tuple<GClassInitFunc, void*>>;
 
   /** 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
@@ -65,11 +67,16 @@ public:
    * @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.
+   * @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_list_type& interface_classes) const;
+    const char* custom_type_name, const interface_classes_type* interface_classes,
+    const class_init_funcs_type* class_init_funcs, GInstanceInitFunc instance_init_func) const;
 
 protected:
   GType gtype_;
diff --git a/glib/glibmm/extraclassinit.cc b/glib/glibmm/extraclassinit.cc
new file mode 100644
index 0000000..343fc6f
--- /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 0000000..9d2fc60
--- /dev/null
+++ b/glib/glibmm/extraclassinit.h
@@ -0,0 +1,97 @@
+#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_window(GTK_WIDGET(instance), true);
+ *   }
+ *
+ *   Glib::ustring m_css_name;
+ * };
+ *
+ * class MyWidget : public Gtk::WidgetCustomDraw, public MyExtraInit, public Gtk::Widget
+ * {
+ * public:
+ *   MyWidget()
+ *   :
+ *   // The GType name will be gtkmm__CustomObject_MyMidget
+ *   Glib::ObjectBase("MyWidget"), // Unique class name
+ *   Gtk::WidgetCustomDraw(),
+ *   MyExtraInit("my-widget"),
+ *   Gtk::Widget()
+ *   {
+ *     // ...
+ *   }
+ *   // ...
+ * };
+ * @endcode
+ *
+ * @note Classes derived from %ExtraClassInit (Gtk::WidgetCustomDraw and 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,52}
+ */
+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 afeb2b4..e04a0b7 100644
--- a/glib/glibmm/filelist.am
+++ b/glib/glibmm/filelist.am
@@ -14,6 +14,7 @@ glibmm_files_extra_cc =                       \
        error.cc                        \
        exception.cc                    \
        exceptionhandler.cc             \
+       extraclassinit.cc               \
        init.cc                         \
        interface.cc                    \
        main.cc                         \
@@ -50,6 +51,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 0429840..31a5278 100644
--- a/glib/glibmm/interface.cc
+++ b/glib/glibmm/interface.cc
@@ -96,10 +96,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.
-      // custom_interface_classes_ is a std::forward_list. There is no emplace_back().
-      custom_interface_classes_.emplace_front(&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 19f30d5..4dd8d4e 100644
--- a/glib/glibmm/object.cc
+++ b/glib/glibmm/object.cc
@@ -195,13 +195,12 @@ Object::Object()
   if (custom_type_name_ && !is_anonymous_custom_())
   {
     object_class_.init();
-    // Reverse the interface class list in order to have the interfaces added
-    // in the same order as they are declared. (Don't know if it makes any difference,
-    // but it's an inexpensive operation. The list is often empty, never long.)
-    custom_interface_classes_.reverse();
+
     // This creates a type that is derived (indirectly) from GObject.
-    object_type = object_class_.clone_custom_type(custom_type_name_, custom_interface_classes_);
-    custom_interface_classes_.clear();
+    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_newv(object_type, 0, nullptr);
@@ -220,10 +219,11 @@ Object::Object(const Glib::ConstructParams& construct_params)
 
   if (custom_type_name_ && !is_anonymous_custom_())
   {
-    custom_interface_classes_.reverse();
     object_type =
-      construct_params.glibmm_class.clone_custom_type(custom_type_name_, custom_interface_classes_);
-    custom_interface_classes_.clear();
+      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 429d11d..7e36414 100644
--- a/glib/glibmm/objectbase.cc
+++ b/glib/glibmm/objectbase.cc
@@ -40,6 +40,17 @@ namespace Glib
 
 /**** Glib::ObjectBase *****************************************************/
 
+// Used only during the construction of named custom types.
+struct ObjectBase::PrivImpl
+{
+  // Pointers to the interfaces of custom types.
+  Class::interface_classes_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;
+};
+
 ObjectBase::ObjectBase()
 : gobject_(nullptr),
   custom_type_name_(anonymous_custom_type_name),
@@ -379,6 +390,49 @@ ObjectBase::thaw_notify()
   g_object_thaw_notify(gobj());
 }
 
+void ObjectBase::add_custom_interface_class(const Interface_Class* iface_class)
+{
+  if (!priv_pimpl_)
+    priv_pimpl_ = std::make_unique<PrivImpl>();
+  priv_pimpl_->custom_interface_classes.emplace_back(iface_class);
+}
+
+void ObjectBase::add_custom_class_init_function(GClassInitFunc class_init_func, void* class_data)
+{
+  if (!priv_pimpl_)
+    priv_pimpl_ = std::make_unique<PrivImpl>();
+  priv_pimpl_->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)
+{
+  if (!priv_pimpl_)
+    priv_pimpl_ = std::make_unique<PrivImpl>();
+  priv_pimpl_->custom_instance_init_function = instance_init_func;
+}
+
+const Class::interface_classes_type* ObjectBase::get_custom_interface_classes() const
+{
+  return priv_pimpl_ ? &priv_pimpl_->custom_interface_classes : nullptr;
+}
+
+const Class::class_init_funcs_type* ObjectBase::get_custom_class_init_functions() const
+{
+  return priv_pimpl_ ? &priv_pimpl_->custom_class_init_functions : nullptr;
+}
+
+GInstanceInitFunc ObjectBase::get_custom_instance_init_function() const
+{
+  return priv_pimpl_ ? priv_pimpl_->custom_instance_init_function : nullptr;
+}
+
+void ObjectBase::custom_class_init_finished()
+{
+  priv_pimpl_.reset();
+}
+
+/**** Global function *****************************************************/
 bool
 _gobject_cppinstance_already_deleted(GObject* gobject)
 {
diff --git a/glib/glibmm/objectbase.h b/glib/glibmm/objectbase.h
index 4dce060..3faed9d 100644
--- a/glib/glibmm/objectbase.h
+++ b/glib/glibmm/objectbase.h
@@ -28,6 +28,7 @@
 #include <glibmm/debug.h>
 #include <sigc++/trackable.h>
 #include <typeinfo>
+#include <memory>
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
 extern "C" {
@@ -202,9 +203,15 @@ protected:
 
   bool is_anonymous_custom_() const;
 
-  // List of pointers to the interfaces of custom types.
-  // Used only during the construction of named custom types.
-  Class::interface_class_list_type custom_interface_classes_;
+  // 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_classes_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
@@ -232,12 +239,15 @@ protected:
 
 private:
 #ifndef DOXYGEN_SHOULD_SKIP_THIS
+  // Private part of implementation.
+  // Used only during construction of named custom types.
+  struct PrivImpl;
+  std::unique_ptr<PrivImpl> priv_pimpl_;
+
   virtual void set_manage(); // calls g_error()
-#endif // DOXYGEN_SHOULD_SKIP_THIS
 
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
   friend class Glib::GSigConnectionNode; // for GSigConnectionNode::notify()
-#endif
+#endif // DOXYGEN_SHOULD_SKIP_THIS
 };
 
 #ifndef DOXYGEN_SHOULD_SKIP_THIS


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