{.emit: """
typedef struct {
    int id;
} Member;

typedef struct {
    Member m;
    unsigned int perm;
} Manager;

int member_size()
{
    return sizeof(Member);
}

int manager_size()
{
    return sizeof(Manager);
}
""".}

type
    Member {.importc, nodecl, pure, inheritable.} = ref object
        id: cint
    Manager {.importc, nodecl, pure.} = ref object of Member
        perm: uint

proc member_size(): cint {.importc, nodecl.}
proc manager_size(): cint {.importc, nodecl.}

var m = Member(id: 10)
var mngr = Manager(id: 20, perm: 0x1234)

說明如下

  • 在 nim 裡, object 的 root class 是 RootObj, 如果要產生新的 root, 就要用上 inheritable pragma
  • 要用 C 裡的東西, 要通過 importc pragma 來描述 type/proc 的長像讓 nim compiler 知道怎麼對應到 nim 中
  • nodecl 是告訴 nim compiler 不用從 binding 再產生 C 的宣告, 在 C 宣告是放在 header 裡的時候可配合 nodecl 以免產生出重覆的宣告.
    • nim 支持不需要安裝 *-dev 包的 binding 模式 - 也就是說只需要系統上的 .so 而不需要安裝開發包中的 .h, C 裡的宣告 nim 會自動從 binding 中生成
  • 這裡 sizeof(Member) 應該只有 4 byte 大小, 但 binding 時, 如果 binding 時如果只用了 importc 及 inheritable, 這時其實 nim 會認為 Member 這個 struct 中帶了一個保存 type (用做 runtime type information; RTTI) 的欄位, 造成實際的 Member 從 nim 的觀點來看大小變成了 16 byte, compile 時也可能因為沒法存取到 RTTI 欄位而發生錯誤
  • 如果加上 pure pragma 時, nim 就不會自動插入 RTTI 欄位, 這樣才能完整對映回 C
  • Member 的子類 (Manager) 如果也是 importc 進來的, 那 nim 就不會加上 RTTI 欄位 (因為 parent 是 pure 的關係)
  • 如果子類不是 import 進來的, 就算是 pure type (Member) 的子類, nim 還是會插入 RTTI 欄位, 不過, 因為這種 type 是 nim 中去延伸 C 的, 新增欄位的話其實不需要考慮到跟 C 的兼容, 所以是安全且能 compile 過的