Subject: [clisp-list] Re: clisp ffi

From: Sam Steingold <[email-masked]>
Date: 06 Jun 2001 11:38:19 -0400

> * In message <00e701c0ee78$091f8240$4381fb18@CX417245D>
> * On the subject of "Re: clisp ffi"
> * Sent on Wed, 6 Jun 2001 04:01:27 -0700
> * Honorable "Eric de Groot" <[email-masked]> writes:
>
>     Ok, cool, the only problem I have at the moment is the function pointer.
> For the lisp I have:
> 
> (error_handler (c-ptr (c-function)))
> 
> which gives me:
> 
> void * ERROR_HANDLER ();
> 
> but I need:
> 
> void (*ERROR_HANDLER)();

I thought that in ANSI C
        void (*f) ();
and
        void f ();
were interchangeable.

please try this patch and see if it helps _and_ does not break any other
code. 

Index: foreign1.lisp
===================================================================
RCS file: /cvsroot/clisp/clisp/src/foreign1.lisp,v
retrieving revision 1.7
diff -u -w -b -u -b -w -i -B -r1.7 foreign1.lisp
--- foreign1.lisp	2001/06/03 04:04:35	1.7
+++ foreign1.lisp	2001/06/06 15:31:03
@@ -599,7 +599,7 @@
               (to-c-typedecl (svref c-type 1)
                              (format nil "(~A)[~D]" name (svref c-type 2))))
              ((c-ptr c-ptr-null c-array-ptr)
-              (to-c-typedecl (svref c-type 1) (format nil "* ~A" name)))
+              (to-c-typedecl (svref c-type 1) (format nil "(* ~A)" name)))
              (c-function
               (to-c-typedecl (svref c-type 1) (format nil "~A ()" name)))
              (t (error (ENGLISH "illegal foreign data type ~S")

[PS. no clisp support via private e-mail - please use the lists!]

-- 
Sam Steingold (http://www.podval.org/~sds)
Support Israel's right to defend herself! <http://www.i-charity.com/go/israel>
Read what the Arab leaders say to their people on <http://www.memri.org/>
A poet who reads his verse in public may have other nasty habits.

---

From: "Eric de Groot" <[email-masked]>
Date: Wed, 6 Jun 2001 13:48:45 -0700

Sam-

    Ok, I tried the patch you sent me, but now I get parens around
everything I use c-ptr with.  Only function pointers need the parens.
Maybee this will require a new case, like C-FUNCTIONPTR or something?  I
also get parens around the def of mysql_init now like:

extern struct t2248 { /* ....... */ } (* (mysql_init)());

from:

(def-c-call-out mysql_init (:arguments (mysql (c-ptr st_mysql)))
                           (:return-type (c-ptr st_mysql)))

Also, you can see above, it's still not doing the arguments, I just get
mysql_init().

Thanks.

-Eric de Groot
mailto:[email-masked]

---

From: Sam Steingold <[email-masked]>
Date: 06 Jun 2001 18:13:54 -0400

> * In message <005c01c0eeca$1eae2e70$4381fb18@CX417245D>
> * On the subject of "Re: [clisp-list] Re: clisp ffi"
> * Sent on Wed, 6 Jun 2001 13:48:45 -0700

please send an example and then remove the patch. ;-)

also, since in C
        int (*f) ();
and
        int f()
are equivalent, you can just write
        (error_handler (c-function))

> Also, you can see above, it's still not doing the arguments, I just
> get mysql_init().

this should not be a problem as C does not require full prototypes.

-- 
Sam Steingold (http://www.podval.org/~sds)
Support Israel's right to defend herself! <http://www.i-charity.com/go/israel>
Read what the Arab leaders say to their people on <http://www.memri.org/>
Are you smart enough to use Lisp?

---

From: "Eric de Groot" <[email-masked]>
Date: Wed, 6 Jun 2001 17:27:02 -0700

Sam-

    in C, int (*f)(); and int f(); are not equivalent.  I'm trying to
declare a function pointer as a member of a structure, like:

struct mystruct {
    int (*f)();
};

you cannot write:

struct mystruct {
    int f();
}

this would work in c++ if mystruct::f() {} was defined elsewhere, but even
then it's still not a function pointer.  Excuse me if you already know this,
but just to make sure we are both clear on function pointers..

with function pointers you can do this:

struct mystruct {
    int (*f)();
};

int a() {
    printf("aaaa");
}

int b() {
    printf("bbbb");
}

struct mystruct mystructinst;

mystructinst.f = &a;
(*mystructinst.f)();

mystructinst.f = &b;
(*mystructinst.f)();

which will result in a call to a(), then b() and print out: aaaabbbb.

the declation of the function pointer itself has to look like <returntype>
(*<pointername>)(<args); or int (*f)(); in the above case.

-Eric de Groot

---

From: "Eric de Groot" <[email-masked]>
Date: Thu, 7 Jun 2001 04:13:05 -0700

Sam-

    Ok, I was having more problems with the c code in callmysql.c generated
by:

./clisp-link add-module-set mysql base base+mysql

It kept telling me the structs generated were incompatible with the structs
in mysql.h.  I checked the sizeof() of everything, and everything matched up
perfectly between the structs in mysql.h and the structs clisp generated..
So i finally just deleted all the prototypes and structures clisp generates
and let it use the prototypes and structs from mysql.h, works great.  Why
couldn't you just do this in the first place instead of having it generate
duplicate prototypes and structures?  Anyways, with that I was able to
finally talk to mysql!!  very exciting after all this;)  I'm having another
problem now with the following though:

(let ((conn (MYSQL::mysql_init nil)))
  (MYSQL::mysql_connect conn "localhost" "root" "")
  (MYSQL::mysql_get_server_info conn))

gives me: NIL

(let ((conn (MYSQL::mysql_init nil)))
  (let ((conn (MYSQL::mysql_connect conn "localhost" "root" "")))
    (MYSQL::mysql_get_server_info conn)))

gives me exactly what i want: "3.22.32-log"

in the first example, why is conn not being modified by mysql_connect?  The
second example works because mysql_connect both modifies the st_mysql
structure you pass it, and on a successful connection the the database
server, also returns it, otherwise returns null on failure.  In C, just
doing:

mysql_connect( conn, "localhost", "root", NULL );

would be fine.  The only thing I would use the returned struct for was error
checking:

if ( mysql_connect( conn, "localhost", "root", NULL ) == NULL ) {
  /* error handling.. */
}

With CLISP, when you pass a var to a c-ptr argument, do you have to do
something special to allow it to be modified by the function you passed it
to?

Thanks.

-Eric de Groot

---

From: "Eric de Groot" <[email-masked]>
Date: Thu, 7 Jun 2001 04:41:55 -0700

Sam-

    Ok, I was just studying the lisp from the postgresql bindings in the
modules directory some more and it just clicked.  Through the entire they
whoever wrote just used c-pointer, rather than defining all the structs and
everything.  This will work for my mysql bindings aswell because mysql has
accessor functions for all the data needed from the structures.  I don't
need the details.  Damn, I wish I noticed this earlier!  Anyways, changing
everything to either c-pointer or (c-ptr-null nil) works!!  so far atleast;)

-Eric de Groot

---

From: "Eric de Groot" <[email-masked]>
Date: Thu, 7 Jun 2001 05:31:04 -0700

More questions!  I'm having a problem with number type conversions, I have
the following two C functions:

int myintfun() {
  return 11;
}

unsigned long long myulonglongfun() {
  return 11;
}

For the FFI lisp I have, respectively:

(def-c-call-out myintfun (:return-type int))
(def-c-call-out myulonglongfun (:return-type uint64))

'unsigned long long' is 8 bytes unsigned.  uint64 is the only unsigned 8
byte type you have listed in the FFI docs.  Anyways, after loading clisp up
with bindings for these, calling them gives me:

(MYSTUFF::myintfun)
11

(MYSTUFF::myulonglongfun)
52
(MYSTUFF::myulonglongfun)
0

it just gives a trash random number.

Any ideas why??  Thanks.

-Eric de Groot

---

From: Sam Steingold <[email-masked]>
Date: 07 Jun 2001 13:52:09 -0400

> * In message <011401c0eee8$9312f390$4381fb18@CX417245D>
> * Sent on Wed, 6 Jun 2001 17:27:02 -0700
>     in C, int (*f)(); and int f(); are not equivalent.

year - I got it mixed up with the fact that you can write, say, "sin"
instead of "&sin" for the address of the functions "double sin(double)".

please apply this patch to foreign1.lisp and try again.
your help is greatly appreciated.

@@ -601,7 +546,17 @@
              ((c-ptr c-ptr-null c-array-ptr)
               (to-c-typedecl (svref c-type 1) (format nil "* ~A" name)))
              (c-function
-              (to-c-typedecl (svref c-type 1) (format nil "~A ()" name)))
+              (to-c-typedecl (svref c-type 1)
+                             (format nil "(~A) (~{~A~^,~})" name
+                                     (do ((ii 0 (+ 2 ii)) ret
+                                          (arg (gensym "arg")))
+                                         ((= (length (svref c-type 2)) ii)
+                                          (nreverse ret))
+                                       (push (to-c-typedecl
+                                              (svref (svref c-type 2) ii)
+                                              (format nil "~a_~d"
+                                                      arg (/ ii 2)))
+                                             ret)))))
              (t (error (ENGLISH "illegal foreign data type ~S")
                        c-type))))))))

