update tour.texi
[bpt/guile.git] / libguile / foreign.c
index 90607e8..1e91661 100644 (file)
@@ -162,18 +162,6 @@ SCM_DEFINE (scm_pointer_address, "pointer-address", 1, 0, 0,
 }
 #undef FUNC_NAME
 
-SCM_DEFINE (scm_dereference_pointer, "dereference-pointer", 1, 0, 0,
-           (SCM pointer),
-           "Assuming @var{pointer} points to a memory region that\n"
-           "holds a pointer, return this pointer.")
-#define FUNC_NAME s_scm_dereference_pointer
-{
-  SCM_VALIDATE_POINTER (1, pointer);
-
-  return scm_from_pointer (* (void **) SCM_POINTER_VALUE (pointer), NULL);
-}
-#undef FUNC_NAME
-
 SCM_DEFINE (scm_pointer_to_bytevector, "pointer->bytevector", 2, 2, 0,
            (SCM pointer, SCM len, SCM offset, SCM uvec_type),
            "Return a bytevector aliasing the @var{len} bytes pointed\n"
@@ -299,8 +287,6 @@ SCM_DEFINE (scm_set_pointer_finalizer_x, "set-pointer-finalizer!", 2, 0, 0,
 }
 #undef FUNC_NAME
 
-\f
-
 void
 scm_i_pointer_print (SCM pointer, SCM port, scm_print_state *pstate)
 {
@@ -310,6 +296,55 @@ scm_i_pointer_print (SCM pointer, SCM port, scm_print_state *pstate)
 }
 
 \f
+/* Non-primitive helpers functions.  These procedures could be
+   implemented in terms of the primitives above but would be inefficient
+   (heap allocation overhead, Scheme/C round trips, etc.)  */
+
+SCM_DEFINE (scm_dereference_pointer, "dereference-pointer", 1, 0, 0,
+           (SCM pointer),
+           "Assuming @var{pointer} points to a memory region that\n"
+           "holds a pointer, return this pointer.")
+#define FUNC_NAME s_scm_dereference_pointer
+{
+  SCM_VALIDATE_POINTER (1, pointer);
+
+  return scm_from_pointer (* (void **) SCM_POINTER_VALUE (pointer), NULL);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_string_to_pointer, "string->pointer", 1, 0, 0,
+           (SCM string),
+           "Return a foreign pointer to a nul-terminated copy of\n"
+           "@var{string} in the current locale encoding.  The C\n"
+           "string is freed when the returned foreign pointer\n"
+           "becomes unreachable.\n\n"
+            "This is the Scheme equivalent of @code{scm_to_locale_string}.")
+#define FUNC_NAME s_scm_string_to_pointer
+{
+  SCM_VALIDATE_STRING (1, string);
+
+  /* XXX: Finalizers slow down libgc; they could be avoided if
+     `scm_to_string' & co. were able to use libgc-allocated memory.  */
+
+  return scm_from_pointer (scm_to_locale_string (string), free);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_pointer_to_string, "pointer->string", 1, 0, 0,
+           (SCM pointer),
+           "Return the string representing the C nul-terminated string\n"
+           "pointed to by @var{pointer}.  The C string is assumed to be\n"
+           "in the current locale encoding.\n\n"
+           "This is the Scheme equivalent of @code{scm_from_locale_string}.")
+#define FUNC_NAME s_scm_pointer_to_string
+{
+  SCM_VALIDATE_POINTER (1, pointer);
+
+  return scm_from_locale_string (SCM_POINTER_VALUE (pointer));
+}
+#undef FUNC_NAME
+
+\f
 
 SCM_DEFINE (scm_alignof, "alignof", 1, 0, 0, (SCM type),
             "Return the alignment of @var{type}, in bytes.\n\n"
@@ -495,7 +530,7 @@ fill_ffi_type (SCM type, ffi_type *ftype, ffi_type ***type_ptrs,
           *ftype = ffi_type_void;
           return;
         default:
-          scm_wrong_type_arg_msg ("make-foreign-function", 0, type,
+          scm_wrong_type_arg_msg ("pointer->procedure", 0, type,
                                   "foreign type");
         }
     }
@@ -528,19 +563,14 @@ fill_ffi_type (SCM type, ffi_type *ftype, ffi_type ***type_ptrs,
       ftype->elements[i] = NULL;
     }
 }
-    
-SCM_DEFINE (scm_make_foreign_function, "make-foreign-function", 3, 0, 0,
-            (SCM return_type, SCM func_ptr, SCM arg_types),
-            "Make a foreign function.\n\n"
-            "Given the foreign void pointer @var{func_ptr}, its argument and\n"
-            "return types @var{arg_types} and @var{return_type}, return a\n"
-            "procedure that will pass arguments to the foreign function\n"
-            "and return appropriate values.\n\n"
-            "@var{arg_types} should be a list of foreign types.\n"
-            "@code{return_type} should be a foreign type.")
-#define FUNC_NAME s_scm_make_foreign_function
+
+/* Return a "cif" (call interface) for the given RETURN_TYPE and
+   ARG_TYPES.  */
+static ffi_cif *
+make_cif (SCM return_type, SCM arg_types, const char *caller)
+#define FUNC_NAME caller
 {
-  SCM walk, scm_cif;
+  SCM walk;
   long i, nargs, n_structs, n_struct_elts;
   size_t cif_len;
   char *mem;
@@ -548,8 +578,6 @@ SCM_DEFINE (scm_make_foreign_function, "make-foreign-function", 3, 0, 0,
   ffi_type **type_ptrs;
   ffi_type *types;
 
-  SCM_VALIDATE_POINTER (2, func_ptr);
-
   nargs = scm_ilength (arg_types);
   SCM_ASSERT (nargs >= 0, arg_types, 3, FUNC_NAME);
   /* fixme: assert nargs < 1<<32 */
@@ -563,32 +591,31 @@ SCM_DEFINE (scm_make_foreign_function, "make-foreign-function", 3, 0, 0,
   for (walk = arg_types; scm_is_pair (walk); walk = scm_cdr (walk))
     if (!parse_ffi_type (scm_car (walk), 0, &n_structs, &n_struct_elts))
       scm_wrong_type_arg (FUNC_NAME, 3, scm_car (walk));
-  
+
   /* the memory: with space for the cif itself */
   cif_len = sizeof (ffi_cif);
 
   /* then ffi_type pointers: one for each arg, one for each struct
      element, and one for each struct (for null-termination) */
   cif_len = (ROUND_UP (cif_len, alignof(void*))
-             + (nargs + n_structs + n_struct_elts)*sizeof(void*));
-  
+            + (nargs + n_structs + n_struct_elts)*sizeof(void*));
+
   /* then the ffi_type structs themselves, one per arg and struct element, and
      one for the return val */
   cif_len = (ROUND_UP (cif_len, alignof(ffi_type))
-             + (nargs + n_struct_elts + 1)*sizeof(ffi_type));
+            + (nargs + n_struct_elts + 1)*sizeof(ffi_type));
 
   mem = scm_gc_malloc_pointerless (cif_len, "foreign");
-  scm_cif = scm_from_pointer (mem, NULL);
   cif = (ffi_cif *) mem;
 
   /* reuse cif_len to walk through the mem */
   cif_len = ROUND_UP (sizeof (ffi_cif), alignof(void*));
   type_ptrs = (ffi_type**)(mem + cif_len);
   cif_len = ROUND_UP (cif_len
-                      + (nargs + n_structs + n_struct_elts)*sizeof(void*),
-                      alignof(ffi_type));
+                     + (nargs + n_structs + n_struct_elts)*sizeof(void*),
+                     alignof(ffi_type));
   types = (ffi_type*)(mem + cif_len);
-  
+
   /* whew. now knit the pointers together. */
   cif->rtype = types++;
   fill_ffi_type (return_type, cif->rtype, &type_ptrs, &types);
@@ -605,12 +632,33 @@ SCM_DEFINE (scm_make_foreign_function, "make-foreign-function", 3, 0, 0,
   cif->nargs = nargs;
   cif->bytes = 0;
   cif->flags = 0;
-  
+
   if (FFI_OK != ffi_prep_cif (cif, FFI_DEFAULT_ABI, cif->nargs, cif->rtype,
-                              cif->arg_types))
-    scm_misc_error (FUNC_NAME, "ffi_prep_cif failed", SCM_EOL);
+                             cif->arg_types))
+    SCM_MISC_ERROR ("ffi_prep_cif failed", SCM_EOL);
 
