Я пытаюсь создать привязку к libpython использование FFI схемы. Чтобы сделать это, я должен получить местоположение Python, создать ffi-lib
, и затем создайте функции из него. Так, например, я мог сделать это:
(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 (созданный Python ссылки) глобально так или иначе. Там какой-либо путь состоит в том, чтобы экспортировать эти функции, после того как Python ссылки называют? Другими словами, я хотел бы, чтобы кто-то использующий модуль смог сделать это:
(require pyscheme)
(link-python)
(Py_Initialize)
... или это:
(require pyscheme)
(link-python "/weird/location/for/libpython.so")
(Py_Initialize)
... но имейте, это дает ошибку:
(require pyscheme)
(Py_Initialize)
Как я могу сделать это?
Вероятно, самый простой способ сделать что-то подобное - отложить привязку до тех пор, пока они не понадобятся. Примерно так (непроверенный) код:
#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"))
Если вы можете выполнить настройку внутри самой функции, вы не будете связывать функции, которые никогда не вызываются:
#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...
Но два важных замечания:
Хотя это возможно, откладывать вещи таким образом кажется некрасивым. Я бы предпочел использовать некоторую переменную среды, которая известна при запуске кода.
В прошлом были попытки связать схему plt с python , и IIRC, решение проблем с памятью было неприятным. (Но это было до того, как у нас была действующая иностранная система.)
Я сделал не так много модулей, но думаю, вам нужно «предоставить» функции, которые вы хотите загрузить в новое пространство имен.
(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)