
Windowsでは通るのにLinuxで落ちる原因は「型の違い」だった?Zig+SDL3+ImGui+ImPlotのundefined symbol解決ガイド
WindowsではSDL3+ImGui+ImPlotのC++プロジェクトが普通にビルドできるのに、Linuxだけリンク時に ld.lld: undefined symbol で落ちる――この手のトラブルは、ソースやライブラリの不整合というより「テンプレートと型サイズの差」で説明がつくケースがよくあります。
今回のエラーはまさにそれで、原因を理解すると最短で直せます。
起きていること:Linuxだけ ImPlot::PlotBars<unsigned long> が見つからない
ログの要点はこれです。
-
Linuxでリンク時に未定義参照(undefined symbol)
-
未定義なのは
void ImPlot::PlotBars<unsigned long>(...) -
しかも「似た候補」として
PlotBars<signed char>が提示され、implot_items.oに定義があると言われている
つまり「ImPlot自体はリンクできているが、unsigned long 用の PlotBars 実体がどこにも生成されていない」状態です。
C++のテンプレートは、定義がヘッダ内にあるか、あるいは .cpp側で明示的インスタンス化しているかでリンクの挙動が変わります。このエラーは後者パターンで起きやすい典型例です。
いちばん多い真因:WindowsとLinuxで unsigned long の意味が違う
ここが最大の落とし穴です。
-
Windows(多くの環境は LLP64):
unsigned longは 32bit -
Linux(一般的な x86_64 は LP64):
unsigned longは 64bit
同じ unsigned long と書いても、OS/ABIが違うと別物になり得ます。
ImPlot側が「ある型の組み合わせだけ」を .cpp で明示的にインスタンス化している(または内部でサポート型が限定されている)場合、Windowsでは偶然 “32bit枠” に収まって通っても、Linuxでは “64bit型” になって未対応=未生成になり、リンクで落ちます。
今回、未定義なのが PlotBars<unsigned long> であることは、まさにこの状況を強く示唆します。
解決策A:固定幅整数に置き換える(最短・安全・おすすめ)
クロスプラットフォームで型を安定させるなら、unsigned long を捨てて uint32_t / uint64_t に寄せるのが正攻法です。
さらに、ImPlotに渡す型は「ライブラリが確実に扱える型」に寄せるのが無難です。
例:元が unsigned long* の配列なら、まず描画用に double や int に変換するのが堅いです。
#include <cstdint>
#include <vector>
// 例:counts が本来 unsigned long だったもの
std::vector<double> plot_values;
plot_values.reserve(count);
for (int i = 0; i < count; i++) {
plot_values.push_back(static_cast<double>(counts[i]));
}
ImPlot::PlotBars("counts", plot_values.data(), count);
値が大きく double 変換が嫌なら、まずは uint32_t へ整理できないか検討してください(多くのヒストグラム・カウント用途は32bitで足ります)。
std::vector<uint32_t> v32;
v32.reserve(count);
for (int i = 0; i < count; i++) v32.push_back(static_cast<uint32_t>(counts[i]));
// ImPlot側が uint32_t を確実に持っているかは実装次第なので、最終的に double へ寄せるのが安全
ポイントは「unsigned long をAPI境界に持ち込まない」ことです。WindowsとLinuxで意味がズレる型を使うと、今回のように“片方だけ”落ちる温床になります。
解決策B:ImPlot側で unsigned long の明示的インスタンス化を追加する(根治だが保守コストあり)
もしプロジェクト都合でどうしても unsigned long をそのまま PlotBars に渡したいなら、ImPlotの実装に合わせて unsigned long 用の実体を生成させる必要があります。
よくある構造は次のどちらかです。
-
テンプレート定義がヘッダに無い(.cppにある)
-
.cpp末尾で「対応型だけ」明示的インスタンス化している
この場合、implot_items.cpp などの末尾付近にある“対応型リスト”に unsigned long を足す、あるいは同様の記述を追加します(実際のマクロ名・書式はImPlotの該当ファイルに合わせてください)。
ただしこの方法は、ImPlot更新時に衝突しやすく、チーム開発では差分管理が面倒になりがちです。
長期的には解決策A(固定幅型+描画用型への変換)の方がトラブルが減ります。
解決策C:ビルドの一貫性をチェック(「本当は別物をリンクしていた」を潰す)
今回のログだと implot_items.o 自体は作られていて参照もされているので、完全な未リンクではなさそうですが、念のため次も確認すると事故を防げます。
-
ImPlot/ImGuiの全ソースが同じC++モードでコンパイルされているか(例:C++17など)
-
Linux側だけ
#defineが違っていないか(IMGUI_DISABLE_*やIMPLOT_*系) -
ある翻訳単位だけ
-fno-rtti-fno-exceptionsなど差が出ていないか -
implot_items.cppを別設定でビルドしていないか(Zig経由だとフラグ差が混じることがある)
ただ、今回の未定義シンボル名が “特定型のテンプレート” にピンポイントで出ているため、ビルド不整合よりも「型のズレ(unsigned long)」が主犯である可能性が高いです。
まとめ:Linuxだけ落ちるなら、まず unsigned long を疑う
-
unsigned longはWindowsとLinuxでビット幅が変わりやすい -
テンプレート関数が「対応型だけ実体化」される構造だと、片OSだけ未定義参照が起きる
-
最短で強い対処は「固定幅型に寄せる」「ImPlotに渡す前に
double等へ変換する」 -
どうしても必要ならImPlot側で明示的インスタンス化を追加(ただし保守コスト増)
同じコードがWindowsで動くほどややこしいのがこの問題ですが、原因はシンプルです。API境界の型を安定させれば、Linuxでも一気に通るようになります。