]> git.draconx.ca Git - gob-dx.git/commitdiff
Add support for simple dynamic types.
authorNick Bowler <nbowler@draconx.ca>
Mon, 25 Feb 2019 04:56:34 +0000 (23:56 -0500)
committerNick Bowler <nbowler@draconx.ca>
Wed, 27 Feb 2019 13:48:51 +0000 (08:48 -0500)
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.

doc/gob2.1.in
src/main.c
src/parse.y
src/treefuncs.def
tests/general.at

index f39238425c2a56365db1c86cc8bf0018226cb74d..9037aa2673b985c5b81023e6474644cca7a0017c 100644 (file)
@@ -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.
 
 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,
 .SH DATA MEMBERS
 .PP
 There are five types of data members.  Three of them are normal data members,
index 9a9a1ac1c5d7957ad7403691511b0b7c49a640a8..424f12c0fcfd48584e88a4e571d1fe0fb9329ee6 100644 (file)
@@ -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)
 {
 static void
 add_get_type(void)
 {
@@ -1174,6 +1197,57 @@ add_get_type(void)
                   "}\n\n");
 }
 
                   "}\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)
 {
 static void
 add_bonobo_object_get_type (void)
 {
@@ -3652,8 +3726,23 @@ print_class_block(Class *c)
                    " * Public methods\n"
                    " */\n");
 
                    " * 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) {
        }
 
        for(li = c->nodes; li != NULL; li = li->next) {
@@ -3721,11 +3810,13 @@ print_class_block(Class *c)
 
        add_interface_inits (c);
 
 
        add_interface_inits (c);
 
-       if ( ! overrode_get_type) {
+       if (!overrode_get_type) {
                if (c->bonobo_object_class != NULL)
                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
                else
-                       add_get_type ();
+                       add_get_type();
        }
 
        out_printf (out, "/* a macro for creating a new object of our type */\n");
        }
 
        out_printf (out, "/* a macro for creating a new object of our type */\n");
index 1831ad067cd6eb8d7fae0d1b351bfd24142c93a8..662f990f2a4e6afea8f2dfd45de2ac0b944f403c 100644 (file)
@@ -45,6 +45,7 @@ static GList *flag_vals = NULL;
 static GList *error_vals = NULL;
 
 static gboolean abstract = FALSE;
 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;
 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,
                                          "interfaces:steal", interfaces,
                                          "chunk_size:steal", chunk_size,
                                          "abstract", abstract,
+                                         "dynamic", dynamic,
                                          NULL);
                        bonobo_object_class = NULL;
                        glade_xml = FALSE;
                                          NULL);
                        bonobo_object_class = NULL;
                        glade_xml = FALSE;
@@ -833,6 +835,8 @@ classflags:
        | '(' TOKEN ')' classflags {
                        if(strcmp($<id>2,"abstract") == 0) {
                                abstract = TRUE;
        | '(' TOKEN ')' classflags {
                        if(strcmp($<id>2,"abstract") == 0) {
                                abstract = TRUE;
+                       } else if(strcmp($<id>2,"dynamic") == 0) {
+                               dynamic = TRUE;
                        } else {
                                yyerror(_("parse error"));
                                YYERROR;
                        } else {
                                yyerror(_("parse error"));
                                YYERROR;
index 83b1d6296d8c40957946444046a95ded7d815886..5e5d7744d4327eb321369ee2cdd4a4c3ffdf1e4e 100644 (file)
@@ -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
   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
 ENDCLASS
 
 CLASS Type
index 355f09e1ea89abd766efce63496092ddf442f62d..9e5fbccd8b36ccb61d58db7eb1dc29d8ac328e93 100644 (file)
@@ -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
 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 <stdio.h>
+%}
+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 <stdio.h>
+#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 <stdio.h>
+#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