1
votes

I'm playing with idea of loading С/Rust/etc functions via FFI in Racket. I'd like to specify list of function names as strings and then just load them by some helper function. Main problem is creating identifier/word from a string. For example, it is very simple in Rebol:

foo: "test1"
set to-word (rejoin [foo "_result_data"]) some
print test1_result_data

but in Racket I have to use syntax stuff. So I've found examples like How do I define functions using Racket macros? and Racket Macro to auto-define functions given a list. They cover a lot knowledge I need so I've written next snippet:

#lang racket
(require (for-syntax racket/syntax ffi/unsafe))

(define-for-syntax *method-names*
  ; Given I have hello, one and two methods in my shared lib
  (list "hello"
        "one"
        "two"
        ))

(define-syntax (define-ffi-func stx)
  (syntax-case stx ()
    [(_)
     (let ([elem->fn-id 
            (λ (elem-str)
              (format-id 
               stx "~a" 
               (datum->syntax stx (string->symbol elem-str))))]
            [rustlib
              (ffi-lib "/path/to/libffitest.dylib")
              ]
            )
       (with-syntax 
         ([((method cation) ...)
           (map 
            (λ (elem)
              ; I can load C code here for sure :) But not much more
              ; (define c (get-ffi-obj elem rustlib (_fun -> _int)
              ;  (lambda ()
              ;     (error 'rustlib
              ;      "installed lib does not provide given method"))))
              (list (elem->fn-id elem) elem)
            )
            *method-names*)])
         #`(begin
             (define (method) 
              ; I'm curious what should be here
                1
              )
             ...)))]))

(define-ffi-func)
; Actually one, two and hello methods are in scope
; but surely they all return 1
(two) 

But still I can't bond new Racket methods with ffi calls. I guess I can't match them in with-syntax body but further syntax definition knows nothing about external modules (e.g.

#`(begin
    (define (ate) 
        (get-ffi-obj elem rustlib (_fun -> _int))
         )
     ...)))]))

will not work.

I think I'm missing something about binding match too.

How can I get FFI-bonded methods by specifying a list of names? Thanks in advance!

2

2 Answers

2
votes

Since you already answered your main question, I just wanted to note that it's not very Rackety in general to use lists of strings to represent names. It's usually better to use identifiers. Do you actually need to dynamically compute these strings?

If you don't, then it's very simple to write a macro to define all the FFI bindings given a list of identifiers:

(define-syntax-rule (define-ffi-functions name ...)
  (begin (define name
           (get-ffi-obj (quote name) ffi-lib (_fun -> _int)))
         ...))

(define-ffi-functions hello one two)

with a suitable definition of ffi-lib.

0
votes

Finally ended up with something like this:

(define-for-syntax *method-names*
  (list "hello"
        "one"
        "two"
        ))

(define rustlib (ffi-lib "/path/to/Projects/libffitest.dylib"))

(define-syntax (define-ffi-func stx)
  (syntax-case stx ()
    [(_ lib ffi-func)
     (let ([elem->fn-id 
            (λ (elem-str)
              (format-id 
               stx "~a" 
               (datum->syntax stx (string->symbol elem-str))))]
            )
       (with-syntax 
         ([((method name) ...)
           (map 
            (λ (elem)
              (list (elem->fn-id elem) elem)
            )
            *method-names*)])
         #`(begin
             (define method
                (ffi-func name lib (_fun -> _int))
              )
             ...)))]))

(define-ffi-func rustlib get-ffi-obj)
(+ (one) (two))

Still needs polishing but in general is understandable.