以下の内容はhttps://error-daizenn.hatenablog.com/entry/2026/02/07/191128より取得しました。


Windowsでは通るのにLinuxでC++リンクエラーになる理由と解決策:ImPlot::PlotBars未定義シンボルの正体

 

Windowsでは通るのにLinuxでC++リンクエラーになる理由と解決策:ImPlot::PlotBars未定義シンボルの正体

WindowsでSDL3+Dear ImGui+ImPlotのプロジェクトが問題なくビルドできるのに、Linuxに持っていくとld.lld: undefined symbolで落ちる――この手の現象は「環境差」ではなく、ほぼ確実に“型”と“テンプレートの実体化”が原因です。とくに今回の ImPlot::PlotBars<unsigned long> が未定義になるケースは、再現性が高く、直し方もパターン化できます。この記事では、ログに出ている情報だけを手がかりに、なぜ起きるか/どう直すかを、手順としてまとめます。

ノイズ除去後の要点(何が起きているか)

エラーの核心はこれです。

  • Linuxでリンク時に ImPlot::PlotBars<unsigned long>(...) が見つからない

  • 代わりに ImPlot::PlotBars<signed char>(...) は定義されている と示される

  • implot_items.o に定義があることもログに出ているが、欲しい型の実体がない

つまり「ImPlotのPlotBarsテンプレート関数を unsigned long で呼んだが、その型の“実体(インスタンス化された定義)”がリンク対象に存在しない」という状態です。

なぜWindowsでは通ってLinuxで落ちるのか:unsigned long のサイズ差がトリガー

ここが最大の落とし穴です。

  • Windows(とくにMSVC系)では unsigned long はたいてい32bit

  • Linux(x86_64のGCC/Clang系)では unsigned long はたいてい64bit

同じソースで「unsigned long を使っている」つもりでも、実際には別サイズ・別ABIの別型として扱われます。テンプレートは「型が違えば別物」なので、Windowsで実体化されていた型(例:32bit相当)が、Linuxでは64bitになり、ImPlot側が用意している(または明示的に実体化している)型セットから外れて未定義になる、という流れが起きます。

ログに「did you mean: PlotBars<signed char>」と出るのも、ImPlot側が“いくつかの型だけ”明示的に用意していて、たまたまそこにsigned charが含まれている、という典型的な症状です。

仕組み:テンプレート定義が.cpp側にあると「使った型だけ」では自動生成されない

テンプレート関数は通常、ヘッダに定義があると呼び出し側で自動的に実体化されます。ところが、ライブラリ実装の都合で

  • 宣言はヘッダ

  • 定義(実装)は implot_items.cpp の中

という形になっていると、呼び出し側の翻訳単位では実体化されません。結果として、ImPlot側が“明示的に実体化した型”以外で呼ぶとリンクエラーになります。

今回まさにそれが起きていて、Linuxでunsigned long(64bit)を要求したのに、ImPlot側にその型の実体がない、ということです。

最短で直す:unsigned long をやめて固定幅整数にする(推奨)

一番堅くて移植性が高い解決はこれです。

  • 棒グラフ用の配列を uint32_t / uint64_t / int32_t / int64_t のような固定幅型に揃える

  • PlotBars 呼び出しもその型に合わせる(必要ならキャストではなく、データ側を変える)

例として、カウント値・インデックス値が本当に32bitで十分なら uint32_t に寄せると、Windows/Linuxで同じ型になります。

ポイント

  • unsigned long / size_t は環境依存になりやすい

  • 「Windowsで通る」は安全性の証明にならない(むしろ危険信号)

すぐ効く回避策:呼び出し側で型を変える(キャスト)

データ構造の変更が重い場合は、呼び出し箇所で型を“ImPlotが想定している型”に寄せます。

  • unsigned long 配列をそのまま渡さず、unsigned intImU64 などに変換したバッファを作って渡す

  • もしくは、表示用だけ double / float に変換して渡す(プロット用途なら十分なことが多い)

注意

  • キャストで無理やり型を合わせると、要素サイズがズレて壊れます(unsigned long*uint32_t* として渡すのはNG)

  • 必ず「変換した実体のバッファ」を用意するのが安全です

根本対応:ImPlot側で unsigned long の明示的インスタンス化を追加する

テンプレートが.cpp実装で、明示的インスタンス化の方式を採っている場合、ImPlot側に「この型でも作って」と指示するのが王道です。

  • implot_items.cpp の末尾付近にある“明示的インスタンス化”の並びに
    unsigned long(あるいは uint64_t 相当)を追加する

  • ただしこれは ライブラリ改変 になるため、将来の更新でコンフリクトしやすい

チーム開発やCIを考えるなら、推奨は「プロジェクト側で固定幅型へ統一」です。

Zigビルドで気をつける点:C++標準ライブラリとリンカの組み合わせ

ログを見る限り、Zigのビルドで-lc++ -lcを付けてld.lldでリンクしています。ここ自体が即原因とは限りませんが、Linuxでは

  • 使っているC++標準ライブラリ(libc++ / libstdc++)

  • ABI差

  • 最適化やLTOの有無

で症状の見え方が変わることがあります。今回のように「特定テンプレートだけ未定義」は、まず型差・実体化不足を疑うのが最短ルートです。C++ランタイムを入れ替える前に、unsigned long問題を潰すのが得策です。

チェックリスト(この順で潰すと早い)

  1. PlotBars に渡している配列の型が unsigned long / size_t になっていないか確認

  2. それを uint32_t / uint64_t など固定幅型に変更(推奨)

  3. 変更が難しければ、表示用に別バッファ(doubleなど)へ変換して渡す

  4. どうしてもunsigned longを使うなら、ImPlot側に明示的インスタンス化を追加(最終手段)

まとめ:Linuxで落ちるのは「環境差」ではなく「型とテンプレート」の必然

今回のリンクエラーは、ImPlotが悪いというより、C++のテンプレートと環境依存型の組み合わせが起こす定番の罠です。Windowsでの成功は「unsigned long がたまたま別の型に見えていただけ」という可能性が高く、Linuxで顕在化したのが今回の未定義シンボルです。

最も堅い解決は、unsigned longsize_t のままプロット関数に渡さず、固定幅型に統一すること。これだけで、Windows/Linux双方で再発しにくいコードになります。




以上の内容はhttps://error-daizenn.hatenablog.com/entry/2026/02/07/191128より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14