Skip to content

C bitfields are inherently non-interoperable #103

@ZimM-LostPolygon

Description

@ZimM-LostPolygon

Dear ImGui uses C bit fields in some structs. While most of these fields are in imgui_internal.h and/or not particularly useful, this does technically mean the layout of those structs is completely compiler and/or platform defined:

  • Different compilers might pack bitfields differently.
  • Depending on platform endianness, compiler might pack bitfields in the opposite order.
  • Pretty sure padding between bitfields is undefined.
  • Don't get me started on alignment either.

From https://en.cppreference.com/w/cpp/language/bit_field.html:

The following properties of bit-fields are implementation-defined:

...

  • Everything about the actual allocation details of bit-fields within the class object.
    • For example, on some platforms, bit-fields don't straddle bytes, on others they do.
    • Also, on some platforms, bit-fields are packed left-to-right, on others right-to-left.

GCC just goes "Determined by ABI" for pretty much everything:

https://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit-fields-implementation.html

What this means is that those structs technically can't be represented in any other language other than C/C++. Yes, dear_bindings can understand bitfields and output them as part of metadata, but what's next? How would that metadata be used? If one were to, say, write a C# wrapper using dear_bindings, they'd have to reverse engineer and then replicate the actual memory layout of the data inside the struct, and even then, that would still be dependent on the compiler that built the native library. So either way, if a struct includes bitfields, it's completely non-interoperable with any other language. Annoyingly, even if one doesn't intend to ever touch those specific fields, they change how other fields are laid out in memory, including those of actual interest.

I've been thinking about this for quite a while, and other than going "YOLO, in real world all compilers would probably use the same layout", I can't think of any easy way to handle this. Here's some ideas:

  • If a struct has any bitfields, it must be treated as opaque pointer, and any and all access to the fields is done through get/set accessors that dear_bindings will generate. Pretty terrible tbh, and introduces a whole new paradigm because of something pretty much no one will ever need.
  • Remove bit size specifiers from dear imgui code itself, effectively converting them to regular fields. Very easy to do, but probably won't happen due to significantly increased memory usage.
  • In dear imgui code, rework all bitfields to be just regular fields, and then do all the bit manipulations in dear imgui code manually. This would make the memory layout consistent and interopeable while keeping the memory savings, but would probably make that code quite messy...
**All bitfields as of 1.92.x**

(public)

ImFontBaked.MetricsTotalSurface:26 (uint)
ImFontBaked.WantDestroy:1 (uint)
ImFontBaked.LockLoadingFallback:1 (uint)

ImFontGlyph.Colored:1 (uint)
ImFontGlyph.Visible:1 (uint)
ImFontGlyph.SourceIdx:4 (uint)
ImFontGlyph.Codepoint:26 (uint)

(internal)

ImGuiTableColumn.SortDirection:2 (ImU8)
ImGuiTableColumn.SortDirectionsAvailCount:2 (ImU8)
ImGuiTableColumn.SortDirectionsAvailMask:4 (ImU8)

ImGuiStyleVarInfo.Count:8 (ImU32)
ImGuiStyleVarInfo.DataType:8 (ImGuiDataType)
ImGuiStyleVarInfo.Offset:16 (ImU32)

ImGuiWindow.SetWindowPosAllowFlags:8 (ImGuiCond)
ImGuiWindow.SetWindowSizeAllowFlags:8 (ImGuiCond)
ImGuiWindow.SetWindowCollapsedAllowFlags:8 (ImGuiCond)
ImGuiWindow.SetWindowDockAllowFlags:8 (ImGuiCond)
ImGuiWindow.DockIsActive:1 (bool)
ImGuiWindow.DockNodeIsVisible:1 (bool)
ImGuiWindow.DockTabIsVisible:1 (bool)
ImGuiWindow.DockTabWantClose:1 (bool)

ImGuiDockNode.AuthorityForPos:3 (ImGuiDataAuthority)
ImGuiDockNode.AuthorityForSize:3 (ImGuiDataAuthority)
ImGuiDockNode.AuthorityForViewport:3 (ImGuiDataAuthority)
ImGuiDockNode.IsVisible:1 (bool)
ImGuiDockNode.IsFocused:1 (bool)
ImGuiDockNode.IsBgDrawnThisFrame:1 (bool)
ImGuiDockNode.HasCloseButton:1 (bool)
ImGuiDockNode.HasWindowMenuButton:1 (bool)
ImGuiDockNode.HasCentralNodeChild:1 (bool)
ImGuiDockNode.WantCloseAll:1 (bool)
ImGuiDockNode.WantLockSizeOnce:1 (bool)
ImGuiDockNode.WantMouseMove:1 (bool)
ImGuiDockNode.WantHiddenTabBarUpdate:1 (bool)
ImGuiDockNode.WantHiddenTabBarToggle:1 (bool)

ImGuiTable.RowFlags:16 (ImGuiTableRowFlags)
ImGuiTable.LastRowFlags:16 (ImGuiTableRowFlags)

ImGuiStackLevelInfo.DataType:8 (ImGuiDataType)

ImGuiBoxSelectState.KeyMods:16 (ImGuiKeyChord)

ImGuiTableColumnSettings.SortDirection:2 (ImU8)
ImGuiTableColumnSettings.IsEnabled:2 (ImS8)
ImGuiTableColumnSettings.IsStretch:1 (ImU8)

ImFontAtlasRectEntry.TargetIndex:20 (int)
ImFontAtlasRectEntry.Generation:10 (int)
ImFontAtlasRectEntry.IsUsed:1 (uint)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions