QA@IT

C++でmain()より先に実行される処理をライブラリ化したい

4104 PV

C++で、グローバル変数のコンストラクタを使ってとある関数を呼び出したいのですが、その変数がライブラリにある場合当然のごとくリンクされず、その結果コンストラクタも呼び出されませんので、呼び出したい関数を呼び出せません。

変数がメイン側から参照されないためにリンクされない事自体は当然の話ではありますが、そもそもの手法(main()実行前に関数を実行する方法)も含めてよりよい方法があればと思い質問いたしました。よろしくお願いいたします。

追記1

main()へ関数登録を明示的に書く場合、DerivedがDerived2、Derived3、など数が増えていく度に追加しなければならず、かつ私がその追加作業を忘れやすいため、できれば(私にとって)わかりやすい方法を取りたいためです。

後出しで恐縮ですが、少し変更した例を用意しました。FactoryRegistryへの登録できる関数の数を複数にして、Derived、Derived2、Derived3の定義とFactoryRegistryへの登録を一つのソースファイルにまとめてあります。

このような例の場合、登録だけをmain.ccに分けて書くことがちょっと苦痛に感じているため、クラスの定義と登録をひとつにまとめられることは私にとって利点です。

ちなみにこの方法はCppUnitでのテストコードの書き方を見て真似てみたものです。
http://cppunit.svn.sourceforge.net/viewvc/cppunit/trunk/cppunit/examples/money/MoneyTest.cpp?revision=603&view=markup

    // Registers the fixture into the 'registry'
    CPPUNIT_TEST_SUITE_REGISTRATION( MoneyTest );

MoneyTestクラスのソースファイルに上記の行を書くことで、テストを駆動する部分には触れること無くMoneyTestのテストを実行できます。

  • 私の見解を紹介する意図で書きます。こうしてほしいという意図ではありません。コードの例をリンク先に書かれていますが、質問の本文に書かれたほうが良いと思います。本文に書けばQA@IT内の検索で見つけることができるし、将来リンク先が消失することもないからです。一方、リンク先のほうがコードが見やすい、やコードをリンク先に書くことで質問本文が簡潔になる、などの見解をお持ちのかたもいらっしゃると思います。 -
  • ご指摘ありがとうございます。ご指摘の点はおっしゃるとおりです。質問内容をもう少し簡潔にまとめられれば良かったのですが、本文のみでうまく説明できる自信がなかったためひとまず動くコードを用意した次第です。簡潔なコードで問題を提示できそうなら本文に追記いたします。 -
  • そもそも、なぜmian()より先に処理しなければならないのですか?main()の冒頭で処理すれば済む話なのでは?また、ソースコードも見ましたが、そのFactoryクラスは一般的なFactoryクラスと何が違うのですか?または、一般的なFactoryクラスだと何か問題があるのですか? -
  • 補足説明を本文へ追記しました。おっしゃることはわかります。ただ、そのやりかたですと私はmain()に書くことを忘れやすいので、できるかぎり忘れにくい方法を取りたかったのです。あと、一般的なFactoryクラスを用いることで私のやりたいことが解決するならばそのようにいたします(そのあたりまだ私の理解が浅いです)。 -
  • その3番目に提示されたコードでも、まだ問題は解決していないという事なのでしょうか?もしまだ問題が残っているとしたら、それがなんなのか教えてください。 -
  • 質問本文に戻りますが、「main()を含むメイン側から参照されていない変数を、メイン側から明示せずリンクする方法を知りたい」です。その上でもし可能であれば「そもそも今回私が用いた方法はまずくて、もっとよりよい方法があるなら知りたい」ということです。前者はすでにngyukiさんから一つ回答をいただきました。後者の答えは得られなくても構いません。 -
  • ん??main()から参照されていない変数はmain()では使われないのですから、通常ならリンクする必要がないのでは?また、「そもそも今回私が用いた方法はまずくて...」の中の「まずい理由」を、もう少し詳しくお願いします。 -
  • 私の上げた例ではmain()では参照されていない変数がリンクされれば、私の意図通りに動くようになっています。ただ、この「main()では参照されていない変数がリンクされれば、私の意図通りに動く」事自体良くない方法ではないのか、という疑念が私にはあります。そのあたりを「もっとこうしたら良い」「別の方法を使え」「そもそも無理だから諦めろ」みたいな指摘を受けることを期待していました。 -
  • 素朴な疑問ですが、なぜ ok の方ではダメなのでしょう? テストコードをライブラリ化するメリットがわからないです。 -
  • あ、単に CppUnit を参考にしているだけでテストコードをライブラリ化したい訳ではないんですね -
  • 仰るとおりCppUnitにおけるテストクラスの登録手法を真似ました。 -

