F # P / Вызвать маршалинг рекурсивных структур - PullRequest
2 голосов
/ 11 января 2011

Ни один из примеров, которые я видел до сих пор, кажется, не рассматривал проблему маршалинга структуры, содержащей объединение структур, которые содержат рекурсивные ссылки.Я пытаюсь написать маршалер для структуры, которая содержит их и пока что не удалась.

Например:

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 #.

Заранее благодарим вас за ваше время.

1 Ответ

4 голосов
/ 11 января 2011

У тебя две проблемы. Прежде всего, любые взаимно рекурсивные типы (будь то различимые объединения, классы или структуры) должны быть определены с использованием

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

вместо

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

(и обратите внимание, что атрибуты могут идти до или после слова type, но только после слова and ...). Однако, если вы попробуете это, вы увидите, что вы просто получите другую ошибку, потому что структуры не могут быть напрямую рекурсивными как поля друг друга. Если бы структура A имела поле, которое было структурой B, а структура B имела поле, которое было структурой A (и у любого из них были другие поля), то размер был бы бесконечным. Обратите внимание, что это верно и для вашего кода C - StructB содержит указатель на MyStruct, а не MyStruct сам по себе. В .NET вы можете использовать IntPtr для этого - в F # вы можете использовать nativeint псевдоним или nativeptr<MyStruct>. Попробуйте это:

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
...