Re: The 'native' function (Was: First 64-bit release)

2009-07-22 Thread Tomas Hlavaty
Hi Alex,

thanks for the description of the FFI.  When I have time, I should
port my ffi.l to the 64 bit version.

Cheers,

Tomas
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


The 'native' function (Was: First 64-bit release)

2009-07-07 Thread Alexander Burger
Hi Tomas,

now the 'native' C function call is working. It is in the testing
release, and I think it looks quite nice! :-)

The syntax is

   (native lib fun ret val ..) - any

(the lib and fun arguments were swapped since my last mail).

With this function it is possible to call many C functions right out of
the box, without needing a glue function. In cases where a glue is still
needed, it should be easier to write one, as it does not need to know
about Lisp data types.


lib is the path name of the dynamically loadable library. When it is
empty ( or NIL), the main program is used.

fun must be the name of a symbol in that library.

ret specifies the return value, and an arbitrary number of vals
specify the arguments.


For val it is legal to pass:

   - a number (for a byte, int, long or pointer argument)
   - a symbol (for a string argument)
   - a list with a single number (for a dynamic buffer size)


The return value specification ret is more complicated. It uses
the following token symbols for primitive types:

  NIL   void
  T bool # NIL if zero / T if non-zero byte value
  B byte # Unsigned byte
  C char # UTF-8 character, 1-3 bytes
  I int  # Signed integer. Default if just a number is given
  N long # Unsigned long or pointer
  S String   # UTF-8. Default if just a symbol is given

That is, a function may directly return such a scalar value.

But a function may also return a pointer to an array or a structure (it
could optionally have been passed in with the single number list
argument above). In that case, the token symbols can be arbitrarily
nested, in combination with count values. Examples:

   The return value is an array of 4 longs:
   (N . 4)  -  long[4];

   The return value is a structure consisting of a char pointer, an
   integer array, and a character buffer:
   (S (I . 4) (C . 8))  -  struct {char *s; int i[4]; char nm[8];}


Let's see it in action:

   : (native NIL getenv 'S TERM)
   - xterm

So 'getenv' returns a string 'S', and accepts a string argument.


We might use 'printf':

   : (native NIL printf 'I abc%d%s^J (+ 3 4) (pack X Y Z))
   abc7XYZ
   - 8


More than the six register arguments of the x86-64 calling convention do
also work:

   : (native NIL printf 'I %d %d %d %d %d %d %d %d %d^J 1 2 3 4 5 6 7 8 9)
   1 2 3 4 5 6 7 8 9
   - 18


Then, to test structured returns, I wrote a simple C program, in a file
named dll.c:

   struct {
  char *s;
  int i[4];
  char nm[8];
   } Data;

   void *foo(int i, char *s) {
  printf(%d -- %s\n, i, s);
  Data.s = Hello;
  Data.i[0] = 1;
  Data.i[1] = 2;
  Data.i[2] = 3;
  Data.i[3] = 4;
  strcpy(Data.nm, world);
  return Data;
   }

I compiled it in the current directory with

   gcc -o dll.so \
  -fPIC -shared -export-dynamic \
  -O -falign-functions -fomit-frame-pointer \
  -W -Wimplicit -Wreturn-type -Wunused -Wformat \
  -Wuninitialized -Wstrict-prototypes \
  -pipe -D_GNU_SOURCE dll.c

Then I can called it as

   : (native ./dll.so foo '(S (I . 4) (C . 8)) 12345 a number)
   12345 -- a number
   - (Hello (1 2 3 4) (w o r l d NIL NIL NIL))

or

   : (native ./dll.so foo '((S I (I . 2) (B . 4)) (C . 8)) 12345 a number)
   12345 -- a number
   - ((Hello 1 (2 3) (4 0 0 0)) (w o r l d NIL NIL NIL))

The ret structure let's you quite nicely control the result.


Hey, Randall, isn't this quite near to what you always urged me to do
while at BMSC?

One drawback of 'native' is that it does not directly support floating
point numbers. Here, a glue function is still necessary.

Also functions that expect a structure pointer as an argument will need
a simple glue function. The elements of the structure must then be
passed as arguments, and the glue function can build the structure.

If a function expects a structure so that it can fill it with values,
and also returns that structure to the caller, it can be called directly
if the size of the structure (say 1 kB) is passed to 'native' as (1024).
'native' will then allocate the buffer, parse the returned contents, and
free the buffer memory afterwards.

If such a function does not return the structure pointer, such a dynamic
buffer is still useful when writing a glue function so that that glue
function will not have to worry about memory allocation and disposing,
as this must be handled by the caller (i.e 'native' in our case).

Hope I did not forget too much.  Any comments?

Cheers,
- Alex
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: The 'native' function (Was: First 64-bit release)

2009-07-07 Thread Randall Dow
Hi Alex,

This looks very cool!  Yes, that is what I wished for at bmsc. Thanks!

Rand

On Tue, Jul 7, 2009 at 7:48 PM, Alexander Burgera...@software-lab.de wrote:
 now the 'native' C function call is working. It is in the testing
 release, and I think it looks quite nice! :-)
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe