F# P/Invoke 编组递归结构

发布于 2024-10-11 23:41:40 字数 1397 浏览 8 评论 0原文

到目前为止,我见过的所有示例似乎都没有解决编组包含递归引用的结构联合的结构的问题。我正在尝试为包含这些的结构编写一个封送拆收器,但迄今为止失败了。

例如:

typedef enum {
    My_StructA = 0x7878,
    My_StructB
} MyStructTag;

 typedef struct _MyStruct MyStruct;  

 struct _MyStruct {  
     MyStructTag discriminator;  
     union {  
        struct {  
            int a;  
            int b;  
        } StructA;  

        struct {  
            int c;  
            MyStruct* d;  
        } StructB;
    } MyUnion;  
};

我尝试按如下方式定义结构:

type MyStructTag =
    | My_StructA = 0x7878
    | My_StructB = 0x7879

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructA =
    val mutable a : int
    val mutable b : int

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructB =
    val mutable c : int
    val mutable d : MyStruct

[<Struct; StructLayout(LayoutKind.Explicit)>]
type MyStruct =
    [<FieldOffset(0)>] val discriminator : MyStructTag
    [<FieldOffset(4)>] val structA : StructA
    [<FieldOffset(4)>] val structB : StructB

请注意,我费心显式定义 MyStruct 的原因是为了让自己在为此结构编写自定义封送拆收器时能够使用 Marshal.OffsetOf() 和 Marshal.SizeOf()。据我所知,编写自定义封送拆收器是处理联合的唯一方法。如果我错了,参考资料将不胜感激!

编写上述代码时收到的错误是:

error FS0039: The type 'MyStruct' is not defined

我认为这是因为只能递归定义可区分的联合类型。但是,我不知道有任何其他方法可以在 F# 中表示这些结构。

预先感谢您抽出时间。

None of the examples I have seen thus far appear to address the problem of marshaling a structure containing a union of structures that contain recursive references. I am attempting to write a marshaler for a structure which contains these and have so far failed.

For example:

typedef enum {
    My_StructA = 0x7878,
    My_StructB
} MyStructTag;

 typedef struct _MyStruct MyStruct;  

 struct _MyStruct {  
     MyStructTag discriminator;  
     union {  
        struct {  
            int a;  
            int b;  
        } StructA;  

        struct {  
            int c;  
            MyStruct* d;  
        } StructB;
    } MyUnion;  
};

I attempted to define the structures as follows:

type MyStructTag =
    | My_StructA = 0x7878
    | My_StructB = 0x7879

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructA =
    val mutable a : int
    val mutable b : int

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructB =
    val mutable c : int
    val mutable d : MyStruct

[<Struct; StructLayout(LayoutKind.Explicit)>]
type MyStruct =
    [<FieldOffset(0)>] val discriminator : MyStructTag
    [<FieldOffset(4)>] val structA : StructA
    [<FieldOffset(4)>] val structB : StructB

Note that the reason I bothered to define MyStruct explicitly is to allow myself to make use of Marshal.OffsetOf() and Marshal.SizeOf() when writing the custom marshaler for this structure. From what I have seen, writing a custom marshaler is the only way to handle unions. If I am wrong about that, references would be greatly appreciated!

The error I receive when writing the above code is:

error FS0039: The type 'MyStruct' is not defined

I assume this is because only discriminated union types can be defined recursively. However, I am not aware of any other way to represent these structures in F#.

Thank you in advance for your time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

惟欲睡 2024-10-18 23:41:40

你有两个问题。首先,任何相互递归类型(无论是可区分的联合、类还是结构)都需要使用

type A = ... 
and B = ...

而不是

type A = ... 
type B = ...

定义(并注意属性可以出现在单词 type 之前或之后,但只能在词...)。但是,如果您尝试这样做,您会发现您只是收到了不同的错误,因为结构不能作为彼此的字段直接递归。如果结构体 A 有一个结构体 B 的字段,并且结构体 B 有一个结构体 A 的字段(并且它们中的任何一个都有任何其他字段),那么大小将是无限的。请注意,您的 C 代码也是如此 - StructB 包含指向 MyStruct指针,而不是 MyStruct > 本身。在 .NET 中,您可以使用 IntPtr 来实现此目的 - 在 F# 中,您可以使用 nativeint 别名或 nativeptr。试试这个:

open System.Runtime.InteropServices

type MyStructTag =
| My_StructA = 0x7878
| My_StructB = 0x7879

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructA =
  val mutable a : int
  val mutable b : int

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructB =
  val mutable c : int
  val mutable d : nativeptr<MyStruct>

and [<Struct; StructLayout(LayoutKind.Explicit)>]MyStruct =
    [<FieldOffset(0)>] val discriminator : MyStructTag
    [<FieldOffset(4)>] val structA : StructA
    [<FieldOffset(4)>] val structB : StructB

You've got two problems. First of all, any mutually recursive types (whether discriminated unions, classes, or structs) need to be defined using

type A = ... 
and B = ...

rather than

type A = ... 
type B = ...

(and note that attributes can come before or after the word type, but only after the word and...). However, if you try this, you'll see that you just get a different error, because structs can't be directly recursive as fields of each other. If struct A had a field which was a struct B and struct B had a field which was a struct A (and either of them had any other fields), then the size would be infinite. Note that this is true of your C code as well - StructB contains a pointer to a MyStruct, not a MyStruct itself. In .NET, you can use an IntPtr for this - in F# you can use the nativeint alias or nativeptr<MyStruct>. Try this:

open System.Runtime.InteropServices

type MyStructTag =
| My_StructA = 0x7878
| My_StructB = 0x7879

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructA =
  val mutable a : int
  val mutable b : int

[<Struct; StructLayout(LayoutKind.Sequential)>]
type StructB =
  val mutable c : int
  val mutable d : nativeptr<MyStruct>

and [<Struct; StructLayout(LayoutKind.Explicit)>]MyStruct =
    [<FieldOffset(0)>] val discriminator : MyStructTag
    [<FieldOffset(4)>] val structA : StructA
    [<FieldOffset(4)>] val structB : StructB
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文