Wolfgang Meyer wrote :
Dear Chris,

I think a libffi wrapper could be a very useful library for Oz, especially if it works cross-platform. As far as I know, nothing like this is available so far.

To define procedures with a variable number of arguments, you might consider to use "Compiler.evalExpression". You would basically create the source code at runtime (using string processing) and then compile it to create a procedure value. This is probably an order of magnitude slower than your existing Bind procedure. But maybe that's okay because every foreign function is only bound once.

This problem shows up every so often. We should really add something like :
{MakeProcWithArity P A ?R}
such that
{R X1 X2 X3 ... XA} is equivalent to {P [X1 X2 X3 ... XA]}
to the language.

In the case at hand,
the fast but ugly way to do it is to add Compiler to the functor's import and to define Bind like this:

fun{Bind F Name Type}
  NumArgs = {Width Type.2}
in if NumArgs == 0 then
     proc {$ R}
    Fun
     in
    {FFI.bind F Name Fun}
    {FFI.call Fun Type nil R}
     end
  elseif NumArgs == 1 then
     proc {$ A R}
    Fun
     in
    {FFI.bind F Name Fun}
    {FFI.call Fun Type [ A ] R}
     end
  else
     thread
    Res in
    {{Compiler.assemble
      {Flatten
       [
        lbl(1)
        definition(x(0)
           2
pid({VirtualString.toAtom Name} NumArgs+1 pos('' 1 0) nil NumArgs+1)
           unit
           [g(0) g(1) g(2) g(3) g(4)])
        allocateL3
        move(x(0) y(0))
        move(x(1) y(1))
        move(x(NumArgs) y(2))
        putConstant(nil x(0))
        for P in NumArgs-1..0;~1 collect:C do
       R=if P>1 then x(P) else y(P) end
        in
       {C [
           move(x(0) x(1))
           putList(x(0))
           setValue(R)
           setValue(x(1))
          ]}
        end
        move(x(0) y(0))
        move(g(2) x(0))
        move(g(3) x(1))
        createVariableMove(y(1) x(2))
        callGlobal(g(0) 6)
        move(y(1) x(0))
        move(g(4) x(1))
        move(y(0) x(2))
        move(y(2) x(3))
        deAllocateL3
        callGlobal(g(1) 9)
        endDefinition(1)
        lbl(2)
        unify(x(0) g(5))
       ]
      }
      [FFI.bind FFI.call F Name Type Res]
      default
      $
      _}}
     end
     {Wait Res}
     Res
  end
end

This is the same idea you suggested but using Oz VM assembly in list format rather than Oz code in text format.
Another possibility is to define Bind as a macro (<http://www.mozart-oz.org/documentation/macro/index.html>). But this involves quite a bit of syntax tree manipulation and requires that the arguments to Bind are known at compile time. Also, macros are an unofficial language feature.

Actually, they are a deprecated experimental feature.
Cheers,
 Wolfgang



Chris Double wrote:
I'm playing around with a wrapper for libffi so I can access functions
in shared libraries without writing wrappers in C.

That's cool!
It's very simple at the moment and has only been tested on Linux.
Before I go any further I thought I'd better check and see if there is
already anything like this out there that I should use instead or get
feedback on design, etc. I will get this working on Windows and Mac OS
X and clean it up if it proves useful.

I don't think anything like that as been done (or at least made public).
There is a 'ffitest.oz' file which shows very simple example of usage,
and can be tried line by line from the interactive environment. Here's
some simple usage:

First load the module:
  declare [FFI]={Module.link ['ffi.ozf']}

This provides a few procedures. The first, 'load' takes a path or name
for a shared library and loads it. It returns an extension object that
needs to be passed to the other calls. The name of the shared library
can be 'nil' to represent using a name from the shared libraries
loaded by the current process. This is what the following line does:

  declare Std = {FFI.load nil}

The second procedure is 'bind'. This will take the extension object
returned above, the name of a function, and a tuple describing the
functions return types and arguments. It will return a procedure, that
when called, will marshall types, call the C function, and return the
relevant value. e.g:

declare GetEnv = {FFI.bind Std "getenv" stdcall(string getenv(string))}
  {Browse {GetEnv "OZHOME}}

This will show the value of the OZHOME environment variable in the
browser. Hopefully. I haven't decided on the final syntax of the tuple
describing the arguments yet. At the moment it looks like:

  stdcall(<returntype> <ignored>(<argtype1> <argtype2> ...))

stdcall and <ignored> are ignored. Originally I was going to use them
for the calling argument convention and the function name but ended up
not doing this. The types can be any of:

  pointer - any C pointer type, from Oz it needs to be a foreignPointer
  string - a C char*, or Oz String
  uint8 - 8 bit unsigned integer from C, Number in Oz
  sint8 - 8 bit signed integer from C, Number in Oz
  uint16 - 16 bit unsigned integer from C, Number in Oz
  sint16 - 16 bit signed integer from C, Number in Oz
  uint32 - 32 bit unsigned integer from C, Number in Oz
  sint32 - 32 bit signed integer from C, Number in Oz
  uint64 - 64 bit unsigned integer from C, Number in Oz
  sint64 - 64 bit signed integer from C, Number in Oz
  float - float in C, float in Oz
  double - double in C, float in Oz

Another example:
  declare Pow = {FFI.bind Std "pow" stdcall(double pow(double double))}
  {Browse {Pow 2.0 6.0}}

This should show 64.0 in the browser.

The library has a number of limitations at the moment which I hope to
fix. This first is that there is no support for defining and using C
structures. I need to think of a good way of doing this. There are
memory leaks when calling C functions due to be not correctly cleaning
up copies of atoms, etc. There is a hard coded limit on the number of
arguments. Currently it's 3 but I can make this more. It's hard coded
due to the implementation of Bind which does this:

      NumArgs = {Width Type.2}
   in
      if NumArgs == 0 then
     proc {$ R}
        Fun
     in
        {FFI.bind F Name Fun}
        {FFI.call Fun Type nil R}
     end
      elseif NumArgs == 1 then
     proc {$ A R}
        Fun
     in
        {FFI.bind F Name Fun}
        {FFI.call Fun Type [ A ] R}
     end
      elseif NumArgs == 2 then
     proc {$ A B R}
        Fun
     in
        {FFI.bind F Name Fun}
        {FFI.call Fun Type [ A B ] R}
     end
      elseif NumArgs == 3 then

Is there A way of defining a procedure which can have a variable
number of arguments? I didn't find a way so resorted to this approach.
Any thoughts on a better way?

I'm an Oz/Mozart newbie so comments on the way I'm doing things are
appreciated. My work in progress is available from:

Well, that isn't quite the typical newbie program.
http://www.bluishcoder.co.nz/ozffi.tar.gz
In the archive is a simple build.sh shell script that will build the
native extension and the .oz wrapper. You need to have libffi
installed. In Ubuntu this is done with:

sudo apt-get install libffi4 libffi4-dev

Or you can build from source:

http://sources.redhat.com/libffi/

A git repository holding the source can be retrieved with:

git clone git://double.co.nz/git/ozffi.git

or

git clone http://double.co.nz/git/ozffi.git

A gitweb view is at:
http://www.double.co.nz/cgi-bin/gitweb.cgi?p=ozffi.git;a=summary

Chris.

Yves

_________________________________________________________________________________
mozart-users mailing list                               
[email protected]
http://www.mozart-oz.org/mailman/listinfo/mozart-users

Reply via email to