Subject: [clisp-list] linux:seed48 & C declaration questions (was: linux FFI: What/wher e is _IO_stdin/out/err_?)

From: "Hoehle, Joerg-Cyril" <[email-masked]>
Date: Fri May 16 05:47:25 2003

Hi,

Who is firm in C prototypes and declarations?

Sam Steingold wrote:
>[2]> (linux:getpwent)
>#S(LINUX:passwd :|pw_name| "root" :|pw_passwd| "x" :|pw_uid| 0 
>:|pw_gid| 0
>   :|pw_gecos| "root" :|pw_dir| "/root" :|pw_shell| "/bin/bash")
>[3]> (linux::putpwent (linux:getpwent) linux:stdout)
>
>*** - handle_fault error2 ! address = 0x0 not in 
>[0x20228000,0x203673ac) !
>SIGSEGV cannot be cured. Fault address = 0x0.
>Segmentation fault
This was a bug in putpwent that's just been fixed.
There's also one in seed48.

I have a question about the correct prototype for seed48().

According to the Linux manpage, it should be:
(def-call-out seed48 (:arguments (seed16v (c-ptr (c-array ushort 3))))
  (:return-type (c-ptr (c-array ushort 3)) :none))
Because it returns a pointer to an array of three unsigned shorts.

However, this definition causes trouble to gcc:
linux.c:67: `seed48' declared as function returning an array
linux.c:67: conflicting types for `seed48'
/usr/include/stdlib.h:473: previous declaration of `seed48'

Here is what linux.c contains:
extern uint16 (* (seed48)()[3]);

And here is stdlib.h:
extern unsigned short int *seed48 (unsigned short int __seed16v[3]) __THROW;

What can be done to improve this situation?
[The current definition in linux.lisp is broken, meant to be
  (def-call-out seed48 (:arguments (seed16v (c-ptr (c-array ushort 3))))
    (:return-type (c-ptr ushort) :none))
which wrongly returns a single short.]

Maybe this should be generated instead??
extern uint16 (* (seed48)())[3];
But that looks like one too many indirection.

This could be a bug in foreign1.lisp:
TO-C-TYPEDECL works,
FFI[31]> (ffi::to-c-typedecl (parse-c-type '(c-function (:return-type (c-ptr (c-array uint16 3))))) "seed48")
"uint16 (* (seed48) ())[3]"
but ffi::finalize-coutput-file bypasses to-c-typedecl and generates a different extern declaration
uint16 (* (seed48)()[3]);
Note the trailing ')'.

The prototype from finalize-coutput-file is even completely incorrect when I specify :built-in:
extern uint16 (* (seed48b)()[3]uint16 (* )[3]);
which is generated from:
(def-call-out seed48b (:arguments (seed16v (c-ptr (c-array ushort 3))))
  (:return-type (c-ptr (c-array ushort 3)) :none) (:built-in t))

However, it seems like FFI::to-c-typedecl is also subtly broken w.r.t. arrays:
(ffi::to-c-typedecl (parse-c-type '(c-function (:arguments (p (c-array uint16 3))) (:return-type (c-ptr (c-array uint16 3))))) "seed48")
"uint16 (* (seed48) (uint16 (arg692)[3]))[3]"
But (c-array #) is not the correct definition in FFI, (c-ptr (c-array #)) is.

FFI[28]> (ffi::to-c-typedecl (parse-c-type '(c-function (:arguments (p (c-ptr (c-array uint16 3)))) (:return-type (c-ptr (c-array uint16 3))))) "seed48")
"uint16 (* (seed48) (uint16 (* arg673)[3]))[3]"

That looks like too many indirections in the parameter list...

It looks like (c-ptr (c-array ...)) or c-array-max must internally be handled as (c-array[-max] #) for C declaration purposes.

And it looks like the return type must be treated specially w.r.t. (c-ptr (c-array X)) and handle that like (c-ptr X).

<irony/I really love C!/

Oh, I forgot. If you want to try out to-c-typedecl interactively yourself, you'll need:
(setq *ffi-module* (make-ffi-module :name "foo" :c-name "foo"))

Thanks,
	Jorg Hohle.