我为 GLFW 创建了一些 F# 绑定。为此,有几个回调。现在,除了回调定义(这是一种委托类型)、更惯用的 F# 回调函数、这些函数类型的参数以及将回调函数编组到 GLFW 的实际绑定之外,我设置的所有回调包装器基本上都是相同的.
这里有两个绑定和回调委托:
module Windowing.GLFW.Native.Window
open System.Runtime.InteropServices
[<Struct>]
type GLFWwindow = struct end
type GLFWwindowsizefun = delegate of window: nativeptr<GLFWwindow> * width_height: (int * int) -> unit
type GLFWwindowfocusfun = delegate of window: nativeptr<GLFWwindow> * focused: int -> unit
[<DllImport(glfwDLLPath, CallingConvention = CallingConvention.Cdecl)>]
extern GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback)
/// Sets the focus callback for the specified window.
[<DllImport(glfwDLLPath, CallingConvention = CallingConvention.Cdecl)>]
extern GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback
然后我有高级包装器,旨在隐藏互操作细节:
module Windowing.GLFW.FSharp
open Windowing.GLFW.Native
open FSharp.NativeInterop
open System.Runtime.InteropServices
type GLFWWindow = GLFWWindow of nativeptr<Window.GLFWwindow>
let setWindowSizeCallback (callbackRef: outref<Window.GLFWwindowsizefun>)
(callback: GLFWWindow * int * int -> unit)
(GLFWWindow window) =
let callback = Window.GLFWwindowsizefun(fun window (width, height) -> callback(GLFWWindow window, width, height))
callbackRef <- callback
Window.glfwSetWindowSizeCallback(window, callback) |> ignore
let setWindowFocusCallback (callbackRef: outref<Window.GLFWwindowfocusfun>)
(callback: GLFWWindow * int -> unit)
(GLFWWindow window) =
let callback = Window.GLFWwindowfocusfun(fun window focused -> callback(GLFWWindow window, focused))
callbackRef <- callback
Window.glfwSetWindowFocusCallback(window, callback) |> ignore
如您所见,这些包装函数实际上是相同的。要创建一个新的,我只需复制一个,更改函数委托类型、回调类型定义和 GLFW 绑定。
我想要的是创建一个处理所有这些机制的通用函数,然后定义实际的包装器,我只需传入相关的类型值。但是,我无法让它与通用委托约束一起工作。
例如,我尝试过这个:
let setGenericCallback (callbackDef: 'GLFWcallback when 'GLFWcallback: delegate<nativeptr<Window.GLFWwindow> * 'Args, unit>)
(setCallbackFun: nativeptr<Window.GLFWwindow> * 'GLFWcallback -> 'GLFWcallback)
(callback: GLFWWindow * 'Args -> unit)
(callbackRef: outref<'GLFWcallback>)
(GLFWWindow window) =
let callback = callbackDef(fun window args -> callback(GLFWWindow window, args))
callbackRef <- callback
setCallbackFun(window, callback) |> ignore
但是在
let callback = callbackDef ...
行上,我收到错误“该值不是函数,无法应用”。如果我尝试 callbackDef.Invoke
,我会收到错误“查找不确定类型的对象”,即使它应该是委托。同样奇怪的是,我无法用 'GLFWcallback
静态约束 ^GLFWcallback
。看来 outref<type>
不能采用 type
的静态约束类型。
我提到的两个资源是:
如果我有这样一个通用函数,那么我可以将其重新定义为:
let setWindowSizeCallback callbackRef callback window =
setGenericCallback Window.GLFWwindowposfun
Window.glfwSetWindowPosCallback
callback
callbackRef
window
所以,我被困住了。我收到的错误对我来说没有意义,也没有告诉我真正的问题是什么。委托约束似乎没有正常工作或者我没有正确使用它。
如何让它发挥作用?如何正确定义这样的函数以允许我使用通用委托?请注意,我的代表并不那么通用。他们都是
delegate of nativeptr<GLFWwindow> * 'T -> unit
类型。
您可以将类型化委托转换为非类型化委托,然后动态调用它。我也不确定对性能的影响是什么 - 我怀疑它可能会更慢,但我还没有尝试过。
我无法运行它,但进行了以下类型检查(但我做了一些编辑,因为我对
GLFWindow
和 GLFwindow
之间的差异感到困惑):
let inline setGenericCallback
(callbackDef: 'GLFWcallback when 'GLFWcallback: delegate<nativeptr<GLFWwindow> * 'Args, unit>)
(callback: GLFWWindow * (int * int) -> unit) (GLFWWindow window) =
let callback =
(callbackDef :> System.Delegate).DynamicInvoke
[| fun window args -> callback(GLFWWindow window, args) |]
()
也就是说,我同意其他评论者的说法——接受一些代码重复只是更容易。这对于基本上是手动自动生成的绑定的代码尤其有意义。
遇到同样的问题后,进一步的研究最终发现了这个问题以及以下规范:
5.2.8 委托约束 显式委托约束具有以下形式:
typar : delegate<tupled-arg-type, return-type>
在约束求解期间(第 14.5 节),如果
是具有声明constraint type : delegate<tupled-arg-type, return-types>
和type
的委托类型D
,则满足type D = delegate of object * arg1 * ... * argN
。也就是说,委托必须匹配 CLI 设计模式,其中tupled-arg-type = arg1 * ... * argN
对象是事件的第一个参数。sender
注意:此约束形式的存在主要是为了允许定义与事件编程相关的某些 F# 库函数。它很少直接在 F# 编程中使用。
约束并不暗示任何有关子类型的信息。特别是,一个 “委托”约束并不意味着该类型是delegate
的子类型。System.Delegate
约束仅适用于遵循 CLI 事件处理程序通常形式的委托类型,其中第一个参数是“发送者”对象。原因是该约束的目的是简化向 F# 程序员呈现 CLI 事件处理程序。delegate
所以基本上,
delegate