読者です 読者をやめる 読者になる 読者になる

C# で、C の共用体を実現するには?

おくればせながら、あけましておめでとうございます。今年もよろしくおねがいします。

今日、SDL.net を github にうpした。
SDL.net は SDLC# binding である。
これを書くときにつまづいたことなどを書いてみる。

まずは SDL_event.h からコピペしたコード片を見て欲しい。

typedef union SDL_Event {
    Uint8 type;
    SDL_ActiveEvent active;
    SDL_KeyboardEvent key;
    SDL_MouseMotionEvent motion;
    SDL_MouseButtonEvent button;
    SDL_JoyAxisEvent jaxis;
    SDL_JoyBallEvent jball;
    SDL_JoyHatEvent jhat;
    SDL_JoyButtonEvent jbutton;
    SDL_ResizeEvent resize;
    SDL_ExposeEvent expose;
    SDL_QuitEvent quit;
    SDL_UserEvent user;
    SDL_SysWMEvent syswm;
} SDL_Event;

お気づきだろうか?
SDL_Event は union、共用体である。
型を定義する方法として、C# には class と struct、enum しかないのである。
これはまずい。私の動物的直感がそう告げている。
P/Invoke(プラットフォーム呼び出し)、早い話が C# から C 言語で書かれた関数を呼び出す時に、
構造体の中身が違うために サッカーボールが裏返しになって帰ってきてしまうようなおかしなことが起こったりしてしまうかもしれないのだ*1

ググったところ、StructLayoutAttributeLayoutKind.Explicit を付けた上で、FieldOffsetAttribute でメンバーの順番を指定する必要があるようだ。
多分大丈夫な SDL_Event がこちら。

[StructLayout(LayoutKind.Explicit)]
public /*union*/struct SDL_Event {
    [FieldOffset(0)]
    public byte type;
    [FieldOffset(0)]
    public SDL_ActiveEvent active;
    [FieldOffset(0)]
    public SDL_KeyboardEvent key;
    [FieldOffset(0)]
    public SDL_MouseMotionEvent motion;
    [FieldOffset(0)]
    public SDL_MouseButtonEvent button;
    [FieldOffset(0)]
    public SDL_JoyAxisEvent jaxis;
    [FieldOffset(0)]
    public SDL_JoyBallEvent jball;
    [FieldOffset(0)]
    public SDL_JoyHatEvent jhat;
    [FieldOffset(0)]
    public SDL_JoyButtonEvent jbutton;
    [FieldOffset(0)]
    public SDL_ResizeEvent resize;
    [FieldOffset(0)]
    public SDL_ExposeEvent expose;
    [FieldOffset(0)]
    public SDL_QuitEvent quit;
    [FieldOffset(0)]
    public SDL_UserEvent user;
    [FieldOffset(0)]
    public SDL_SysWMEvent syswm;
};

はてなブログに先にうpしたんだけど、シンタックスハイライトがはてなブログではただのスーパー pre 記法になってしまうので嫌な感じ。
なんだかなー。

*1:実際にサッカーボールが裏返しになって帰ってくるわけじゃなくて、そーいう比喩