大した問題ではないのだが、私の観測範囲でこの問題によく遭遇・目撃する。

ライブラリ A, B, C があり、B と C は A を利用している。 A は static ライブラリで B と C は共有ライブラリである。 この場合、B と C の .dll や .so ファイルの中に A が含まれるため、 A の static variable が2重になってしまう。

解決方法は以下のどれか、

  • ライブラリ A を共有ライブラリにする
  • 共有ライブラリ B から A のシンボルも export して、C はそれを利用する
  • ライブラリ B と C はライブラリ A を利用していることを隠蔽する
  • ライブラリ A の static variable をなくした設計にする

Windows で static な CRT で同じ問題が出る件についての記事。

Hyper Estraier の API は返されたオブジェクトを free で開放する仕様のため est_free の追加の要望があった。 この件は私も依頼した記憶があるが見つけられなかった。 cbfree というのがあるようだ。

WebKit は WTF と bmalloc が static library で JavaScriptCore やそれ以外が共有ライブラリのため問題が発生する。

cairo の API はコンテキストを引数に取るので一見すると問題なさそうに思ってしまうが、 static 変数を持っていてそのアドレスの比較をしている ためこの問題に遭遇したことがある。

隠蔽する解決方法では反対の問題が起こりうる。 共有ライブラリ B と C の中に A を隠蔽して A のオブジェクトを外に出さないようにする。 だれかがそのライブラリをすべて static ライブラリか、すべて共有ライブラリとしてビルドしてしまうと、 2つあるべき A がひとつになってしまう。 そのため、A のライブラリの初期化・終了処理が2度呼ばれたり、マルチスレッドだとデータレースが発生する問題が考えられる。

この場合の解決方法としては、API はコンテキストを引数に取るようにして static 変数を利用しないのが望ましい。 static 変数をさけられない場合はライブラリの初期化・終了処理が複数回呼ばれたり、マルチスレッドから呼ばれても大丈夫なようにする。 もしくは、ライブラリ A を内包しない。ライブラリ A の初期化・終了処理は B や C のアプリ側が呼ぶことになる。 もしくは、ライブラリ A を2つにコピーしてリネームしてから B と C に隠蔽する。

オブジェクト指向の菱形継承問題が似ていると思ったら、 Wikipedia の記事にはオブジェクト指向以外の例でヘッダファイルの include の話が挙げられていた。