Hi! taylanbayi...@gmail.com (Taylan Ulrich "Bayırlı/Kammer") skribis:
> scheme@(guile-user)> (import (bytestructures guile)) > scheme@(guile-user)> (import (bytestructures guile ffi)) > scheme@(guile-user)> (define foo_t (bs:struct `((s ,(bs:struct `((x ,int) > (y > ,float32)))) > (z ,int) > (t ,(bs:vector 3 float32))))) > scheme@(guile-user)> (define foo (bytestructure foo_t '((s ((x 1) > (y 2.2))) > (z 3) > (t #(0 1 2))))) Gradually, without thinking much about it ;-), I ended up writing something along these lines in (guix build syscalls), but with slightly different goals. Initially, that was because I was unsatisfied with ‘make-c-struct’ and ‘parse-c-struct’, notably the fact that they insisted on creating lists and computing offsets etc. at run time. The ‘define-c-struct’ macro in that module defines a variable containing the size in bytes of the structure, a procedure to write an instance of that structure to a bytevector, and one to read from a bytevector. Example: --8<---------------cut here---------------start------------->8--- (define-c-struct sockaddr-in ;<linux/in.h> sizeof-sockaddrin (lambda (family port address) (make-socket-address family address port)) read-sockaddr-in write-sockaddr-in! (family unsigned-short) (port (int16 ~ big)) (address (int32 ~ big))) ;; … (define (write-socket-address! sockaddr bv index) "Write SOCKADDR, a socket address as returned by 'make-socket-address', to bytevector BV at INDEX." (let ((family (sockaddr:fam sockaddr))) (cond ((= family AF_INET) (write-sockaddr-in! bv index family (sockaddr:port sockaddr) (sockaddr:addr sockaddr))) ((= family AF_INET6) (write-sockaddr-in6! bv index family (sockaddr:port sockaddr) (sockaddr:flowinfo sockaddr) (sockaddr:addr sockaddr) (sockaddr:scopeid sockaddr))) (else (error "unsupported socket address" sockaddr))))) (define* (read-socket-address bv #:optional (index 0)) "Read a socket address from bytevector BV at INDEX." (let ((family (bytevector-u16-native-ref bv index))) (cond ((= family AF_INET) (read-sockaddr-in bv index)) ((= family AF_INET6) (read-sockaddr-in6 bv index)) (else ;; XXX: Unsupported address family, such as AF_PACKET. Return a ;; vector such that the caller can at least use 'sockaddr:fam'. (vector family))))) --8<---------------cut here---------------end--------------->8--- The expanded ‘read-sockaddr-in’ looks like this: --8<---------------cut here---------------start------------->8--- (define* (read-sockaddr-in bv #:optional (offset 0)) (let ((family (bytevector-uint-ref bv (#{1+}# (logior (#{1-}# offset) (#{1-}# 2))) (native-endianness) 2)) (port (bytevector-uint-ref bv (#{1+}# (logior (#{1-}# (+ (#{1+}# (logior (#{1-}# offset) (#{1-}# 2))) 2)) (#{1-}# 2))) 'big 2)) (address (bytevector-uint-ref bv (#{1+}# (logior (#{1-}# (+ (#{1+}# (logior (#{1-}# (+ (#{1+}# (logior (#{1-}# offset) (#{1-}# 2))) 2)) (#{1-}# 2))) 2)) (#{1-}# 4))) 'big 4))) (make-socket-address family address port))) --8<---------------cut here---------------end--------------->8--- No extra allocations and computations. http://git.savannah.gnu.org/cgit/guix.git/tree/guix/build/syscalls.scm#n264 I’m sure we could build a higher-level interface on top of that, for instance for when we want a C struct to directly have a corresponding Scheme record (as is the case of ‘%statfs’ in the file above.) Ludo’.