开发者

F# P/Invoke Marshaling Recursive Structures

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.Of开发者_如何学PythonfsetOf() 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.


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