为什么F#编译器会为一个案例而不是另一个案例给出错误?

时间:2014-03-08 21:04:42

标签: f#

我正在进行一个来自F#的平台调用调用,我收到一个编译器错误,我真的无法理解这一点。首先,让我展示我正在做的事情的C签名:

int Foo(
    ULONG_PTR *phHandle,
    DWORD flags
);

在F#中,我认为本地调用它的正确方法是:

[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
    (
        [<Out>]nativeint& phHandle,
        uint32 flags
    )

如果我尝试在类中调用此,则在调用它时会出现编译错误:

type Class1() = 
    [<DllImport("somedll.dll")>]
    static extern int APlatformInvokeCall
        (
            nativeint& phHandle,
            uint32 flags
        )

    member this.Foo() =
        let mutable thing = nativeint 0
        APlatformInvokeCall(&thing, 0u) |> ignore
        thing

错误是:

  

类型实例化涉及byref类型。 Common IL的规则不允许这样做。

奇怪的是,当我在一个模块中完成所有操作时,编译错误就会消失:

module Module1 = 
    [<DllImport("somedll.dll")>]
    extern int APlatformInvokeCall
        (
            nativeint& phHandle,
            uint32 flags
        )

    let Foo() =
        let mutable thing = nativeint 0
        APlatformInvokeCall(&thing, 0u) |> ignore
        thing

为什么要编译为模块,而不是作为类编译?

3 个答案:

答案 0 :(得分:2)

我不认为在F#中的类中定义extern方法是有效的。

如果您调高F# 3.0 language specification并搜索DllImport,则底部附近会列出一些特殊属性以及如何使用它们。 [<DllImport>]的文字说:

当应用于模块中的函数定义时,会导致F#编译器忽略定义的实现,而是将其编译为CLI P / Invoke存根声明。

这似乎表明它只对在模块中定义的函数声明extern方法(使用[<DllImport>])有效;但它并没有对班级成员说些什么。

我认为您正在遇到编译器错误。请将此代码提交给fsbugs@microsoft.com,以便他们可以修复编译器发出的错误消息 - 它确实会给您一个关于在类中定义extern方法的错误,因为它不是语言规范允许。

答案 1 :(得分:2)

这是否是一个不能承受的错误,也许这就是正在发生的事情:如果APlatformInvokeCall被认为是静态成员函数,那么该成员只有一个元组类型的参数。元组被编译为泛型类型的对象(请参阅底部的herethe spec中的5.1.3)。在这种情况下,元组是

System.Tuple<nativeint&, uint32>

但是ECMA 335 II.9.4说你不能在byref类型实例化泛型类型。这解释了报告的错误。

这个解释符合上面提到的事实Class1如果你修改extern声明并调用取而代之的是一个参数,那么(好吧,编译)。它也符合模块版本的工作原理,因为在该版本中没有考虑APlatFormInvokeCall成员函数。

答案 2 :(得分:1)

简单的解决方案是检查规范,这里是类定义语法:

type type-name pat_opt as-defn)opt =
    class
        class-inherits-decl_opt
        class-function-or-value-defns_opt
        type-defn-elements
    end

然后我们

class-function-or-value-defn :
      attributes_opt staticopt let rec_opt function-or-value-defns
      attributes_opt staticopt do expr

不允许使用extern。

type-defn-element :
      member-defn
      interface-impl
      interface-signature

这也不是你想要的。

因此,我们可以看到在您尝试使用它时使用extern无法在类中完成。