2011-01-11 3 views
2

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

Например:

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 явно должен позволить себе использовать маршала. OffsetOf() и Marshal.SizeOf() при написании пользовательского маршалера для этой структуры. Из того, что я видел, писать собственный маршалер - единственный способ справиться с профсоюзами. Если я ошибаюсь, ссылки будут очень признательны!

Ошибки я получаю при написании выше кода:

error FS0039: The type 'MyStruct' is not defined 

Я предполагаю, что это потому, что только различали тип союзов можно определить рекурсивно. Однако я не знаю другого способа представления этих структур в F #.

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

ответ

4

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

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

вместо

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

(и обратите внимание, что атрибуты могут прийти до или после того, как слово type, но только после слова and ...). Однако, если вы попробуете это, вы увидите, что вы просто получите другую ошибку, потому что структуры не могут быть непосредственно рекурсивными как поля друг друга. Если у структуры А было поле, которое было структурой B, а структура B имела поле, которое было структурой A (и любое из них имело любые другие поля), тогда размер был бы бесконечным. Обратите внимание, что это верно и для вашего кода на C. StructB содержит указатель до MyStruct, а не MyStruct. В .NET вы можете использовать для этого - в 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 
+0

спасибо! Так оно и было. Один вопрос: я никогда не видел конструкцию nativeptr <| insert unmanaged type here |>. У MSDN нет хорошего объяснения, и google-коды не дали никаких просветительских фрагментов. – arachnid

+0

@arachnid - 'nativeptr <'t>' - F # -специальный тип для работы с указателями. На самом деле это просто псевдоним для 'System.IntPtr', но модуль' NativeInterop.NativePtr' предоставляет несколько полезных функций для взаимодействия с этими указателями строго типизированным способом. – kvb

Смежные вопросы