summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--glib/glibmm/class.cc60
-rw-r--r--glib/glibmm/class.h26
-rw-r--r--glib/glibmm/extraclassinit.cc32
-rw-r--r--glib/glibmm/extraclassinit.h96
-rw-r--r--glib/glibmm/filelist.am2
-rw-r--r--glib/glibmm/interface.cc7
-rw-r--r--glib/glibmm/object.cc34
-rw-r--r--glib/glibmm/objectbase.cc51
-rw-r--r--glib/glibmm/objectbase.h45
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,13 +86,21 @@ 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