Island Life

< Gaucheにdocstringが無い理由 | sambaプチはまりメモ >

2010/08/03

ヘッダにstatic member

C++の class template を使えば static メンバの実体がヘッダファイルに書けるカラクリ - ひげぽん OSとか作っちゃうかMona

C++の class template を使えば static メンバの実体がヘッダファイルに書けるというテクニックがある。考えてみると不思議な動作に思える。だって分割コンパイルしたら実体が複数個出来そうじゃない?。この裏側で起こっている事を実験前に予想して試したところ、予想通りだったのでうれしかったのでメモを残す。

リンカがよしなに計らってくれるのか、なるほど。と思ったけど じゃあリンカの預かり知らぬところで共有されたらやばそうだよなあ。 例えばdlopenされるやつとか。

環境はgcc-4.4.3 on Ubuntu 10.04。

Hoge.h と a.cpp はひげぽんさんのと同じ。 b.cppはdlsym経由で 呼びやすいようにC linkageにしておく。

b.cpp:

#include "Hoge.h"

extern "C"
void funcB()
{
    Hoge<bool> hoge;
    hoge.incrementAndShow();
}

mainはこんなかんじ。

main.cpp

#include <dlfcn.h>
#include <stdio.h>
#include <errno.h>

extern void funcA();
void (*pfuncB)();

int main(int argc, char *argv[])
{
    funcA();

    void *h = dlopen("./b.so", RTLD_NOW);
    if (!h) { perror("dlopen"); return 1; }
    pfuncB = (void (*)())dlsym(h, "funcB");
    if (!pfuncB) { perror("dlsym"); return 1; }
    
    pfuncB();

    dlclose(h);
    return 0;
}

ビルドはこんなかんじ。

Makefile:

all : main b.so

main : a.o
        g++ -o main main.cpp a.o -ldl

b.so : b.o
        g++ -shared -o b.so b.o

b.o : b.cpp
        g++ -fPIC -c b.cpp

clean :
        rm -f main *.so *.o *~

実行結果。別々のHoge::counterの実体を見てる。

$ ./main
counter = 1
counter = 1

まあ仕方ないっちゃ仕方ない話だけど。 外部APIとして見せるクラスでこのテクニックを使う場合は落とし穴になるかも。

(なお、b.cppを共有ライブラリlibb.soにしてmainリンク時に -lbでリンクした場合は、ちゃんとHoge::counterは共有される。)

理想的には、ランタイムがもっと賢くなって、 実行前の静的なリンク実行時の動的なリンクも同じように扱って くれればいいのかなあ。

Tags: Programming, C++

Past comment(s)

ひげぽん (2010/08/05 06:33:50):

今のところ、これはライブラリ提供者が注意しないといけないですね。 dlopen での使用は -lb と比べると頻度は少なそうなのであまり問題は起こらなそうですけど。 といいつつ、FFI が整備されている Lisp 系だと死にますね。

shiro (2010/08/05 07:27:54):

そのライブラリが将来dlopenから使われるかどうかというのはライブラリ作者にはわからないですからねぇ。Aさんが書いたヘッダX.hがこのテクニックを使ってて、無関係なBさんがXをincludeしてライブラリを作って、それをまた無関係なCさんがdlopenした、なんて場合、トラブルシューティングが大変になりそうな予感。 X.hがこのテクニックを使ってることを周知できる範囲だけで使うぶんには良いと思いますが。

Post a comment

Name: