Generic CClosure marshaller using libffi



Hi,

Attached is a patch that adds a generic cclosure marshaller using libffi to
gobject. The reason for this is to allow signals of GObjects written in a
language binding to call signal callbacks in C.
It's not just a theoretical case, Edward Hervey recently ran into this
problem while trying to prototype a GStreamer element in Python.

It has conditional support, perhaps it would be better to required a copy of
libffi, but since there are no official releases since 1998 so you'd have to
use the CVS version.

Tom, do you have any plans to release a standalone version of libffi in the
near future?

Comments and suggestions on the patch are welcome.

--
Johan Dahlin


Mudanças de propriedades em: tests/gobject
___________________________________________________________________
Nome: svn:ignore
   - Makefile
Makefile.in
accumulator
defaultiface
gvalue-test
ifacecheck
ifaceinherit
ifaceinit
ifaceproperties
override
references
testmarshal.c
testmarshal.h
stamp-testmarshal.h
paramspec-test
.libs
.deps

   + Makefile
Makefile.in
accumulator
defaultiface
gvalue-test
ifacecheck
ifaceinherit
ifaceinit
ifaceproperties
override
references
testgenericmarshaller
testmarshal.c
testmarshal.h
stamp-testmarshal.h
paramspec-test
.libs
.deps


Index: tests/gobject/testgenericmarshaller.c
===================================================================
--- tests/gobject/testgenericmarshaller.c	(revisão 0)
+++ tests/gobject/testgenericmarshaller.c	(revisão 0)
@@ -0,0 +1,199 @@
+/* GObject - GLib Type, Object, Parameter and Signal Library
+ * testmarshallergeneric.c: Generic CClosure marshaller
+ * Copyright (C) 2007 Johan Dahlin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib-object.h>
+
+typedef struct {
+  GObject parent;
+} TestObject;
+
+typedef struct {
+  GObjectClass parent_class;
+  void (* test1)                (TestObject *object);
+  void (* test2)                (TestObject *object, char *str);
+  int  (* test3)                (TestObject *object, int i);
+  void (* test4)                (TestObject *object,
+                                 gboolean b, long l, float f, double d,
+                                 guint uint, gulong ulong);
+} TestObjectClass;
+
+#define TEST_TYPE_OBJECT            (test_object_get_type())
+#define TEST_OBJECT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_OBJECT, TestObject))
+#define TEST_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
+#define TEST_IS_OBJECT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), TEST_TYPE_OBJECT))
+#define TEST_OBJECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), TEST_TYPE_OBJECT, TestObjectClass))
+
+GType test_object_get_type (void);
+
+enum {
+  TEST1,
+  TEST2,
+  TEST3,
+  TEST4,
+  LAST_SIGNAL
+};
+
+static guint object_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
+
+static void test_object_init (TestObject *self) 
+{
+}
+
+static void test_object_class_init (TestObjectClass *klass)
+{
+  GObjectClass *gobject_class = (GObjectClass*) klass;
+
+  object_signals[TEST1] =
+    g_signal_new ("test1",
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (TestObjectClass, test1),
+                  NULL, NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE, 0);
+  object_signals[TEST2] =
+    g_signal_new ("test2",
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (TestObjectClass, test2),
+                  NULL, NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE, 1,
+                  G_TYPE_STRING);
+  object_signals[TEST3] =
+    g_signal_new ("test3",
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (TestObjectClass, test3),
+                  NULL, NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_INT, 1,
+                  G_TYPE_DOUBLE);
+  object_signals[TEST4] =
+    g_signal_new ("test4",
+                  G_TYPE_FROM_CLASS (gobject_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (TestObjectClass, test3),
+                  NULL, NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_NONE, 6,
+                  G_TYPE_BOOLEAN,
+                  G_TYPE_LONG,
+                  G_TYPE_FLOAT,
+                  G_TYPE_DOUBLE,
+                  G_TYPE_UINT,
+                  G_TYPE_ULONG);
+}
+
+static void
+test1_callback (TestObject *object, char *data)
+{
+  g_return_if_fail (TEST_IS_OBJECT (object));
+  g_return_if_fail (!strcmp (data, "user-data"));
+}
+
+static void
+test1_callback_swapped (char *data, TestObject *object)
+{
+  g_return_if_fail (TEST_IS_OBJECT (object));
+  g_return_if_fail (!strcmp (data, "user-data"));
+}
+
+static void
+test2_callback (TestObject *object, char *string)
+{
+  g_return_if_fail (TEST_IS_OBJECT (object));
+  g_return_if_fail (!strcmp (string, "string"));
+}
+
+static int
+test3_callback (TestObject *object, double f)
+{
+  g_return_val_if_fail (TEST_IS_OBJECT (object), -1);
+  g_return_val_if_fail (f == 42.0, -1);
+
+  return 20;
+}
+
+static void
+test4_callback (TestObject *object,
+                gboolean b, long l, float f, double d, guint uint, gulong ulong,
+                gpointer user_data)
+{
+  g_return_if_fail (b == TRUE);
+  g_return_if_fail (l == 10L);
+  g_return_if_fail (f <= 3.14001 && f >= 3.13999);
+  g_return_if_fail (d == 1.78);
+  g_return_if_fail (uint == 20);
+  g_return_if_fail (ulong == 30L);
+}
+
+void
+test_cclosure_marshal (void)
+{
+  TestObject *object;
+  gchar *data = "user-data";
+  int i;
+
+  g_type_init ();
+  
+  object = g_object_new (TEST_TYPE_OBJECT, NULL);
+  g_signal_connect (G_OBJECT (object),
+                    "test1",
+                    G_CALLBACK (test1_callback), 
+                    data);
+  g_signal_connect_swapped (G_OBJECT (object),
+                    "test1",
+                    G_CALLBACK (test1_callback_swapped), 
+                    data);
+  g_signal_connect (G_OBJECT (object),
+                    "test2",
+                    G_CALLBACK (test2_callback), 
+                    NULL);
+  g_signal_connect (G_OBJECT (object),
+                    "test3",
+                    G_CALLBACK (test3_callback), 
+                    NULL);
+  g_signal_connect (G_OBJECT (object),
+                    "test4",
+                    G_CALLBACK (test4_callback), 
+                    NULL);
+
+  g_signal_emit (G_OBJECT (object), object_signals[TEST1], 0);
+  g_signal_emit (G_OBJECT (object), object_signals[TEST2], 0, "string");
+  g_signal_emit (G_OBJECT (object), object_signals[TEST3], 0, 42.0, &i);
+  g_signal_emit (G_OBJECT (object), object_signals[TEST4], 0,
+                 TRUE, 10L, 3.14, 1.78, 20, 30L);
+  g_assert (i == 20);
+
+  g_object_unref (object);
+}
+  
+int main(void)
+{
+  test_cclosure_marshal ();
+
+  return 0;
+}
+
Index: tests/gobject/Makefile.am
===================================================================
--- tests/gobject/Makefile.am	(revisão 5317)
+++ tests/gobject/Makefile.am	(cópia de trabalho)
@@ -60,6 +60,10 @@
 	singleton				\
 	references
 
+if HAVE_LIBFFI
+test_programs += testgenericmarshaller
+endif
+
 check_PROGRAMS = $(test_programs)
 
 TESTS = $(test_programs)
Index: gobject/ChangeLog
===================================================================
--- gobject/ChangeLog	(revisão 5317)
+++ gobject/ChangeLog	(cópia de trabalho)
@@ -1,3 +1,11 @@
+2007-01-30  Johan Dahlin  <jdahlin async com br>
+
+	* Makefile.am:
+	* gclosure.c: (g_value_to_ffi_type), (g_cclosure_marshal_generic):
+	* gclosure.h:
+	* gobject.symbols:
+	Add a generic signal cclosure marshaller using libffi. (#408010)
+
 2007-01-02  Tor Lillqvist  <tml novell com>
 
 	* glib-genmarshal.c (main): Handle "/dev/stdin" by dup()ing fd 0
Index: gobject/gobject.symbols
===================================================================
--- gobject/gobject.symbols	(revisão 5317)
+++ gobject/gobject.symbols	(cópia de trabalho)
@@ -77,6 +77,7 @@
 g_closure_sink
 g_closure_unref
 g_signal_type_cclosure_new
+g_cclosure_marshal_generic
 #endif
 #endif
 
Index: gobject/gclosure.c
===================================================================
--- gobject/gclosure.c	(revisão 5317)
+++ gobject/gclosure.c	(cópia de trabalho)
@@ -1,6 +1,7 @@
 /* GObject - GLib Type, Object, Parameter and Signal Library
  * Copyright (C) 2000-2001 Red Hat, Inc.
  * Copyright (C) 2005 Imendio AB
+ * Copyright (C) 2007 Johan Dahlin
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,6 +18,9 @@
  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+
+#include        <config.h>
+
 #include	"gclosure.h"
 
 /*
@@ -26,7 +30,9 @@
 #include	"gvalue.h"
 #include 	"gobjectalias.h"
 #include	<string.h>
-
+#ifdef HAVE_FFI_H
+#include        <ffi.h>
+#endif
 
 #define	CLOSURE_MAX_REF_COUNT		((1 << 15) - 1)
 #define	CLOSURE_MAX_N_GUARDS		((1 << 1) - 1)
@@ -612,5 +618,139 @@ g_signal_type_cclosure_new (GType    ity
   return closure;
 }
 
+#ifdef HAVE_FFI_H
+static ffi_type *
+g_value_to_ffi_type (const GValue *gvalue, gpointer *value)
+{
+  ffi_type *rettype = NULL;
+  GType type = g_type_fundamental (G_VALUE_TYPE (gvalue));
+
+  switch (type) {
+  case G_TYPE_BOOLEAN:
+  case G_TYPE_CHAR:
+  case G_TYPE_INT:
+    rettype = &ffi_type_sint;
+    *value = (gpointer)&(gvalue->data[0].v_int);
+    break;
+  case G_TYPE_UCHAR:
+  case G_TYPE_UINT:
+    rettype = &ffi_type_uint;
+    *value = (gpointer)&(gvalue->data[0].v_uint);
+    break;
+  case G_TYPE_OBJECT:
+  case G_TYPE_STRING:
+  case G_TYPE_POINTER:
+    rettype = &ffi_type_pointer;
+    *value = (gpointer)&(gvalue->data[0].v_pointer);
+    break;
+  case G_TYPE_FLOAT:
+    rettype = &ffi_type_float;
+    *value = (gpointer)&(gvalue->data[0].v_float);
+    break;
+  case G_TYPE_DOUBLE:
+    rettype = &ffi_type_double;
+    *value = (gpointer)&(gvalue->data[0].v_double);
+    break;
+  case G_TYPE_LONG:
+    rettype = &ffi_type_slong;
+    *value = (gpointer)&(gvalue->data[0].v_long);
+    break;
+  case G_TYPE_ULONG:
+    rettype = &ffi_type_ulong;
+    *value = (gpointer)&(gvalue->data[0].v_ulong);
+    break;
+  case G_TYPE_INT64:
+    rettype = &ffi_type_sint64;
+    *value = (gpointer)&(gvalue->data[0].v_int64);
+    break;
+  case G_TYPE_UINT64:
+    rettype = &ffi_type_uint64;
+    *value = (gpointer)&(gvalue->data[0].v_uint64);
+    break;
+  default:
+    rettype = &ffi_type_pointer;
+    *value = NULL;
+    g_warning ("Unsupported fundamental type: %s", g_type_name (type));
+    break;
+  }
+  return rettype;
+}
+
+void
+g_cclosure_marshal_generic (GClosure     *closure,
+                            GValue       *return_gvalue,
+                            guint         n_param_values,
+                            const GValue *param_values,
+                            gpointer      invocation_hint,
+                            gpointer      marshal_data)
+{
+  ffi_cif cif;
+  ffi_type *rtype, **atypes;
+  guint i, n_args;
+  gpointer instance, user_data, rvalue, *args;
+  GCClosure *cc = (GCClosure*) closure;
+
+  /* If using connect_swapped, swap instance and user_data */
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      instance = &closure->data;
+      user_data = (gpointer)&((param_values + 0)->data[0].v_pointer);
+    }
+  else
+    {
+      instance = (gpointer)&((param_values + 0)->data[0].v_pointer);
+      user_data = &closure->data;
+    }
+
+  /* Add an extra argument for user_data */
+  n_args = n_param_values + 1;
+
+  atypes = g_new (ffi_type *, n_args);
+  args =  g_new (gpointer, n_args);
+  
+  /* Normal Arguments:
+   * shortcut the first and the last argument, they'll always be pointers.
+   */
+  atypes[0] = &ffi_type_pointer;
+  args[0] = instance;
+  for (i = 1; i < n_args - 1; i++)
+    atypes[i] = g_value_to_ffi_type (param_values + i, &args[i]);
+  atypes[n_args-1] = &ffi_type_pointer;
+  args[n_args-1] = user_data;
+
+  /* Return value */
+  if (return_gvalue) 
+    {
+      rtype = g_value_to_ffi_type (return_gvalue, &rvalue);
+    }
+  else 
+    {
+      rtype = &ffi_type_void;
+      rvalue = NULL;
+    }
+  
+  if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK)
+    goto out;
+
+  ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args);
+
+ out:
+  g_free (atypes);
+  g_free (args);
+}
+#else
+void
+g_cclosure_marshal_generic (GClosure *closure,
+                            GValue *return_gvalue,
+                            guint n_param_values,
+                            const GValue *param_values,
+                            gpointer invocation_hint,
+                            gpointer marshal_data)
+{
+  g_warning ("Glib was compiled without ffi support, "
+             "g_cclosure_marshal_generic is disabled.");
+}
+#endif
+
 #define __G_CLOSURE_C__
 #include "gobjectaliasdef.c"
