我正在将OCaml Format模块转换为F#,并将问题追溯到使用OCaml Pervasives at_exit。
val at_exit : (unit -> unit) -> unit
在程序终止时注册要调用的给定函数。当程序正常执行退出或终止时,将调用at_exit
注册的函数,或者由于未被捕获例外。这些函数以“后进先出”顺序调用:最近添加at_exit
的函数首先被调用。
在转换过程中,我注释掉了这一行,因为编译器没有将它标记为需要,我没想到代码中有一个事件。
我使用VS对象浏览器检查了FSharp.PowerPack.Compatibility.PervasivesModule
at_exit
并找不到。
我找到了how to run code "at_exit"?和How do I write an exit handler for an F# application?
OCaml系列是
at_exit print_flush
使用print_flush签名:val print_flush : (unit -> unit)
另外,在OCaml代码的调试会话期间查看它的使用时,看起来在初始化结束时和每次使用模块调用结束时都会调用at_exit
。
有关如何执行此操作的任何建议和提示。这将是我在F#中的第一个活动。
编辑
以下是我所学到的关于格式模块的一些知识,可以解释这个问题。
Format模块是一个函数库,用于简单OCaml值的基本漂亮打印机命令,如int,bool,string。格式模块具有print_string
之类的命令,但也有一些命令可以将下一行放在有界框中,考虑新的左右边距集。所以可以写:
print_string "Hello"
或
open_box 0; print_string "<<";
open_box 0; print_string "p \/ q ==> r"; close_box();
print_string ">>"; close_box()
open_box
和print_string
等命令由循环处理,该循环解释命令,然后决定在当前行上打印或前进到下一行。命令保存在队列中,并且有一个状态记录来保存可变值,例如左边距和右边距。
需要启动队列和状态,从调试测试用例到工作的OCaml代码似乎是在模块初始化结束时但在第一次调用Format模块中的任何函数之前完成的。通过使用at_exit
机制来清除和再次为下一组命令启动队列和状态,这些机制识别出对格式模块的初始调用的最后一个匹配帧已被删除,从而触发对at_exit
,它会推出队列中剩余的任何命令并重新初始化队列和状态。
因此,调用print_flush
的顺序非常重要,似乎不仅仅是OCaml文档所说的内容。
答案 0 :(得分:2)
这应该这样做:
module Pervasives =
open System
open System.Threading
//
let mutable private exitFunctions : (unit -> unit) list = List.empty
//
let mutable private exitFunctionsExecutedFlag = 0
//
let private tryExecuteExitFunctions _ =
if Interlocked.CompareExchange (&exitFunctionsExecutedFlag, 1, 0) = 0 then
// Run the exit functions in last-in-first-out order.
exitFunctions
|> List.iter (fun f -> f ())
// Register handlers for events which fire when the process exits cleanly
// or due to an exception being thrown.
do
AppDomain.CurrentDomain.ProcessExit.Add tryExecuteExitFunctions
AppDomain.CurrentDomain.UnhandledException.Add tryExecuteExitFunctions
//
let at_exit f =
// TODO : This function should be re-written using atomic operations
// for thread-safety!
exitFunctions <- f :: exitFunctions
还有一些代码来测试它:
open System
// Register a couple of handlers to test our code.
Pervasives.at_exit <| fun () ->
Console.WriteLine "The first registered function has fired!"
Pervasives.at_exit <| fun () ->
Console.WriteLine "The second registered function has fired!"
TimeSpan.FromSeconds 1.0
|> System.Threading.Thread.Sleep
Console.WriteLine "Exiting the second registered function!"
Pervasives.at_exit <| fun () ->
Console.WriteLine "The third registered function has fired!"
// Do some stuff in our program
printfn "blah"
printfn "foo"
printfn "bar"
(* The functions we registered with at_exit should be fired here. *)
// Uncomment this to see that our handlers work even when the
// program crashes due to an unhandled exception.
//failwith "Uh oh!"