diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index 685529708..25ecd3aec 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -17,6 +17,7 @@ G_TYPE_IS_INSTANTIATABLE G_TYPE_IS_DERIVABLE G_TYPE_IS_DEEP_DERIVABLE G_TYPE_IS_INTERFACE +G_TYPE_IS_FINAL GTypeInterface GTypeInstance GTypeClass @@ -113,6 +114,9 @@ G_DEFINE_TYPE_WITH_CODE G_DEFINE_ABSTRACT_TYPE G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE G_DEFINE_ABSTRACT_TYPE_WITH_CODE +G_DEFINE_FINAL_TYPE +G_DEFINE_FINAL_TYPE_WITH_PRIVATE +G_DEFINE_FINAL_TYPE_WITH_CODE G_ADD_PRIVATE G_PRIVATE_OFFSET G_PRIVATE_FIELD diff --git a/gobject/gtype.c b/gobject/gtype.c index 2cdbe7ce4..422e1cae4 100644 --- a/gobject/gtype.c +++ b/gobject/gtype.c @@ -143,7 +143,7 @@ G_TYPE_FLAG_INSTANTIATABLE | \ G_TYPE_FLAG_DERIVABLE | \ G_TYPE_FLAG_DEEP_DERIVABLE) -#define TYPE_FLAG_MASK (G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT) +#define TYPE_FLAG_MASK (G_TYPE_FLAG_ABSTRACT | G_TYPE_FLAG_VALUE_ABSTRACT | G_TYPE_FLAG_FINAL) #define SIZEOF_FUNDAMENTAL_INFO ((gssize) MAX (MAX (sizeof (GTypeFundamentalInfo), \ sizeof (gpointer)), \ sizeof (glong))) @@ -804,6 +804,13 @@ check_derivation_I (GType parent_type, NODE_NAME (pnode)); return FALSE; } + if ((G_TYPE_FLAG_FINAL & GPOINTER_TO_UINT (type_get_qdata_L (pnode, static_quark_type_flags))) == G_TYPE_FLAG_FINAL) + { + g_warning ("cannot derive '%s' from final parent type '%s'", + type_name, + NODE_NAME (pnode)); + return FALSE; + } return TRUE; } diff --git a/gobject/gtype.h b/gobject/gtype.h index f38b4cf4c..f80c4a580 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -370,6 +370,18 @@ G_BEGIN_DECLS * Returns: %TRUE on success */ #define G_TYPE_HAS_VALUE_TABLE(type) (g_type_value_table_peek (type) != NULL) +/** + * G_TYPE_IS_FINAL: + * @type: a #GType value + * + * Checks if @type is a final type. A final type cannot be derived any + * further. + * + * Returns: %TRUE on success + * + * Since: 2.70 + */ +#define G_TYPE_IS_FINAL(type) (g_type_test_flags ((type), G_TYPE_FLAG_FINAL)) GLIB_AVAILABLE_MACRO_IN_2_70 /* Typedefs @@ -1002,13 +1014,16 @@ typedef enum /*< skip >*/ * @G_TYPE_FLAG_VALUE_ABSTRACT: Indicates an abstract value type, i.e. a type * that introduces a value table, but can't be used for * g_value_init() + * @G_TYPE_FLAG_FINAL: Indicates a final type. A final type is a non-derivable + * leaf node in a deep derivable type hierarchy tree. Since: 2.70 * * Bit masks used to check or determine characteristics of a type. */ typedef enum /*< skip >*/ { - G_TYPE_FLAG_ABSTRACT = (1 << 4), - G_TYPE_FLAG_VALUE_ABSTRACT = (1 << 5) + G_TYPE_FLAG_ABSTRACT = (1 << 4), + G_TYPE_FLAG_VALUE_ABSTRACT = (1 << 5), + G_TYPE_FLAG_FINAL GLIB_AVAILABLE_ENUMERATOR_IN_2_70 = (1 << 6) } GTypeFlags; /** * GTypeInfo: @@ -1685,6 +1700,57 @@ guint g_type_get_type_registration_serial (void); * Since: 2.38 */ #define G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(TN, t_n, T_P) G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, G_TYPE_FLAG_ABSTRACT, G_ADD_PRIVATE (TN)) +/** + * G_DEFINE_FINAL_TYPE: + * @TN: the name of the new type, in Camel case + * @t_n: the name of the new type, in lower case, with words + * separated by `_` (snake case) + * @T_P: the #GType of the parent type + * + * A convenience macro for type implementations. + * + * Similar to G_DEFINE_TYPE(), but defines a final type. + * + * See G_DEFINE_TYPE_EXTENDED() for an example. + * + * Since: 2.70 + */ +#define G_DEFINE_FINAL_TYPE(TN, t_n, T_P) G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, G_TYPE_FLAG_FINAL, {}) GLIB_AVAILABLE_MACRO_IN_2_70 +/** + * G_DEFINE_FINAL_TYPE_WITH_CODE: + * @TN: the name of the new type, in Camel case + * @t_n: the name of the new type, in lower case, with words + * separated by `_` (snake case) + * @T_P: the #GType of the parent type + * @_C_: Custom code that gets inserted in the `type_name_get_type()` function. + * + * A convenience macro for type implementations. + * + * Similar to G_DEFINE_TYPE_WITH_CODE(), but defines a final type and + * allows you to insert custom code into the `*_get_type()` function, e.g. + * interface implementations via G_IMPLEMENT_INTERFACE(). + * + * See G_DEFINE_TYPE_EXTENDED() for an example. + * + * Since: 2.70 + */ +#define G_DEFINE_FINAL_TYPE_WITH_CODE(TN, t_n, T_P, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, G_TYPE_FLAG_FINAL) {_C_;} _G_DEFINE_TYPE_EXTENDED_END() GLIB_AVAILABLE_MACRO_IN_2_70 +/** + * G_DEFINE_FINAL_TYPE_WITH_PRIVATE: + * @TN: the name of the new type, in Camel case + * @t_n: the name of the new type, in lower case, with words + * separated by `_` (snake case) + * @T_P: the #GType of the parent type + * + * A convenience macro for type implementations. + * + * Similar to G_DEFINE_TYPE_WITH_PRIVATE(), but defines a final type. + * + * See G_DEFINE_TYPE_EXTENDED() for an example. + * + * Since: 2.70 + */ +#define G_DEFINE_FINAL_TYPE_WITH_PRIVATE(TN, t_n, T_P) G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, G_TYPE_FLAG_FINAL, G_ADD_PRIVATE (TN)) GLIB_AVAILABLE_MACRO_IN_2_70 /** * G_DEFINE_TYPE_EXTENDED: * @TN: The name of the new type, in Camel case. diff --git a/gobject/tests/meson.build b/gobject/tests/meson.build index 8837f1a65..e97e1bc7e 100644 --- a/gobject/tests/meson.build +++ b/gobject/tests/meson.build @@ -51,6 +51,7 @@ gobject_tests = { 'source' : ['signals.c', marshalers_h, marshalers_c], }, 'testing' : {}, + 'type-flags' : {}, } if cc.get_id() != 'msvc' diff --git a/gobject/tests/type-flags.c b/gobject/tests/type-flags.c new file mode 100644 index 000000000..249153b55 --- /dev/null +++ b/gobject/tests/type-flags.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2021 Emmanuele Bassi + +#include + +#define TEST_TYPE_FINAL (test_final_get_type()) +G_DECLARE_FINAL_TYPE (TestFinal, test_final, TEST, FINAL, GObject) + +struct _TestFinal +{ + GObject parent_instance; +}; + +struct _TestFinalClass +{ + GObjectClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (TestFinal, test_final, G_TYPE_OBJECT) + +static void +test_final_class_init (TestFinalClass *klass) +{ +} + +static void +test_final_init (TestFinal *self) +{ +} + +#define TEST_TYPE_FINAL2 (test_final2_get_type()) +G_DECLARE_FINAL_TYPE (TestFinal2, test_final2, TEST, FINAL2, TestFinal) + +struct _TestFinal2 +{ + TestFinal parent_instance; +}; + +struct _TestFinal2Class +{ + TestFinalClass parent_class; +}; + +G_DEFINE_TYPE (TestFinal2, test_final2, TEST_TYPE_FINAL) + +static void +test_final2_class_init (TestFinal2Class *klass) +{ +} + +static void +test_final2_init (TestFinal2 *self) +{ +} + +/* test_type_flags_final: Check that trying to derive from a final class + * will result in a warning from the type system + */ +static void +test_type_flags_final (void) +{ + GType final2_type; + + /* This is the message we print out when registering the type */ + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, + "*cannot derive*"); + + /* This is the message when we fail to return from the GOnce init + * block within the test_final2_get_type() function + */ + g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, + "*g_once_init_leave: assertion*"); + + final2_type = TEST_TYPE_FINAL2; + g_assert_true (final2_type == G_TYPE_INVALID); + + g_test_assert_expected_messages (); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/type/flags/final", test_type_flags_final); + + return g_test_run (); +}