在Racket中,我可以在调用另一个函数后导出函数吗?

在Racket中,我可以在调用另一个函数后导出函数吗?,c,scheme,racket,ffi,C,Scheme,Racket,Ffi,我正在尝试使用scheme的FFI创建到libpython的绑定。要做到这一点,我必须获得python的位置,创建ffi库,然后从中创建函数。例如,我可以这样做: (module pyscheme scheme (require foreign) (unsafe!) (define (link-python [lib "/usr/lib/libpython2.6.so"]) (ffi-lib lib)) 这一切都很好,但我想不出导出函数的方法。例如,我可以这样做: (de

我正在尝试使用scheme的FFI创建到libpython的绑定。要做到这一点,我必须获得python的位置,创建
ffi库
,然后从中创建函数。例如,我可以这样做:

(module pyscheme scheme
  (require foreign)
  (unsafe!)

  (define (link-python [lib "/usr/lib/libpython2.6.so"])
    (ffi-lib lib))
这一切都很好,但我想不出导出函数的方法。例如,我可以这样做:

(define Py_Initialize (get-ffi-obj "Py_Initialize" libpython (_fun -> _void)))
…但我必须以某种方式全局存储对libpython(由linkpython创建)的引用。一旦调用linkpython,有没有办法导出这些函数?换句话说,我希望使用该模块的人能够做到这一点:

(require pyscheme)
(link-python)
(Py_Initialize)
……或者:

(require pyscheme)
(link-python "/weird/location/for/libpython.so")
(Py_Initialize)
…但让它给出一个错误:

(require pyscheme)
(Py_Initialize)

我该怎么做呢?

我没有制作很多模块,但我认为您需要“提供”要加载到新名称空间中的函数

(module pyscheme scheme
  (provide
   link-python
   Py_Initialize
   <...>)
  (require foreign)
  (unsafe!)

  (define (link-python [lib "/usr/lib/libpython2.6.so"])
    (ffi-lib lib)))
(require pyscheme)
(link-python "/weird/location/for/libpython.so")
(Py_Initialize)
(模块pyscheme)
(提供
链接python
Py_初始化
)
(需要外文)
(不安全!)
(定义(链接python[lib”/usr/lib/libpython2.6.so“)
(ffi lib)
(计划)
(链接python“/wird/location/for/libpython.so”)
(Py_初始化)

像这样做的最简单的方法可能是延迟绑定,直到需要它们为止。类似以下(未测试)代码:

Ir您可以在函数本身内部进行设置,这样就不会绑定从未调用过的函数:

#lang scheme

(require scheme/foreign)
(unsafe!)

(define libpython #f)

(define (link-python [lib "/usr/lib/libpython2.6.so"])
  (if libpython (error "Foo!") (set! libpython (ffi-lib lib))))

(define (Py_Initialize . args)
  (if libpython
    (begin (set! Py_Initialize
                 (get-ffi-obj "Py_Initialize" libpython
                              (_fun -> _void)))
           (apply Py_Initialize args))
    (error 'Py_Initialize "python not linked yet")))
由于您不希望对每个函数都执行此操作,因此应将其包装在宏中:

#lang scheme

(require scheme/foreign)
(unsafe!)

(define libpython #f)

(define (link-python [lib "/usr/lib/libpython2.6.so"])
  (if libpython (error "Foo!") (set! libpython (ffi-lib lib))))

(define-syntax-rule (defpython <name> type)
  (define (<name> . args)
    (if libpython
      (begin (set! <name> (get-ffi-obj '<name> libpython <type>))
             (apply <name> args))
      (error '<name> "python not linked yet"))))

(defpython Py_Initialize (_fun -> _void))
(defpython Py_Foo (_fun _int _int -> _whatever))
...more...
#lang方案
(需要计划/国外)
(不安全!)
(定义libpython#f)
(定义(链接python[lib”/usr/lib/libpython2.6.so“)
(如果libpython(错误“Foo!”)(set!libpython(ffi-lib)))
(定义语法规则(定义Python类型)
(定义(.args)
(如果是libpython
(开始(设置!(获取ffi obj'libpython))
(应用args))
(错误“python尚未链接”))
(defpythonpy_初始化(_fun->_void))
(不管怎样)
更多
但有两个高层注意事项:

  • 尽管这是可能的,但以这种方式拖延事情似乎很难看。我宁愿使用一些在代码启动时已知的环境变量

  • 过去有人试图将IIRC与记忆问题联系起来,但处理记忆问题并不愉快。(但这是在我们建立当前的外国体系之前。)


CMake可以自动找到
libpython
路径:

find_包(需要PythonLibs 3.3)

更详细的例子

然后可以使用一些链接的碎片库。 例如:

两件事:1。这将是一个图书馆。我更愿意将配置留给应用程序开发人员。2.我不知道这在过去有人尝试过。老实说,我认为在我做基准测试的时候,这将是一个有趣的项目。事实证明,之前的方法有点不同。他们编写了一个Python到Scheme编译器。看起来在那篇论文中,他们还没有对Python的C API做任何事情。我不记得论文中实际描述了什么,但我知道Daniel尝试了几种方法,其中一种涉及直接处理Python C级别。IIRC,方法的根本区别(Python是否只进行引用计数?)是真正的PITA,这使得整个过程非常困难。
#lang scheme

(require scheme/foreign)
(unsafe!)

(define libpython #f)

(define (link-python [lib "/usr/lib/libpython2.6.so"])
  (if libpython (error "Foo!") (set! libpython (ffi-lib lib))))

(define-syntax-rule (defpython <name> type)
  (define (<name> . args)
    (if libpython
      (begin (set! <name> (get-ffi-obj '<name> libpython <type>))
             (apply <name> args))
      (error '<name> "python not linked yet"))))

(defpython Py_Initialize (_fun -> _void))
(defpython Py_Foo (_fun _int _int -> _whatever))
...more...