From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Matt Wette Newsgroups: gmane.lisp.guile.devel,gmane.lisp.guile.user Subject: ffi-help: documentation Date: Wed, 6 Dec 2017 18:46:59 -0800 Message-ID: <372AB0BD-F04E-4BEA-9496-88E46FF2676D@gmail.com> References: <410F22E6-00DC-49BD-910F-6F2E2BFE9C94@gmail.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: quoted-printable X-Trace: blaine.gmane.org 1512614844 8886 195.159.176.226 (7 Dec 2017 02:47:24 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 7 Dec 2017 02:47:24 +0000 (UTC) Cc: guile-devel To: Guile User Mailing List Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Thu Dec 07 03:47:18 2017 Return-path: Envelope-to: guile-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1eMmDV-00028p-IG for guile-devel@m.gmane.org; Thu, 07 Dec 2017 03:47:17 +0100 Original-Received: from localhost ([::1]:58752 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eMmDb-0005M4-00 for guile-devel@m.gmane.org; Wed, 06 Dec 2017 21:47:23 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:52965) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eMmDO-00056G-5l for guile-devel@gnu.org; Wed, 06 Dec 2017 21:47:11 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eMmDM-0006oi-Fk for guile-devel@gnu.org; Wed, 06 Dec 2017 21:47:10 -0500 Original-Received: from mail-pg0-x22f.google.com ([2607:f8b0:400e:c05::22f]:38068) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eMmDH-0006gY-75; Wed, 06 Dec 2017 21:47:03 -0500 Original-Received: by mail-pg0-x22f.google.com with SMTP id f12so3441480pgo.5; Wed, 06 Dec 2017 18:47:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=mCjVql5SZVdfqiT1AbfhSnn7shHE1BMzQGnb7a/p4wo=; b=WhLaiMxzeVchZOzTIsdqGUmrWbkzUepMA1qIgVP3zkwuco5q9eCW0wzqAmpZmIuqxu /MGbI6g83iEbL4VH3tL4rN2sLwgBHnUBBu94CMd4+MYrKE+8hiiZb/P5uCFHbG3avYwi +2IQ1hS0eh1tsWK4we2WSNI8Q6e+rwsMX1bgS7SKN1NMVZNK25tTzqJTv0Oe7kiqFyVL mY7gphhV49Hn0eNoU/koes0Z3C9FVdM8W0lOigCQxGGePw8TGvSy8O3SSfZqerBdNL8Q D3AAn3t6+TI2p8YbeIMTP5Sd4thPEc+FeyWojzeC/0ZVccY0ps6tmt8BuSQt7KJymhIp UM1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=mCjVql5SZVdfqiT1AbfhSnn7shHE1BMzQGnb7a/p4wo=; b=PjoL9+4pqx3tIqVBRpA02G6sh5acN1g5Ici55h20t+gAPRiiW5FZu1elpj/mLxfu75 6GbEoogvjeE4bO5y1VQt8lAuxtNtBCBHH9KY+tPGHWgibWP+TBFj21hN9tmCHy+8WhPb IRYPiPaqB19p2GGaIvWfmGypbpp4kMBEACCR83W7nLArD3jjz13dhZ2fQHIPp70MaYFd R2bUpA1O2CSEnoltsTpKnN8SeSCrdYC4x1Zj07saMkLo/0EaNxb+RuyZSovi623SSHRy bLnh3Tx6/eo2ZO/cEcPrJTPqHofA7egehfuxFn6Lig2Nlg8v7SnICvSi6mH4zpwKp1nA er5w== X-Gm-Message-State: AJaThX46GftSE7Sn86S4rB6oWpQS16GLnl4gOi9Knj0OK3TpbOVNFH1p OUcUmAK6pJliz9t/fZxbwBrQbmUh X-Google-Smtp-Source: AGs4zMa3TecRqPsWCCf20D7uE+cGTc2btefNjtoGfTDcbpZyHids7BWYhgakTo0BO+4BPfWZgehd+A== X-Received: by 10.99.174.1 with SMTP id q1mr23178351pgf.434.1512614821288; Wed, 06 Dec 2017 18:47:01 -0800 (PST) Original-Received: from nautilus.championbroadband.com (216-165-229-229.championbroadband.com. [216.165.229.229]) by smtp.gmail.com with ESMTPSA id k63sm7462141pfk.172.2017.12.06.18.47.00 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 06 Dec 2017 18:47:00 -0800 (PST) In-Reply-To: <410F22E6-00DC-49BD-910F-6F2E2BFE9C94@gmail.com> X-Mailer: Apple Mail (2.3273) X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c05::22f X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Developers list for Guile, the GNU extensibility library" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Original-Sender: "guile-devel" Xref: news.gmane.org gmane.lisp.guile.devel:19397 gmane.lisp.guile.user:14327 Archived-At: Hi All, I am working on a ffi-helper (FH): a program that will read in a C dot-h = file=20 and generate a Guile dot-scm file which defines a module to provide = hooks into the associated C library. =20 Currently, I'm working on documentation. I like manuals that start out = with=20 simple demos to show what's going on. That is my approach here. Enjoy = ... FFI Helper for Guile ******************** Matt Wette December 2017 With NYACC Version 0.00.0 1 Introduction ************** The acronym FFI stands for "Foreign Function Interface". It refers to the Guile facility for binding functions and variables from C source libraries into Guile programs. This distribution provides utilities for generating a loadable Guile module from a set of C declarations and associated libraries. The C declarations can, and conventionally do, come from naming a set of C include files. The nominal method for use is to write a _ffi-module_ specification in a file which includes a 'define-ffi-module' declaration and then use the command 'guild compile-ffi' to convert this to Guile Scheme. $ guild compile-ffi ffi/cairo.ffi wrote `ffi/cairo.scm' Note that no C code is generated. The hooks to access C-coded functions in the Cairo library are provided in 100% Guile Scheme. The compiler for the FFI Helper (FH) is based on the C parser and utilities which are included in the NYACC (https://www.nongnu.org/nyacc) package. Development for the FH is currently being performed in the 'c99dev' branch of the associated git repository. Within the NYACC distribution, the relevant modules can be found under the directory 'examples/'. Use of the FFI-helper module depends on the _scheme-bytestructure_ package available from . Since this package is currently not under version control we provide a partial copy in the NYACC distribution. You are probably hoping to see an example, so let's do that. 2 Demonstration *************** This is a small FFI Helper example to illustrate its use. We will start with the Cairo (cairographics.org) package because that is the first one I started with in developing this package. Say you are an avid Guile user and want to be able to use cairo in Guile. On most systems this comes with associated _pkg-config_ support files. WARNING: The FFI-helper package is under active development and there is some chance the following example will cease to work in the future. If you want to follow along and are working in the distribution tree, you should source the file 'env.sh' in the 'examples' directory. By practice, I like to put all FH generated modules under a directory called 'ffi/', so we will do that. We start by generating, in the 'ffi' directory, a file named 'cairo.ffi' with the following contents: (define-ffi-module (ffi cairo) #:pkg-config "cairo" #:include '("cairo.h" "cairo-pdf.h" "cairo-svg.h")) Now to generate a Guile module you use 'guild' as follows: $ guild compile-ffi ffi/cairo.ffi wrote `ffi/cairo.scm' Though the file 'cairo/cairo.ffi' is only three lines long, the file 'ffi/cairo.scm' will be over five thousand lines long. It looks like the following: (define-module (ffi cairo) #:use-module (system ffi-help-rt) #:use-module ((system foreign) #:prefix ffi:) #:use-module (bytestructures guile) ) (define link-libs (list (dynamic-link "libcairo"))) ;; int cairo_version(void); (define ~cairo_version (delay (fh-link-proc ffi:int "cairo_version" (list) link-libs))) (define (cairo_version) (let () ((force ~cairo_version)))) (export cairo_version) ... ;; typedef struct _cairo_matrix { ;; double xx; ;; double yx; ;; double xy; ;; double yy; ;; double x0; ;; double y0; ;; } cairo_matrix_t; (define-public cairo_matrix_t-desc (bs:struct (list `(xx ,double) `(yx ,double) `(xy ,double) `(yy ,double) `(x0 ,double) `(y0 ,double)))) (define-fh-compound-type cairo_matrix_t cairo_matrix_t-desc = cairo_matrix_t? make-cairo_matrix_t) (export cairo_matrix_t cairo_matrix_t? make-cairo_matrix_t) ... many, many more declarations ... ;; access to enum symbols and #define'd constants: (define ffi-cairo-symbol-val (let ((sym-tab '((CAIRO_SVG_VERSION_1_1 . 0) (CAIRO_SVG_VERSION_1_2 . 1) (CAIRO_PDF_VERSION_1_4 . 0) (CAIRO_PDF_VERSION_1_5 . 1) (CAIRO_REGION_OVERLAP_IN . 0) (CAIRO_REGION_OVERLAP_OUT . 1) (CAIRO_REGION_OVERLAP_PART . 2) (CAIRO_FILTER_FAST . 0) (CAIRO_FILTER_GOOD . 1) (CAIRO_FILTER_BEST . 2) ... more constants ... (CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID . "application/x-cairo.jbig2-global-id")))) (lambda (k) (or (assq-ref sym-tab k))))) (export ffi-cairo-symbol-val) (export cairo-lookup) ... more ... Note that from the _pkg-config_ spec the FH compiler picks up the required libraries to bind in. Also, '#define' based constants, as well as those defined by enums, are provided in a lookup function 'ffi-cairo-symbol-val'. So, for example guile> (use-modules (ffi cairo)) ;;; ffi/cairo.scm:6112:11: warning: possibly unbound variable = `cairo_raster_source_acquire_func_t*' ;;; ffi/cairo.scm:6115:11: warning: possibly unbound variable = `cairo_raster_source_release_func_t*' guile> (ffi-cairo-symbol-val 'CAIRO_FORMAT_ARGB32)) $1 =3D 0 We will discuss the warnings later. These are signals that extra code needs to be added to the ffi module. But you see how the constants (but not CPP function macros) can be accessed. Let's try something more useful: a real program. Create the following code in a file, say 'cairo-demo.scm', first up a Guile sesion and 'load' the file. (use-modules (ffi cairo)) (define srf (cairo_image_surface_create 'CAIRO_FORMAT_ARGB32 200 = 200)) (define cr (cairo_create srf)) (cairo_move_to cr 10.0 10.0) (cairo_line_to cr 190.0 10.0) (cairo_line_to cr 190.0 190.0) (cairo_line_to cr 10.0 190.0) (cairo_line_to cr 10.0 10.0) (cairo_stroke cr) (cairo_surface_write_to_png srf "cairo-demo.png") (cairo_destroy cr) (cairo_surface_destroy srf) If we set up everything correctly you should have the target 'png' image of a square. A few items in the above code are notable. First, the call to 'cairo_image_surface_create' accepted a symbolic form (''CAIRO_FORMAT_ARGB32' for the format. It would have also accepted the associated constant '0'. In addition, '(ffi cairo)' function will accept Scheme strings where the C function wants "pointer to string." Now try this in your Guile session: guile> srf $4 =3D # guile> cr $5 =3D # Note that the FH keeps track of the C types you use. This can be useful for debugging but may bloat the namespace. The constants you see are the pointer values. But it goes further. Let's generate a matrix type: guile> (define m (make-cairo_matrix_t)) guile> m $6 =3D # guile> (pointer-to m) $7 =3D # When it comes to C APIs that expect the user to allocate memory for a structure and pass the pointer address to the C function, FH provides the solution: guile> (cairo_get_matrix cr (pointer-to m)) guile> (use-modules (system ffh-help-rt)) guile> (fh-object-ref m 'xx) $9 =3D 1.0