-  return cif_to_procedure (scm_cif, func_ptr);
+  return cif;
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_pointer_to_procedure, "pointer->procedure", 3, 0, 0,
+            (SCM return_type, SCM func_ptr, SCM arg_types),
+            "Make a foreign function.\n\n"
+            "Given the foreign void pointer @var{func_ptr}, its argument and\n"
+            "return types @var{arg_types} and @var{return_type}, return a\n"
+            "procedure that will pass arguments to the foreign function\n"
+            "and return appropriate values.\n\n"
+            "@var{arg_types} should be a list of foreign types.\n"
+            "@code{return_type} should be a foreign type.")
+#define FUNC_NAME s_scm_pointer_to_procedure
+{
+  ffi_cif *cif;
+
+  SCM_VALIDATE_POINTER (2, func_ptr);
+
+  cif = make_cif (return_type, arg_types, FUNC_NAME);
+
+  return cif_to_procedure (scm_from_pointer (cif, NULL), func_ptr);
 }
 #undef FUNC_NAME
 
@@ -897,6 +945,81 @@ scm_i_foreign_call (SCM foreign, const SCM *argv)
 }
 
 \f
+/* Function pointers aka. "callbacks" or "closures".  */
+
+#ifdef FFI_CLOSURES
+
+/* Trampoline to invoke a libffi closure that wraps a Scheme
+   procedure.  */
+static void
+invoke_closure (ffi_cif *cif, void *ret, void **args, void *data)
+{
+  size_t i;
+  SCM proc, *argv, result;
+
+  proc = PTR2SCM (data);
+
+  argv = alloca (cif->nargs * sizeof (*argv));
+
+  /* Pack ARGS to SCM values, setting ARGV pointers.  */
+  for (i = 0; i < cif->nargs; i++)
+    argv[i] = pack (cif->arg_types[i], args[i]);
+
+  result = scm_call_n (proc, argv, cif->nargs);
+
+  unpack (cif->rtype, ret, result);
+}
+
+SCM_DEFINE (scm_procedure_to_pointer, "procedure->pointer", 3, 0, 0,
+           (SCM return_type, SCM proc, SCM arg_types),
+           "Return a pointer to a C function of type @var{return-type}\n"
+           "taking arguments of types @var{arg-types} (a list) and\n"
+           "behaving as a proxy to procedure @var{proc}.  Thus\n"
+           "@var{proc}'s arity, supported argument types, and return\n"
+           "type should match @var{return-type} and @var{arg-types}.\n")
+#define FUNC_NAME s_scm_procedure_to_pointer
+{
+  SCM pointer;
+  ffi_cif *cif;
+  ffi_status err;
+  void *closure, *executable;
+
+  cif = make_cif (return_type, arg_types, FUNC_NAME);
+
+  closure = ffi_closure_alloc (sizeof (ffi_closure), &executable);
+  err = ffi_prep_closure_loc ((ffi_closure *) closure, cif,
+                             invoke_closure, SCM2PTR (proc),
+                             executable);
+  if (err != FFI_OK)
+    {
+      ffi_closure_free (closure);
+      SCM_MISC_ERROR ("`ffi_prep_closure_loc' failed", SCM_EOL);
+    }
+
+  if (closure == executable)
+    pointer = scm_from_pointer (executable, ffi_closure_free);
+  else
+    {
+      /* CLOSURE needs to be freed eventually.  However, since
+        `GC_all_interior_pointers' is disabled, we can't just register
+        a finalizer for CLOSURE.  Instead, we create a pointer object
+        for CLOSURE, with a finalizer, and register it as a weak
+        reference of POINTER.  */
+      SCM friend;
+
+      pointer = scm_from_pointer (executable, NULL);
+      friend = scm_from_pointer (closure, ffi_closure_free);
+
+      register_weak_reference (pointer, friend);
+    }
+
+  return pointer;
+}
+#undef FUNC_NAME
+
+#endif /* FFI_CLOSURES */
+
+\f
 
 static void
 scm_init_foreign (void)