回答

そういうことがやりたいわけでは無いと思うので回答としては間違っていると思いますが・・・

明示的にシンボル(foo)をインポートする

make LDFLAGS="-u foo"

追記

解決になるかどうかわかりませんが、共有ライブラリにしてしまえば main 側で参照していなくてもライブラリ側のグローバルなインスタンスのコンストラクタも呼ばれるようです。

CXX = g++
CXXFLAGS = -Wall -Wextra -O2 -g
CPPFLAGS =
AR = ar
LD = g++
LDFLAGS =
LIBS =
RM = rm -f

all: ok ng ss

clean:
    $(RM) ok ng ss
    $(RM) libderived.a libderived.so
    $(RM) main.o derived.o derived2.o derived3.o

ok: main.o derived.o derived2.o derived3.o
    $(LD) $(LDFLAGS) -o $@ main.o derived.o derived2.o derived3.o

ng: main.o libderived.a
    $(LD) $(LDFLAGS) -o $@ main.o -L. -lderived

ss: main.o libderived.so
    $(LD) $(LDFLAGS) -o $@ main.o -Wl,-rpath -Wl,. libderived.so

main.o: main.cc base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -o $@ -c $<

derived.o: derived.cc base.hh base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -fPIC -o $@ -c $<

derived2.o: derived2.cc base.hh base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -fPIC -o $@ -c $<

derived3.o: derived3.cc base.hh base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -fPIC -o $@ -c $<

libderived.a: derived.o derived2.o derived3.o
    $(RM) $@; $(AR) crs $@ derived.o derived2.o derived3.o

libderived.so: derived.o derived2.o derived3.o
    $(LD) $(LDFLAGS) -shared -o $@ derived.o derived2.o derived3.o
make ss && ./ss

追記

あるいは -whole-archive でアーカイブ内のシンボルを全部リンクとか

CXX = g++
CXXFLAGS = -Wall -Wextra -O2 -g
CPPFLAGS =
AR = ar
LD = g++
LDFLAGS =
LIBS =
RM = rm -f

all: ok ng aa

clean:
    $(RM) ok ng aa
    $(RM) libderived.a libderived.so
    $(RM) main.o derived.o derived2.o derived3.o

ok: main.o derived.o derived2.o derived3.o
    $(LD) $(LDFLAGS) -o $@ main.o derived.o derived2.o derived3.o

ng: main.o libderived.a
    $(LD) $(LDFLAGS) -o $@ main.o -L. -lderived

aa: main.o libderived.a
    $(LD) $(LDFLAGS) -o $@ main.o -Wl,-whole-archive libderived.a -Wl,-no-whole-archive

main.o: main.cc base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -o $@ -c $<

derived.o: derived.cc base.hh base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -o $@ -c $<

derived2.o: derived2.cc base.hh base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -o $@ -c $<

derived3.o: derived3.cc base.hh base_factory_registry.hh
    $(CXX) $(CXXFLAGS) -o $@ -c $<

libderived.a: derived.o derived2.o derived3.o
    $(RM) $@; $(AR) crs $@ derived.o derived2.o derived3.o

make aa && ./aa

追記

まさに回答のような記事 main() の前に関数を呼ぶ

編集 履歴 (3)
  • ご回答ありがとうございます。手元のMac OS Xで動作を確認できました。なるほどこういう手もアリですね。 -
  • 追記ありがとうございます。すばらしいご回答有り難うございます。手元ではリンカに -whole-archive を渡す方法を使うことにいたしました。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。