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' : {