Запуск кода только один раз в библиотеке OCaml
Я пишу библиотеку OCaml, в которой есть некоторый код инициализации, который нужно запускать только один раз за время существования программы с использованием библиотеки (и хранить некоторое состояние, которое будет сохраняться в течение всего срока действия программы, но использоваться только внутри самой библиотеки).) и некоторый код очистки, который нужно запускать только при выходе из программы, использующей библиотеку.
Если это уместно, моя библиотека состоит из двух частей: интерфейса с низкоуровневой библиотекой C и некоторых высокоуровневых вещей, облегчающих программирование. Могу ли я сделать то, что мне нужно где-то в C? В идеале моим пользователям было бы все равно, как это реализовано, они никогда не увидят биты Си.
В Python я бы сделал это, запустив код на import
но OCaml's open
на самом деле ничего не запускается, он просто подслащивает пространство имен модуля, а затем Python atexit
, но я не могу найти эквивалент Ocaml.
Один из подходов, который я рассмотрел, - это структурирование моей библиотеки как "фреймворка", но я не думаю, что это достаточно важно, чтобы оправдать такой чрезмерно сложный подход. Спасибо!
ОБНОВЛЕНИЕ: хорошо понял - я думаю. Я использую код C для очистки при выходе, и я немного перебираю код, поэтому на стороне C есть указатель на глобальное состояние
Похоже, что в моей библиотеке у меня теперь есть
let global_env = env_create ()
И когда это open
с помощью основной программы, она запускается... Но как?
2 ответа
Обратите внимание, что это можно сделать на стороне OCaml с помощью Pervasives.at_exit
и операторы верхнего уровня для создания среды и установки кода очистки:
let env = init ()
let cleanup () = do_clean env
let () = at_exit cleanup
let f x = f_stub env x
Операторы верхнего уровня выполняются, когда модуль загружается (независимо от того, используете ли вы его в конечном итоге) и модули загружаются в порядке, указанном вами во время соединения (таким образом, модули в зависимости от других гарантируют, что их зависимости будут инициализированы, когда наступит их очередь) см. "Аргументы, оканчивающиеся на.cmo" в руководстве ocamlc. Это влечет за собой выполнение операторов верхнего уровня перед тем, как вы попытаетесь получить доступ к модулю. Это не вопрос открытия модуля, open
это просто (плохое) синтаксическое удобство.
Если вы хотите, чтобы код инициализации выполнялся тогда и только тогда, когда функция модуля в конце концов вызывается, используйте ленивое значение:
let env = lazy (init ())
let cleanup () = if Lazy.lazy_is_val env then (do_clean env) else ()
let () = at_exit cleanup
let f x = f_stub (Lazy.force env) x
Btw. не забудьте документировать возникающие проблемы с безопасностью потоков...
Как только let x = function ...
определяет функцию x
который доступен с этого момента, ваш let global_env = ...
определяет значение global_env
то есть. Если вам не нужно возвращаемое значение env_create
потому что вы запускаете его только для его побочных эффектов, вы могли бы также просто упомянуть env_create ()
в конце (если честно, где угодно) файла ml. В этом случае я бы сделал let _ = env_create ()
хотя, что я думаю, является более явным.
РЕДАКТИРОВАТЬ: R указал, что следующее неправильно:
"Чтобы сделать это чисто в C, я думаю, что _init
а также _fini
это то, что нужно искать ". Как объяснено в этом документе, оно действительно устарело и теперь должно выполняться через атрибуты.