From 9b4bd73d42ae1f648a235a08d4dd3b2989747104 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 13 Dec 2019 16:34:55 +0000 Subject: [PATCH] Add a G_DECLARE macro for "protected" types Certain libraries want to provide types that are derivable for internal users, but final for any external consumer of the API. Other languages use the term "protected" to refer to this kind of type visibility attribute. Since we're providing macros to declare derivable and final types, we should also provide a macro for declaring protected types. The mechanism is the same as the other G_DECLARE macros; protected types: - define an opaque type for the instance, like G_DEFINE_FINAL_TYPE - define an opaque type for the class - define cast and type check functions for instance pointers, for public consumers, and cast and type check functions for class pointers, for internal consumers - omit an accessor for retrieving the class structure from the instance structure, as it would be pointless to do so --- gobject/gtype.h | 82 +++++++++++++++++++++++++++++++++++ gobject/tests/internal-type.c | 53 ++++++++++++++++++++++ gobject/tests/meson.build | 1 + 3 files changed, 136 insertions(+) create mode 100644 gobject/tests/internal-type.c diff --git a/gobject/gtype.h b/gobject/gtype.h index 02a718464..69601a9ea 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -1724,6 +1724,88 @@ guint g_type_get_type_registration_serial (void); return G_TYPE_INSTANCE_GET_CLASS (ptr, module_obj_name##_get_type (), ModuleObjName##Class); } \ G_GNUC_END_IGNORE_DEPRECATIONS +/** + * G_DECLARE_INTERNAL_TYPE: + * @ModuleObjName: the name of the new type, in camel case (like `GtkWidget`) + * @module_obj_name: the name of the new type in lowercase, with words + * separated by `_` (like `gtk_widget`) + * @MODULE: the name of the module, in all caps (like `GTK`) + * @OBJ_NAME: the bare name of the type, in all caps (like `WIDGET`) + * @ParentName: the name of the parent type, in camel case (like `GtkWidget`) + * + * A convenience macro for emitting the usual declarations in the header file for a type which is not (at the + * present time) intended to be subclassed from outside a project. + * + * You might use it in a header as follows: + * + * ```c + * #ifndef _myapp_window_h_ + * #define _myapp_window_h_ + * + * #include + * + * #define MY_APP_TYPE_WINDOW my_app_window_get_type () + * G_DECLARE_INTERNAL_TYPE (MyAppWindow, my_app_window, MY_APP, WINDOW, GtkWindow) + * + * MyAppWindow * my_app_window_new (void); + * + * … + * + * #endif + * ``` + * + * This results in the following things happening: + * + * - the usual `my_app_window_get_type()` function is declared with a return type of [alias@GObject.Type] + * + * - the `MyAppWindow` types is defined as a typedef of `struct _MyAppWindow`. The struct itself is not + * defined and should be defined from the private `.h` file included in the source file before + * `G_DEFINE_TYPE()` is used. + * + * - the `MY_APP_WINDOW()` cast is emitted as static inline function along with the `MY_APP_IS_WINDOW()` type + * checking function. + * + * - the `MY_APP_WINDOW_CLASS()` cast is emitted as static inline functions along with the + * `MY_APP_WINDOW_CLASS()` type checking function. + * + * - the `MyAppWindowClass` type is defined as a typedef of `struct _MyAppWindowClass`. The struct itself + * is not defined and should be defined from the private `.h` file included in the source file before + * `G_DEFINE_TYPE()` is used. + * + * - `g_autoptr()` support being added for your type, based on the type of your parent class. + * + * You can only use this function if your parent type also supports `g_autoptr()`. + * + * Because the type macro (`MY_APP_TYPE_WINDOW` in the above example) is not a callable, you must continue to + * manually define this as a macro for yourself. + * + * The declaration of the `_get_type()` function is the first thing emitted by the macro. This allows this macro + * to be used in the usual way with export control and API versioning macros. + * + * If you want to declare a type derivable by users of your code, use `G_DECLARE_DERIVABLE_TYPE()`. + * + * Since: 2.84 + **/ +#define G_DECLARE_INTERNAL_TYPE(ModuleObjName, module_obj_name, MODULE, OBJ_NAME, ParentName) \ + GType module_obj_name##_get_type (void); \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + typedef struct _##ModuleObjName ModuleObjName; \ + typedef struct _##ModuleObjName##Class ModuleObjName##Class; \ + \ + _GLIB_DEFINE_AUTOPTR_CHAINUP (ModuleObjName, ParentName) \ + G_DEFINE_AUTOPTR_CLEANUP_FUNC (ModuleObjName##Class, g_type_class_unref) \ + \ + G_GNUC_UNUSED static inline ModuleObjName * MODULE##_##OBJ_NAME (gpointer ptr) { \ + return G_TYPE_CHECK_INSTANCE_CAST (ptr, module_obj_name##_get_type (), ModuleObjName); } \ + G_GNUC_UNUSED static inline ModuleObjName##Class * MODULE##_##OBJ_NAME##_CLASS (gpointer ptr) { \ + return G_TYPE_CHECK_CLASS_CAST (ptr, module_obj_name##_get_type (), ModuleObjName##Class); } \ + G_GNUC_UNUSED static inline gboolean MODULE##_IS_##OBJ_NAME (gpointer ptr) { \ + return G_TYPE_CHECK_INSTANCE_TYPE (ptr, module_obj_name##_get_type ()); } \ + G_GNUC_UNUSED static inline ModuleObjName##Class * MODULE##_##OBJ_NAME##_GET_CLASS (gpointer ptr) { \ + return G_TYPE_INSTANCE_GET_CLASS (ptr, module_obj_name##_get_type (), ModuleObjName##Class); } \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + GOBJECT_AVAILABLE_MACRO_IN_2_84 + /** * G_DECLARE_INTERFACE: * @ModuleObjName: The name of the new type, in camel case (like `GtkWidget`) diff --git a/gobject/tests/internal-type.c b/gobject/tests/internal-type.c new file mode 100644 index 000000000..ab0cbc545 --- /dev/null +++ b/gobject/tests/internal-type.c @@ -0,0 +1,53 @@ +/* internal-type.c: Test internal type + * + * SPDX-FileCopyrightText: 2024 Bilal Elmoussaoui + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include + +struct _RandomClass { + GObjectClass parent_class; + gint some_value; +}; + +struct _Random { + GObject parent; +}; + +G_DECLARE_INTERNAL_TYPE (Random, random, G, RANDOM, GObject) + +G_DEFINE_FINAL_TYPE (Random, random, G_TYPE_OBJECT) + +static void +random_class_init (RandomClass *klass) +{ + klass->some_value = 3; +} + +static void +random_init (Random *self) +{ +} + +static void +test_internal_type (void) +{ + GObject *object; + RandomClass *klass; + + object = g_object_new (random_get_type (), NULL); + klass = G_RANDOM_GET_CLASS (object); + + g_assert_cmpint (klass->some_value, ==, 3); + g_assert_true (G_IS_RANDOM (object)); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/type/internal-type", test_internal_type); + return g_test_run (); +} diff --git a/gobject/tests/meson.build b/gobject/tests/meson.build index a163e5f91..bd49684d3 100644 --- a/gobject/tests/meson.build +++ b/gobject/tests/meson.build @@ -97,6 +97,7 @@ gobject_tests = { 'closure' : {}, 'closure-refcount' : { 'suite': ['slow'] }, 'object' : {}, + 'internal-type': {}, 'signal-handler' : {}, 'ifaceproperties' : {}, 'signals' : {