Запуск кода только один раз в библиотеке 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 это то, что нужно искать ". Как объяснено в этом документе, оно действительно устарело и теперь должно выполняться через атрибуты.

Другие вопросы по тегам