Index: gobject/Makefile.am
===================================================================
--- gobject/Makefile.am	(revisão 5317)
+++ gobject/Makefile.am	(cópia de trabalho)
@@ -79,6 +79,10 @@
 
 libgobject_2_0_la_LIBADD = $(libglib)
 
+if HAVE_LIBFFI
+libgobject_2_0_la_LIBADD += -lffi
+endif
+
 libgobject_2_0_la_DEPENDENCIES = $(gobject_win32_res) $(GOBJECT_DEF)
 
 #
Index: gobject/gclosure.h
===================================================================
--- gobject/gclosure.h	(revisão 5317)
+++ gobject/gclosure.h	(cópia de trabalho)
@@ -150,6 +150,12 @@
 						 const GValue	*param_values,
 						 gpointer	 invocation_hint);
 
+void      g_cclosure_marshal_generic            (GClosure       *closure,
+                                                 GValue         *return_gvalue,
+                                                 guint           n_param_values,
+                                                 const GValue   *param_values,
+                                                 gpointer        invocation_hint,
+                                                 gpointer        marshal_data);
 /* FIXME:
    OK:  data_object::destroy		-> closure_invalidate();
    MIS:	closure_invalidate()		-> disconnect(closure);
Index: configure.in
===================================================================
--- configure.in	(revisão 5317)
+++ configure.in	(cópia de trabalho)
@@ -1376,6 +1376,18 @@
 AC_MSG_RESULT($GIO)
 AC_SUBST(GIO)
 
+dnl *********************
+dnl *** libffi checks ***
+dnl *********************
+
+AC_MSG_CHECKING(for ffi.h)
+AC_TRY_CPP([#include <ffi.h>], gobject_ffi_h=yes, gobject_ffi_h=no)
+if test $gobject_ffi_h = yes; then
+   AC_DEFINE(HAVE_FFI_H,1,[Have ffi.h include file])
+fi
+AC_MSG_RESULT([$gobject_ffi_h])
+AM_CONDITIONAL(HAVE_LIBFFI, test "$gobject_ffi_h" = "yes")
+
 dnl ****************************************
 dnl *** platform dependent source checks ***
 dnl ****************************************
Index: ChangeLog
===================================================================
--- ChangeLog	(revisão 5317)
+++ ChangeLog	(cópia de trabalho)
@@ -1,3 +1,10 @@
+2007-01-30  Johan Dahlin  <jdahlin async com br>
+
+	* configure.in:
+	* tests/gobject/Makefile.am:
+	* tests/gobject/testgenericmarshaller.c:
+	Add a generic CClosure marshaller using libffi, (#401080)
+
 2007-01-26  Matthias Clasen <mclasen redhat com>
 
 	* configure.in: Define G_GNUC_INTERNAL for Sun Studio


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]