ChezScheme
ChezScheme copied to clipboard
ftype-pointer and foreign-procedure: counter-intuitive behavior
Consider the following example program:
(import (chezscheme))
(load-shared-object "libc.so.6")
(define memcpy/void* (foreign-procedure "memcpy" (void* void* size_t) void*))
(define sz (ftype-sizeof uptr))
(define a-addr (foreign-alloc sz))
(define b-addr (foreign-alloc sz))
(define a (make-ftype-pointer uptr a-addr))
(define b (make-ftype-pointer uptr b-addr))
(ftype-set! uptr () a #xdeadbeef)
(memcpy/void* b-addr a-addr sz)
(pretty-print (number->string (ftype-ref uptr () b) 16))
(foreign-free a-addr)
(foreign-free b-addr)
It works but is very weakly typed as we supply fixnums as argument for the foreign function: Supplying a-addr
and b-addr
instead of a
and b
circumvents the type checks. To strengthen the checks I expected that all of the following three variations are valid. They are however rejected as invalid.
;; 1) declare memcpy with typed pointers to be able to use ftype-pointer
(define memcpy/uptr* (foreign-procedure "memcpy" (uptr* uptr* size_t) void*)) ;; --> exception: invalid foreign-procedure argument type specifier uptr*
(memcpy/uptr* b a sz)
;; 2) rely on C rule that a typed pointer will be down-cast to void*
(memcpy/void* b a sz) ;; --> Exception in memcpy/void*: invalid foreign-procedure argument #<ftype-pointer uptr 94627085533504>
;; 3) make a void* ftype-pointer
(memcpy/void* (make-ftype-pointer void b-addr)
(make-ftype-pointer void a-addr)) ;; --> Exception: unrecognized ftype name void
In my opinion this would be a valuable addition to the beautiful Chez ftype interface.
- Would be neat as it would allow to declare pointer to pointers correctly. Currently only base types do seem to be supported.
- Is a bit of a hack as it mimics the behavior of C but not that of C++.
- Seems to be quite nice as it allows to explicitly cast any pointer to void* which is a useful and proven idiom.
Any thoughts on this?
;; 1) declare memcpy with typed pointers to be able to use ftype-pointer (define memcpy/uptr* (foreign-procedure "memcpy" (uptr* uptr* size_t) void*)) ;; --> exception: invalid foreign-procedure argument type specifier uptr*
That's not how you say "pointer to an object of type uptr".
> (define memcpy/uptr* (foreign-procedure "memcpy" ((* uptr) (* uptr) size_t) void*))
> (memcpy/uptr* a b sz)
;; 3) make a void* ftype-pointer (memcpy/void* (make-ftype-pointer void b-addr) (make-ftype-pointer void a-addr)) 3. Seems to be quite nice as it allows to explicitly cast any pointer to void* which is a useful and proven idiom.
Your code and descriptive text don't match. You can make an ftype pointer to objects of void*
just fine, but that's not the same thing as an ftype pointer to the non-existent void
ftype.
It's true that Chez does not have any way to make an ftype pointer that points to an unknown ftype.
Thanks a lot for your quick response.
- Is solved by using the proper syntax.
I am sorry for the confusion, the minimal example I gave above was perhaps a bit too minimalistic. The code I have been experimenting with established various ftype-name
s by means of the define-ftype
special form. These user-defined ftype-name
s do not seem to be available in the foreign-procedure
declaration: invalid (non-base) foreign-procedure argument ftype)
.
Regarding the void
pointer: You are right that constructing a void**
pointer is perfectly feasible in Chez (i.e. (make-ftype-pointer void* 0)
). It is also clear that the void*
case however is special as the void
type is not a proper datatype (e.g. (foreign-sizeof 'void)
is doomed). This however means proposition 2) would make sense as void*
pointers are not first-class citizens in Chez whereas they are in the interfaced language.
These user-defined ftype-names do not seem to be available in the foreign-procedure declaration: invalid (non-base) foreign-procedure argument ftype).
Foreign procedure definitions can use pointers to user-defined ftypes, but it cannot pass structs directly. There is a pull request currently under review ( #213 ) to enhance the foreign procedure system to allow passing and returning structs by value, but I don't know what the status of that review is.
I don't think that will help with your desire to have something closer to C's void*
type, though.
you just need a cffi for scheme like this https://github.com/evilbinary/scheme-lib/blob/master/apps/cffi-test.ss ,pretty eazy to call ffi.
Any update on this?
Pull request #213 has been merged. It describes how to pass structs directly.
The issue was not about passing structs using the C ABI . What I wanted to discuss originally were the following two limitations of the current ftype implementation:
- Pointers to non-base types are currently not supported (e.g.
(* (* char))
or(* my-ftype)
can not be used as argument types). - Pointers supplied for arguments declared as
void*
do not get automatically cast when passed as function arguments.
In practice I found the ftype FFI system really nice to work with but I regularly need to work around these two limitations.
You can use (* ftype-name)
as a foreign-procedure argument type or return-value type for any user-defined ftype name as well as for any base ftype name. You're correct, however, that you can't use (* (* char))
, because the syntax requires _x_
in (* _x_)
to be an identifier. That seems like a reasonable restriction since it's easy to give an ftype a name and easier to use when it has one. You're also correct that actual parameters for arguments declared as void*
aren't automatically cast, which would be a welcome extension.