-- 
Sam Steingold (http://www.podval.org/~sds)
Support Israel's right to defend herself! <http://www.i-charity.com/go/israel>
Read what the Arab leaders say to their people on <http://www.memri.org/>
God had a deadline, so He wrote it all in Lisp.

---

From: "Eric de Groot" <[email-masked]>
Date: Thu, 7 Jun 2001 13:02:09 -0700

Sam-

    Ok thanks, that last patch worked.  I can do everything without having
to manually edit any files.  Excellent.  Do you have any ideas about my
other questions?  If I define all the c structs in lisp and use them as the
types in my def-c-call-outs rather than just using c-pointer, I cannot do
the following:

NIL

I have to do:

"3.22.32-log"

Here are the def-c-callouts using the structs used in the first example:

(def-c-call-out mysql_init (:arguments (mysql (c-ptr-null st_mysql)))
                           (:return-type (c-ptr-null st_mysql)))
(def-c-call-out mysql_get_server_info (:arguments (mysql (c-ptr-null
st_mysql)))
                                      (:return-type c-string))
(def-c-call-out mysql_connect (:arguments (mysql (c-ptr-null st_mysql))
                                          (host c-string)
                                          (user c-string)
                                          (passwd c-string))
                              (:return-type (c-ptr-null st_mysql)))

and heres how I redefined them to use c-pointer, which works, as seen in the
above second example:

(def-c-call-out mysql_init (:arguments (mysql (c-ptr-null nil)))
                           (:return-type c-pointer))
(def-c-call-out mysql_get_server_info (:arguments (mysql c-pointer))
                                      (:return-type c-string))
(def-c-call-out mysql_connect (:arguments (mysql c-pointer)
                                          (host c-string)
                                          (user c-string)
                                          (passwd c-string))
                              (:return-type c-pointer))

How can I get the pointers to work in the second example?

-Eric de Groot

---

From: "Eric de Groot" <[email-masked]>
Date: Thu, 7 Jun 2001 22:19:18 -0700

Ok, I wrote clisp bindings for libmysqlclient.  With the following test
code:

(let ((conn (MYSQL::mysql_init nil)))
  (MYSQL::mysql_connect conn "localhost" "root" "")
  (MYSQL::mysql_get_server_info conn)
  (MYSQL::mysql_select_db conn "mysql")
  (MYSQL::mysql_query conn "select * from user")
  (let ((result (MYSQL::mysql_use_result conn)))
    (loop
      (let ((row (MYSQL::mysql_fetch_row result)))
        (when (eq row nil)
          (return t))
        (print row)))
    (MYSQL::mysql_free_result result))
  (MYSQL::mysql_close conn))

I get:

*** - handle_fault error2 ! address = 0xFE0000 not in
[0x2027A000,0x20337404) !
SIGSEGV cannot be cured. Fault address = 0xFE0000.
Segmentation fault

but, if I run just this part of the code first, followed by the entire
thing:

[1]>
(let ((conn (MYSQL::eric_mysql_init)))
  (MYSQL::mysql_connect conn "localhost" "root" "")
  (MYSQL::mysql_get_server_info conn)
  (MYSQL::mysql_select_db conn "mysql")
  (MYSQL::mysql_query conn "select * from user"))
0
[2]>
(let ((conn (MYSQL::mysql_init nil)))
  (MYSQL::mysql_connect conn "localhost" "root" "")
  (MYSQL::mysql_get_server_info conn)
  (MYSQL::mysql_select_db conn "mysql")
  (MYSQL::mysql_query conn "select * from user")
  (let ((result (MYSQL::mysql_use_result conn)))
    (loop
      (let ((row (MYSQL::mysql_fetch_row result)))
        (when (eq row nil)
          (return t))
        (print row)))
    (MYSQL::mysql_free_result result))
  (MYSQL::mysql_close conn))

#("localhost" "root" "" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y"
"Y" "")
#("cx417245-c" "root" "" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y" "Y"
"Y" "")
#("localhost" "" "" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N"
"")
#("cx417245-c" "" "" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N" "N"
"Y")

[3]>

it works!

but then if I past it a couple more times it eventually seg faults anyway.

Any idea what might be causing this??

again, you can see my files at http://www.ericdegroot.com/clisp/

this is on:
debian 2.2 (2.2.19pre17)
GNU CLISP 2.26.1 (released 2001-06-04) (built 3200899615) (memory
3201017859)

Thanks!  I'm so close..

-eric