在Racket中,可以在调用另一个函数后导出函数吗? [英] In Racket, can I export functions after another function has been called?

查看:96
本文介绍了在Racket中,可以在调用另一个函数后导出函数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用scheme的FFI创建到libpython的绑定.为此,我必须获取python的位置,创建ffi-lib,然后从中创建函数.因此,例如,我可以这样做:

I'm trying to create a binding to libpython using scheme's FFI. To do this, I have to get the location of python, create the ffi-lib, and then create functions from it. So for instance I could do this:

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

  (define (link-python [lib "/usr/lib/libpython2.6.so"])
    (ffi-lib lib))

这一切都很好,但是我想不出一种导出函数的方法.例如,我可以做这样的事情:

This is all well and good, but I can't think of a way to export functions. For instance, I could do something like this:

(define Py_Initialize (get-ffi-obj "Py_Initialize" libpython (_fun -> _void)))

...但是然后我必须以某种方式全局存储对libpython(由link-python创建)的引用.一旦调用link-python,有什么方法可以导出这些函数?换句话说,我希望使用该模块的人能够做到这一点:

...but then I'd have to store a reference to libpython (created by link-python) globally somehow. Is there any way to export these functions once link-python is called? In other words, I'd like someone using the module to be able to do this:

(require pyscheme)
(link-python)
(Py_Initialize)

...或者这个:

(require pyscheme)
(link-python "/weird/location/for/libpython.so")
(Py_Initialize)

...但是有这个错误:

...but have this give an error:

(require pyscheme)
(Py_Initialize)

我该怎么做?

推荐答案

执行此类操作的最简单方法可能是将绑定延迟到需要它们时才进行.像这样(未经测试)的代码:

Probably the easiest way to do something like this is to delay the binding until they're needed. Something like this (untested) code:

#lang scheme

(require scheme/foreign)
(unsafe!)

(define libpython #f)

(define (link-python [lib "/usr/lib/libpython2.6.so"])
  (if libpython
    (error "Foo!")
    (begin (set! libpython (ffi-lib lib))
           (set! Py_Initialize
                 (get-ffi-obj "Py_Initialize" libpython
                              (_fun -> _void))))))

(define (Py_Initialize . args)
  (error 'Py_Initialize "python not linked yet"))

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

Ir you can do the setting inside the function itself, so you don't bind functions that are never called:

#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")))

并且由于您不想为每个函数都执行此操作,因此应将其包装在宏中:

and since you won't want to do this for every single function, you should wrap it in a macro:

#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...

但有两个高级注释:

  • 即使有可能,以这种方式延迟执行也是很丑陋的.我宁愿使用一些在代码启动时就已知的环境变量.

  • Even though it's possible, it seems ugly to delay things this way. I'd rather use some environment variable that is known when the code starts.

过去曾尝试链接 python 和IIRC的plt方案处理内存问题并不令人满意. (但这是在我们没有当前的外国系统之前.)

There have been an attempt in the past to link plt scheme to python, and IIRC, dealing with memory issues was not pleasant. (But this is before we had the current foreign system in place.)

这篇关于在Racket中,可以在调用另一个函数后导出函数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