diff options
-rw-r--r-- | glib/glibmm/class.cc | 60 | ||||
-rw-r--r-- | glib/glibmm/class.h | 26 | ||||
-rw-r--r-- | glib/glibmm/extraclassinit.cc | 32 | ||||
-rw-r--r-- | glib/glibmm/extraclassinit.h | 96 | ||||
-rw-r--r-- | glib/glibmm/filelist.am | 2 | ||||
-rw-r--r-- | glib/glibmm/interface.cc | 7 | ||||
-rw-r--r-- | glib/glibmm/object.cc | 34 | ||||
-rw-r--r-- | glib/glibmm/objectbase.cc | 51 | ||||
-rw-r--r-- | 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,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 |