在 Racket 中动态创建 FFI 方法
Dynamically create FFI methods in Racket
我正在考虑通过 Racket 中的 FFI 加载 С/Rust/etc 函数。我想将函数名称列表指定为字符串,然后通过一些辅助函数加载它们。主要问题是从字符串创建 identifier/word 。比如在Rebol中很简单:
foo: "test1"
set to-word (rejoin [foo "_result_data"]) some
print test1_result_data
但在 Racket 中我必须使用语法。所以我找到了像 How do I define functions using Racket macros? and Racket Macro to auto-define functions given a list 这样的例子。它们涵盖了我需要的很多知识,所以我写了下一个片段:
#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)
但我仍然无法将新的 Racket 方法与 ffi 调用结合起来。我想我无法在 with-syntax
正文中匹配它们,但进一步的语法定义对外部模块一无所知(例如
#`(begin
(define (ate)
(get-ffi-obj elem rustlib (_fun -> _int))
)
...)))]))
不会工作。
我想我也遗漏了一些关于绑定匹配的内容。
如何通过指定名称列表获得 FFI 绑定方法?提前致谢!
最后得到这样的结果:
(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))
仍有待完善,但大体上可以理解。
既然你已经回答了你的主要问题,我只是想指出,通常使用字符串列表来表示名称并不是很容易搞砸。通常最好使用标识符。您真的需要动态计算这些字符串吗?
如果你不这样做,那么编写一个宏来定义给定标识符列表的所有 FFI 绑定将非常简单:
(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)
具有 ffi-lib
的适当定义。
我正在考虑通过 Racket 中的 FFI 加载 С/Rust/etc 函数。我想将函数名称列表指定为字符串,然后通过一些辅助函数加载它们。主要问题是从字符串创建 identifier/word 。比如在Rebol中很简单:
foo: "test1"
set to-word (rejoin [foo "_result_data"]) some
print test1_result_data
但在 Racket 中我必须使用语法。所以我找到了像 How do I define functions using Racket macros? and Racket Macro to auto-define functions given a list 这样的例子。它们涵盖了我需要的很多知识,所以我写了下一个片段:
#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)
但我仍然无法将新的 Racket 方法与 ffi 调用结合起来。我想我无法在 with-syntax
正文中匹配它们,但进一步的语法定义对外部模块一无所知(例如
#`(begin
(define (ate)
(get-ffi-obj elem rustlib (_fun -> _int))
)
...)))]))
不会工作。
我想我也遗漏了一些关于绑定匹配的内容。
如何通过指定名称列表获得 FFI 绑定方法?提前致谢!
最后得到这样的结果:
(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))
仍有待完善,但大体上可以理解。
既然你已经回答了你的主要问题,我只是想指出,通常使用字符串列表来表示名称并不是很容易搞砸。通常最好使用标识符。您真的需要动态计算这些字符串吗?
如果你不这样做,那么编写一个宏来定义给定标识符列表的所有 FFI 绑定将非常简单:
(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)
具有 ffi-lib
的适当定义。