ChezScheme icon indicating copy to clipboard operation
ChezScheme copied to clipboard

ftype-pointer and foreign-procedure: counter-intuitive behavior

Open stergiotis opened this issue 6 years ago • 8 comments

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.

  1. Would be neat as it would allow to declare pointer to pointers correctly. Currently only base types do seem to be supported.
  2. Is a bit of a hack as it mimics the behavior of C but not that of C++.
  3. 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?

stergiotis avatar Feb 11 '18 20:02 stergiotis

;; 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.

jltaylor-us avatar Feb 12 '18 00:02 jltaylor-us

Thanks a lot for your quick response.

  1. 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-names by means of the define-ftype special form. These user-defined ftype-names 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.

stergiotis avatar Feb 12 '18 12:02 stergiotis

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.

jltaylor-us avatar Feb 12 '18 15:02 jltaylor-us

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.

evilbinary avatar Feb 24 '18 03:02 evilbinary

Any update on this?

amirouche avatar Jun 01 '19 22:06 amirouche

Pull request #213 has been merged. It describes how to pass structs directly.

akeep avatar Jun 05 '19 02:06 akeep

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:

  1. Pointers to non-base types are currently not supported (e.g. (* (* char)) or (* my-ftype) can not be used as argument types).
  2. 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.

stergiotis avatar Jun 07 '19 11:06 stergiotis

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.

dybvig avatar Jun 13 '19 22:06 dybvig