From: Nick Bowler Date: Mon, 25 Feb 2019 04:56:34 +0000 (-0500) Subject: Add support for simple dynamic types. X-Git-Url: https://git.draconx.ca/gitweb/gob-dx.git/commitdiff_plain/4f931a71edce4d7456c233487703a894410fd599 Add support for simple dynamic types. Defining types in runtime-loaded modules requires a slightly different get_type implementation, and the type registration is done explicitly using a different API. It's an extra pile of GObject boilerplate which is a good fit for GOB to generate. Add a (dynamic) flag which can be put on any class definition which causes gob to spit out these alternate implementations. --- diff --git a/doc/gob2.1.in b/doc/gob2.1.in index f392384..9037aa2 100644 --- a/doc/gob2.1.in +++ b/doc/gob2.1.in @@ -238,6 +238,17 @@ For example: To make an abstract class (to pass G_TYPE_FLAG_ABSTRACT) add \'(abstract)\' before the curly braces above. This works since version 2.0.13. +.PP +To make a simple dynamic class which can be registered with a GTypeModule, +add \`(dynamic)\' to the class header. +This will cause a type registration method to be defined, which you would +normally call from the load method of a GTypeModule. +In the above example, the registration function will look like +.nf + + void gtk_new_button_register_type(GTypeModule *type_module) + +.fi .SH DATA MEMBERS .PP There are five types of data members. Three of them are normal data members, diff --git a/src/main.c b/src/main.c index 9a9a1ac..424f12c 100644 --- a/src/main.c +++ b/src/main.c @@ -1126,6 +1126,29 @@ add_interfaces (void) } } +static void +add_dynamic_interfaces(void) +{ + GList *li; + for (li = ((Class *)class)->interfaces; + li != NULL; + li = li ->next) { + char *name = replace_sep (li->data, '_'); + char *type = make_pre_macro (li->data, "TYPE"); + + out_printf(out, + "\t\tg_type_module_add_interface(\n" + "\t\t\ttype_module,\n" + "\t\t\t%s_type_id,\n" + "\t\t\t%s,\n" + "\t\t\t&%s_info);\n", + funcbase, type, name); + + g_free(type); + g_free(name); + } +} + static void add_get_type(void) { @@ -1174,6 +1197,57 @@ add_get_type(void) "}\n\n"); } +static void +add_dynamic_get_type (void) +{ + out_printf(out, + "static GType %s_type_id;\n\n" + "GType\n" + "%s_get_type (void)\n" + "{\n" + "\treturn %s_type_id;\n" + "}\n\n", + funcbase, funcbase, funcbase); + + out_printf(out, + "void\n" + "%s_register_type (GTypeModule *type_module)\n" + "{\n" + "\tstatic const GTypeInfo info = {\n" + "\t\tsizeof (%sClass),\n" + "\t\t(GBaseInitFunc) NULL,\n" + "\t\t(GBaseFinalizeFunc) NULL,\n" + "\t\t(GClassInitFunc) %s_class_init,\n" + "\t\t(GClassFinalizeFunc) NULL,\n" + "\t\tNULL /* class_data */,\n" + "\t\tsizeof (%s),\n" + "\t\t%d /* n_preallocs */,\n" + "\t\t(GInstanceInitFunc) %s_init,\n" + "\t\tNULL\n" + "\t};\n\n", + funcbase, typebase, funcbase, typebase, prealloc, funcbase); + + add_interface_infos(); + + out_printf(out, + "\t%s_type_id = g_type_module_register_type(\n" + "\t\ttype_module,\n" + "\t\t%s,\n" + "\t\t\"%s\",\n" + "\t\t&info,\n" + "\t\t(GTypeFlags)%s\n" + "\t);\n\n" + "\t{\n", + funcbase, pmacrotype, typebase, + ((Class *)class)->abstract ? "G_TYPE_FLAG_ABSTRACT" : "0"); + + add_dynamic_interfaces(); + + out_printf(out, + "\t}\n" + "}\n\n"); +} + static void add_bonobo_object_get_type (void) { @@ -3652,8 +3726,23 @@ print_class_block(Class *c) " * Public methods\n" " */\n"); - if ( ! overrode_get_type) { - out_printf (outh, "GType\t%s_get_type\t(void) G_GNUC_CONST;\n", funcbase); + if (!overrode_get_type) { + /* + * For ordinary "static" types it should be safe to mark the + * get_type implementation as const, since the get_type + * function return really is constant at the call boundary + * (even though there is an initial setup on the first call). + * But for dynamic types, since the registration is explicitly + * separated, we need to settle for "pure" as the results of + * get_type differ before and after type registration. + */ + out_printf(outh, "GType\t%s_get_type\t(void) %s;\n", funcbase, + c->dynamic ? "G_GNUC_PURE" : "G_GNUC_CONST"); + } + + if (c->dynamic) { + out_printf(outh, "void\t%s_register_type\t(GTypeModule *);\n", + funcbase); } for(li = c->nodes; li != NULL; li = li->next) { @@ -3721,11 +3810,13 @@ print_class_block(Class *c) add_interface_inits (c); - if ( ! overrode_get_type) { + if (!overrode_get_type) { if (c->bonobo_object_class != NULL) - add_bonobo_object_get_type (); + add_bonobo_object_get_type(); + else if (c->dynamic) + add_dynamic_get_type(); else - add_get_type (); + add_get_type(); } out_printf (out, "/* a macro for creating a new object of our type */\n"); diff --git a/src/parse.y b/src/parse.y index 1831ad0..662f990 100644 --- a/src/parse.y +++ b/src/parse.y @@ -45,6 +45,7 @@ static GList *flag_vals = NULL; static GList *error_vals = NULL; static gboolean abstract = FALSE; +static gboolean dynamic = FALSE; static char *chunk_size = NULL; static char *bonobo_object_class = NULL; static int glade_xml = FALSE; @@ -821,6 +822,7 @@ classdec: CLASS TYPETOKEN FROM TYPETOKEN classflags { "interfaces:steal", interfaces, "chunk_size:steal", chunk_size, "abstract", abstract, + "dynamic", dynamic, NULL); bonobo_object_class = NULL; glade_xml = FALSE; @@ -833,6 +835,8 @@ classflags: | '(' TOKEN ')' classflags { if(strcmp($2,"abstract") == 0) { abstract = TRUE; + } else if(strcmp($2,"dynamic") == 0) { + dynamic = TRUE; } else { yyerror(_("parse error")); YYERROR; diff --git a/src/treefuncs.def b/src/treefuncs.def index 83b1d62..5e5d774 100644 --- a/src/treefuncs.def +++ b/src/treefuncs.def @@ -62,6 +62,7 @@ CLASS Class STRINGLIST interfaces # GObject interfaces this class exports NODELIST nodes BOOL abstract # if G_TYPE_FLAG_ABSTRACT should be used + BOOL dynamic # if class is dynamically registered via GTypeModule ENDCLASS CLASS Type diff --git a/tests/general.at b/tests/general.at index 355f09e..9e5fbcc 100644 --- a/tests/general.at +++ b/tests/general.at @@ -66,3 +66,203 @@ AT_CHECK([awk -NF : '$1 == "main.c" && $2 == "15" { exit 42 }' stderr], [42]) AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main str.o main.o]) AT_CLEANUP + +dnl Check that dynamic types are accepted and compile OK... +AT_SETUP([dynamic types]) +AT_KEYWORDS([dynamic]) + +AT_DATA([test.gob], +[[class :Test from G:Object (dynamic) +{ + public void test(void) + { + } +} +]]) +AT_CHECK([gob2 test.gob]) + +AT_DATA([main.c], +[[#include "test.h" +int main(void) +{ + test_register_type(NULL); +} +]]) + +TEST_COMPILE_GOBJECT([test.c], [0], [], [ignore]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main test.o main.o]) + +AT_CLEANUP + +dnl Dynamic types: simple test case which checks that we can instantiate a +dnl dynamic type after registration. +AT_SETUP([dynamic type registration]) +AT_KEYWORDS([dynamic runtime]) + +AT_DATA([test.gob], +[[%{ +#include +%} +class :Test from G:Object (dynamic) +{ + public gchar *s = { g_strdup("(nil)") }; + property STRING s (link); + + public void test(self) + { + printf("%s\n", self->s); + } +} +]]) + +AT_DATA([mod.gob], +[[%{ +#include "test.h" +%} +class :Mod from G:Type:Module +{ + override (G:Type:Module) gboolean load(G:Type:Module *m) + { + test_register_type(m); + return TRUE; + } +} +]]) + +AT_DATA([main.c], +[[#include +#include "mod.h" +#include "test.h" + +void devnull(const char *a, GLogLevelFlags b, const char *c, gpointer d) { } + +int main(void) +{ + GTypeModule *m = g_object_new(mod_get_type(), NULL); + guint handler; + Test *t; + + /* should fail, suppress internal glib logging... */ + handler = g_log_set_handler("GLib-GObject", G_LOG_LEVEL_MASK, devnull, 0); + t = g_object_new(test_get_type(), NULL); + if (t != NULL) + return EXIT_FAILURE; + g_log_remove_handler("GLib-GObject", handler); + + g_type_module_use(m); + /* should work now */ + t = g_object_new(test_get_type(), "s", "Hello, World", (char *)NULL); + if (!t) + return EXIT_FAILURE; + + test_test(t); + return EXIT_SUCCESS; +} +]]) + +AT_CHECK([gob2 mod.gob]) +AT_CHECK([gob2 test.gob]) +TEST_COMPILE_GOBJECT([mod.c], [0], [], [ignore]) +TEST_COMPILE_GOBJECT([test.c], [0], [], [ignore]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main mod.o test.o main.o]) +AT_CHECK([./main], [0], [Hello, World +]) + +AT_CLEANUP + +dnl Dynamic types: check that we can call interface methods of dynamic types. +AT_SETUP([dynamic interface implementation]) +AT_KEYWORDS([dynamic runtime interface]) + +AT_DATA([iface.h], +[[#define IF_TYPE_TEST if_test_get_type() + +typedef struct IFTestIface { + GTypeInterface parent; + + void (*test)(GObject *obj); +} IFTestIface; + +GType if_test_get_type(void); +]]) + +AT_DATA([test.gob], +[[%{ +#include +#include "iface.h" +%} + +class :Test from G:Object (dynamic) + (interface IF:Test) +{ + private const char *s = { "Hello, World!" }; + interface IF:Test private void test(G:Object *o) + { + Self *self = SELF(o); + printf("%s\n", self->_priv->s); + } +} +]]) + +AT_DATA([mod.gob], +[[%{ +#include "test.h" +%} +class :Mod from G:Type:Module +{ + override (G:Type:Module) gboolean load(G:Type:Module *m) + { + test_register_type(m); + return TRUE; + } +} +]]) + +AT_DATA([main.c], +[[#include "test.h" +#include "mod.h" +#include "iface.h" + +GType if_test_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (IFTestIface), + NULL, + NULL, + }; + + type = g_type_register_static(G_TYPE_INTERFACE, "IFTest", &info, 0); + } + return type; +} + +int main(void) +{ + GTypeModule *m = g_object_new(TYPE_MOD, NULL); + GObject *t; + + g_type_module_use(m); + t = g_object_new(TYPE_TEST, NULL); + + g_return_val_if_fail(G_TYPE_CHECK_INSTANCE_TYPE(t, IF_TYPE_TEST), + EXIT_FAILURE); + G_TYPE_INSTANCE_GET_INTERFACE(t, IF_TYPE_TEST, IFTestIface)->test(t); + + return EXIT_SUCCESS; +} +]]) + +AT_CHECK([gob2 mod.gob]) +AT_CHECK([gob2 test.gob]) +TEST_COMPILE_GOBJECT([mod.c], [0], [], [ignore]) +TEST_COMPILE_GOBJECT([test.c], [0], [], [ignore]) +TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore]) +AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main mod.o test.o main.o]) +AT_CHECK([./main], [0], [Hello, World! +]) + +AT_CLEANUP