Как написать макрос схемы, который определяет переменную, а также получает имя этой переменной в виде строки?

В основном это продолжение этот вопрос. Я решил не забывать о YAGNI и создал глобальную переменную (libpython). Сначала я установил его на #f, затем set!, когда вызывается init. Я добавил функцию, которая должна проверять, инициализировано ли это значение:

  (define (get-cpyfunc name type)
    (lambda args
      (if libpython
        (apply (get-ffi-obj name libpython type) args)
        (error "Call init before using any Python C functions"))))

Итак, теперь вот что я хочу сделать. Я хочу определить макрос, который будет выполнять следующие действия:

(define-cpyfunc Py_Initialize (_fun -> _void))

И преобразовать его в это:

(define Py_Initialize (get-cpyfunc "Py_Initialize" (_fun -> _void)))

Я читал документацию по макросам, чтобы попытаться понять это, но я не могу найти способ заставить это работать. Может ли кто-нибудь помочь мне с этим (или хотя бы дать мне общее представление о том, как будет выглядеть макрос)? Или есть способ сделать это без макросов?


person Jason Baker    schedule 28.05.2010    source источник


Ответы (3)


Я ответил на большую часть этого вопроса в другом (я не видел этого). Можно использовать функцию, которая извлекает такие привязки, но одна из возможных проблем здесь заключается в том, что, поскольку вы создаете привязку только при вызове результирующей функции, эта привязка создается заново при каждом вызове. Простой способ быстро решить эту проблему — использовать промисы, что-то вроде этого:

(require scheme/promise)
(define (get-cpyfunc name type)
  (define the-function
    (delay (if libpython
             (get-ffi-obj name libpython type)
             (error "Call init before using any Python C functions"))))
  (lambda args (apply (force the-function) args)))

Но по сути это почти то же самое, что код, который я разместил в вашем предыдущем вопросе.

Еще случайные заметки:

  • get-ffi-obj примет символ в качестве имени для привязки - это сделано намеренно, чтобы упростить такие макросы (как в последнем вопросе).

  • Использование (symbol->string 'name) в макросе — это нормально. Как я отметил выше в своем ответе на комментарий Натана, это означает, что он вызывается во время выполнения, но mzscheme в любом случае должна иметь возможность оптимизировать это, поэтому нет необходимости пытаться написать какой-то сложный макрос, который выполняет работу во время компиляции.

  • Загляните в каталог PLT — вы найдете коллекцию под названием ffi. Это коллекция примеров переплетов с различными стилями. Макросы, создающие привязки, очень часто встречаются в этих примерах.

person Eli Barzilay    schedule 28.05.2010
comment
Спасибо за указание на примеры! Я просто подумал, что хотел бы иметь больше примеров использования ffi. - person Jason Baker; 29.05.2010

Почему бы вам не изменить сгенерированный код на

(define Py_Initialize (get-cpyfunc 'Py_Initialize (_fun -> _void)))

а затем запустить get-cpyfunc (symbol->string name)?

Конечно, возможно, есть способ сделать это с помощью syntax-case (хотя я никак не могу вспомнить его синтаксис), и определенно, если вы используете схему с CL-esque define-macro.

person Nathan Shively-Sanders    schedule 28.05.2010
comment
Я полагаю, это один из способов сделать это. Не думал об этом. - person Jason Baker; 28.05.2010

Это не полный ответ, но я придумал макрос, который отвечает обоим требованиям (определяет переменную и строку с именем этой переменной):

> (define-syntax (my-syntax stx)
  (syntax-case stx ()
  [(_ id)
  #'(define-values (id) (values (symbol->string (quote id))))]))
> (my-syntax y)
> y
"y"
person Jason Baker    schedule 28.05.2010
comment
Я не могу вспомнить syntax-case достаточно хорошо — symbol-string запускается во время макрорасширения или во время выполнения? - person Nathan Shively-Sanders; 28.05.2010
comment
Не здесь, потому что symbol->string является частью сгенерированного кода (он находится внутри шаблона). - person Eli Barzilay; 28.05.2010