before the method definition. The method can, and probably should be,
private.
.PP
+Interface methods behave very similarly to virtual/override methods.
+As with override methods, you can use PARENT_HANDLER in the definition of an
+interface method to call the implementation of the same interface method in
+a parent class.
+.PP
+The NAME_parent_iface variable may be used to access the complete interface
+structure from a parent class, where NAME is the implemented interface (with
+colons replaced by underscores).
+This enables access to the full parent implementation of an interface.
+For example, if a class implements the Gtk:Tree:Model interface then the
+implementation of the same interface in the parent class is available via
+the Gtk_Tree_Model_parent_iface pointer.
+If an interface is not implemented in the parent class, then the corresponding
+parent_iface value will be a null pointer.
+.PP
The following example implements a new object, that implements the
Gtk:Tree:Model interface and implements the get_flags method of that
interface. Do note that except for standard (GTK+ and glib) specific
out_printf (out, "}\n\n");
}
+static char *
+interface_type(const char *if_name)
+{
+ char *rawtype = remove_sep(if_name);
+ char *end = "", *typename;
+
+ if (!gtk3_ok) {
+ /*
+ * EEEK! evil, we should have some sort of option
+ * to force this for arbitrary interfaces, since
+ * some are Class and some are Iface. Glib is shite
+ * in consistency.
+ */
+
+ if (strcmp (rawtype, "GtkEditable") == 0
+ || strcmp (rawtype, "GTypePlugin") == 0)
+ {
+ end = "Class";
+ } else {
+ /* We'll assume Iface is the standard ending */
+ end = "Iface";
+ }
+ } else {
+ /* GTK3 doesn't need Iface end */
+ end = "Interface";
+ }
+
+ typename = g_strconcat(rawtype, end, (char *)NULL);
+ g_free(rawtype);
+
+ return typename;
+}
+
+static void
+define_parent_interface_refs(Class *c)
+{
+ GList *li;
+
+ if (!c->interfaces)
+ return;
+
+ out_printf(out, "\n/* parent class interface implementations */\n");
+ for (li = c->interfaces; li != NULL; li = li->next) {
+ char *name = replace_sep(li->data, '_');
+ char *type = interface_type(li->data);
+
+ out_printf (out, "static %s *%s_parent_iface;\n", type, name);
+
+ g_free(name);
+ g_free(type);
+ }
+}
+
static void
add_enums(Class *c)
{
"static guint object_signals[LAST_SIGNAL] = {0};\n\n");
out_printf(out, "/* pointer to the class of our parent */\n");
- out_printf(out, "static %sClass *parent_class = NULL;\n\n", ptypebase);
+ out_printf(out, "static %sClass *parent_class = NULL;\n", ptypebase);
+ define_parent_interface_refs(c);
+ out_printf(out, "\n");
}
static void
}
static void
-add_interface_inits (Class *c)
+add_interface_inits(Class *c)
{
GList *li;
out_printf(out, "\n");
for (li = c->interfaces; li != NULL; li = li->next) {
- const char *interface = li->data;
- const char *end;
- char *name = replace_sep (interface, '_');
- char *type = remove_sep (interface);
+ char *name = replace_sep(li->data, '_');
+ char *type = interface_type(li->data);
- if(!gtk3_ok)
- {
- /* EEEK! evil, we should have some sort of option
- * to force this for arbitrary interfaces, since
- * some are Class and some are Iface. Glib is shite
- * in consistency. */
-
- if (strcmp (type, "GtkEditable") == 0 ||
- strcmp (type, "GTypePlugin") == 0)
- end = "Class";
- else
- // We'll assume Iface is the standard ending
- end = "Iface";
- }
- else
- {
- /*GTK3 doesn't need Iface end*/
- end="Interface";
- }
-
- out_printf (out, "\nstatic void\n"
- "___%s_init (%s%s *iface)\n"
- "{\n",
- name, type, end);
+ out_printf(out, "static void\n"
+ "___%s_init (%s *iface)\n"
+ "{\n", name, type);
- add_interface_methods (c, interface);
+ add_interface_methods(c, li->data);
- out_printf (out, "}\n\n");
+ out_printf(out, "\t%s_parent_iface\n", name);
+ out_printf(out, for_cpp ? "\t\t= (%s *)" : "\t\t= ", type);
+ out_printf(out, "g_type_interface_peek_parent(iface);\n"
+ "}\n\n");
g_free (name);
g_free (type);
return g_string_free (gs, FALSE);
}
+static gboolean method_is_void(Method *m)
+{
+ return !strcmp(m->mtype->name, "void") && !m->mtype->pointer;
+}
+
+static const char *method_err_retval(Method *m)
+{
+ if (method_is_void(m))
+ return "(void)0";
+ if (m->onerror)
+ return m->onerror;
+ return "0";
+}
+
+static void
+put_interface_parent_handler(Method *m)
+{
+ const char *errval = method_err_retval(m);
+ char *name = replace_sep(m->interface, '_');
+ char *args = get_arg_names_for_macro(m);
+
+ out_printf(out, "#define PARENT_HANDLER(%s) (%s_parent_iface \\\n"
+ "\t? %s_parent_iface->%s(%s) \\\n"
+ "\t: %s)\n", args, name, name, m->id, args, errval);
+
+ g_free(name);
+ g_free(args);
+}
+
static void
put_method(Method *m)
{
g_free(doc);
}
}
+
switch(m->method) {
case REGULAR_METHOD:
if(m->line_no > 0)
else /* PUBLIC, PROTECTED */
print_method(out, "", "\n", "", " ", "", "\n",
m, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE);
+
+ if (m->interface) {
+ out_addline_outfile(out);
+ put_interface_parent_handler(m);
+ }
+
print_method_body(m, TRUE, TRUE);
+
+ if (m->interface) {
+ out_printf(out, "#undef PARENT_HANDLER\n");
+ }
+
/* the outfile line was added above */
break;
case SIGNAL_FIRST_METHOD:
(interface Test:Fooable)
{
interface Test:Fooable private int foo(G:Object *go)
+ onerror 88
{
$5
abort();
])
AT_CLEANUP
+
+AT_SETUP([interface method chaining])
+AT_KEYWORDS([runtime])dnl
+
+TEST_FOOABLE_IFACE()
+TEST_FOOABLE_IMPL([Test:A], [G:Object],
+ [puts("Test:A foo called"); return PARENT_HANDLER(go);])
+TEST_FOOABLE_IMPL([Test:B], [Test:A],
+ [puts("Test:B foo called"); return PARENT_HANDLER(go);])
+TEST_FOOABLE_IMPL([Test:C], [Test:B],
+ [puts("Test:C foo called"); return PARENT_HANDLER(go);])
+
+AT_DATA([main.c],
+[[#include <stdio.h>
+#include "test-fooable.h"
+#include "test-a.h"
+#include "test-b.h"
+#include "test-c.h"
+
+int main(void)
+{
+ int rc;
+
+ rc = test_foo(g_object_new(TEST_TYPE_C, NULL));
+ printf("%d\n", rc);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ return 0;
+}
+]])
+
+TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore])
+
+AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main \
+ test-a.o test-b.o test-c.o test-fooable.o main.o])
+AT_CHECK([./main], [0], [Test:C foo called
+Test:B foo called
+Test:A foo called
+88
+])
+
+AT_CLEANUP
+
+AT_SETUP([interface method chaining (dynamic)])
+AT_KEYWORDS([runtime])dnl
+
+TEST_FOOABLE_IFACE()
+TEST_FOOABLE_IMPL_DYN([Test:A], [G:Object],
+ [puts("Test:A foo called"); return PARENT_HANDLER(go);])
+TEST_FOOABLE_IMPL_DYN([Test:B], [Test:A],
+ [puts("Test:B foo called"); return PARENT_HANDLER(go);])
+TEST_FOOABLE_IMPL_DYN([Test:C], [Test:B],
+ [puts("Test:C foo called"); return PARENT_HANDLER(go);])
+
+AT_DATA([main.c],
+[[#include <stdio.h>
+#include "test-fooable.h"
+#include "test-a.h"
+#include "test-a-mod.h"
+#include "test-b.h"
+#include "test-b-mod.h"
+#include "test-c.h"
+#include "test-c-mod.h"
+
+int main(void)
+{
+ int rc;
+
+ g_type_module_use(g_object_new(TEST_TYPE_A_MOD, NULL));
+ g_type_module_use(g_object_new(TEST_TYPE_B_MOD, NULL));
+ g_type_module_use(g_object_new(TEST_TYPE_C_MOD, NULL));
+
+ rc = test_foo(g_object_new(TEST_TYPE_C, NULL));
+ printf("%d\n", rc);
+ if (rc < 0)
+ return EXIT_FAILURE;
+
+ return 0;
+}
+]])
+
+TEST_COMPILE_GOBJECT([main.c], [0], [], [ignore])
+
+AT_CHECK([$CC $CFLAGS $LDFLAGS $LIBGOBJECT_LIBS -o main \
+ test-a.o test-a-mod.o test-b.o test-b-mod.o test-c.o test-c-mod.o \
+ test-fooable.o main.o])
+AT_CHECK([./main], [0], [Test:C foo called
+Test:B foo called
+Test:A foo called
+88
+])
+
+AT_CLEANUP