В ракетке я могу экспортировать функции после того, как была вызвана другая функция?
Вопрос
Я пытаюсь создать привязку к 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 (созданный link-python) глобально как-то. Есть ли способ экспортировать эти функции, когда называется Link-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"))
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...
Но два нота Handlevel:
Несмотря на то, что это возможно, кажется уродливым задержать вещи таким образом. Я предпочел бы использовать некоторую переменную среды, которая известна, когда код начинается.
Была попытка в прошлом ссылаться Схема 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)
Cmake может автоматически найти libpython
дорожка:
find_package( PythonLibs 3.3 REQUIRED )
Более подробный пример здесь.
Тогда вы можете использовать некоторые связанные Sherd lib. Например:https://github.com/niitsuma/pyjsonrpcembd