トップ 最新

kumaryu日記

2009-11-18

_ だらだらしすぎ

最近だらだらしすぎてしまった。

…いや、ほんとになにもしてないような。

もうちょっとしっかりしないとなぁ。

やったことと言えばHTMLの書き方がわからなくてぶち切れたり、Rackでform type="file"な内容を取る方法が分からなくてぶち切れたりしたくらいか。HTMLは世界平和のために早くこの世から消えるべき。

そんなことよりDSのWizardryがAmazon専売なのにAmazonで予約もできない状態なんですがどうすればいいんでしょうか。いやほんとどうすれば。

_ [Ruby] Rubyの拡張ライブラリでグローバルなシンボル名が被ると大変なことになる

タイトルは釣り。話はRubyの拡張ライブラリにとどまりません。あれ、釣りになってない。というかこんなネタじゃ誰も釣れない。

MacでRubyの拡張ライブラリを書いてたんですよ。拡張ライブラリ。

で、機能沢山になったので複数の拡張ライブラリに分けた。

しかし元々一個なのをなんとか分けたのでコードが重複する。ちょっとだけなんでまあいいや。

同じ名前のグローバル変数ができちゃうけど、soというかbundleファイル*1が分かれてるからべつにいいよねー。

と思ってたんですが。だめだった。

どうも共有ライブラリをロードした時に、なんか先に同じシンボルがロードされてるなーと思ったらそっちを見に行くようにされてしまうようです。

自分のモジュール内の変数を初期化したと思ったらあっちが初期化されてたとかなってるんですよ。これは困る。

今回はグローバル変数で気付いたけど関数とかも同じはず。なんとかスコープを外に出さないようにしたいんだが。

調べてみるとまあ普通に起こることのようでMacでなくともちらほら情報があったので、それらを参考に解決策を考えてみた。

解決策その1 staticにする

static int hogehoge;

外に公開されませんので安心して使えます。おわり。

いや終わらない。コンパイル単位の中でしか使えないじゃないですか、これ。Cファイル一つで済むならいいんだけど、そんな小さい物ばっかりじゃないよね。

  • ポータビリティ: ◎
  • やりやすさ: ◎
  • 解決度: 論外

解決策その2 コンパイルオプションをいじる

これは難しい。というのもプラットフォームによって違うし、しかもだいたいrubyのコンパイルオプションでわざわざ今のような動作になってるからだ。

Macだと標準でtwo-level namespaceとやらになっているのでこの問題自体が起きないらしい。しかしおそらく他プラットフォームとの互換性などため、競合が発生する-flat_namespaceオプションを付けている。

Linuxなんかだとldに-Bsymbolicをつければ改善されるらしい。でも付けて上手くいくかは知らん。

さらに他のプラットフォームではどうなのかわからん。

  • ポータビリティ: ×?
  • やりやすさ: ◎
  • 解決度: ○

解決策その3 なんか変な指定をする

gccの古くないやつだと__attribute__でvisibility属性をくっつけられるらしい。それでhiddenを指定すると同じモジュール内で先に解決されるみたい。

でもgccの3.3以降?だけで、もちろんgcc以外では動かない。

あと公開したくないメンバ全部にこれつけんのかよ。

  • ポータビリティ: ○?△?
  • やりやすさ: △
  • 解決度: ○

解決策その4 かぶらないような名前にする

絶対かぶらない名前は無理でもかぶらなそうな名前はいけるだろ。モジュール名を頭につけるとかね。

でもそれ全部やんの?

  • ポータビリティ: ◎
  • やりやすさ: ×
  • 解決度: ◎

とまあこんな感じの解決策が思いついた。

コンパイルオプションはよくわからないので、3か4が有力。gccでしか動かないというのがどの程度ネックになるかだなぁ。 一番ポータブルな方法は4の名前をかえる。めんどいけどね。

しかしこれには別な問題もあるのでまともな解決方法は4しかないという。

というのも実はrubyの拡張ライブラリは静的リンクされるライブラリとして作ることができる。拡張ライブラリ全部入りの巨大rubyコマンドが作れるんです。

そのときはさすがに名前同じで実体が複数あるようなのは2でも3でも問題出ちゃうので嫌なら名前を変えるしかないわけだ。まあ静的リンクすることなんて普通無いので見なかったことにするのも手だが。

なので今回は名前を変えることに。そんな面倒なこと普段の俺ならお断りしますだが、今回は幸いC++で書いてたので全部namespaceで囲ってやったさ*2。結果として無難な方法を簡単に実現できた。

しかしこれは難しい問題だな。自分で書く分は気をつければいいが、配布が楽だからって拡張ライブラリに他のCライブラリを静的リンクとかはやめたほうが良さそうだ。静的リンクしたからって安心じゃないからね。他のライブラリとバージョン競合しておかしくなるかもしれん。

と。散々煽りましたが、皆様お使いのWindowsではrubyに拡張ライブラリを静的リンクでもしない限り起きませんのでご安心を。Windowsではインポートするシンボル毎にどのモジュールから拾ってくるか書いてあるので、少なくとも単純な名前衝突では問題でません。というかdll内で解決できるものはインポートテーブルにすら載りません*3。バージョン違いによるDLL Hellとかファイル名衝突とかしたらそりゃ困るけどね。

まあでもWindows以外でも動かしたいと思ったら気をつけないとね。

*1  Macでは拡張ライブラリがsoじゃなくてbundleになるので

*2  もちろんInit_なんたらはトップレベルでextern "C"ですよ。

*3  無理したら出来るかもしれないけどしないよね。