みんな「みーっつ! みんな笑顔であかるい世界!」
ルギア君「いま、5年2月18日って言った?」
ドンちゃん「ああ、そう言ったよ。」
ルギア君「1年ずれてるじゃないか!」
ドンちゃん「ええっと、5年が正しい。*1」
ルギア君「本当?」
ドンちゃん「今度はまちがいない。」
ルギア君「もう年明けから1ヶ月経っていると言うのに。」
ドンちゃん「去年はあまり活動してなかったからこんなことになったんだ。」
ルギア君「しっかりしてくれ。」
ドンちゃん「よし、わかったら仕事始め! 今日は5年2月18日の土曜日だ。頭の中を塗り替えておけ。」
みんな「はい!」
・・・
ルギア君「うーん・・・」
キリルン「どうしたんですか。」
ルギア君「CMake で作った C++ のライブラリにおなじプロジェクトの別のライブラリからリンクすると undefined reference になっちゃうんだよね・・・」
キリルン「それは難しい話ですね。」
ルギア君「そのライブラリの nm はこうだ。」
キリルン「はい。」
ルギア君「見安いようにマングルは解除しておく。」
[lugia@lugia-castle ~]$ nm /usr/lib/libdragonscript.so -C 000032a4 d DW.ref.__gxx_personality_v0 00003108 a _DYNAMIC 00003224 a _GLOBAL_OFFSET_TABLE_ w _Jv_RegisterClasses U _Unwind_Resume@@GCC_3.0 000032c0 b guard variable for dragon::IntlCommon::convert(void*, std::string const&)::ret 000032b0 b guard variable for dragon::IntlCommon::convert(std::string const&, std::string const&, std::string const&)::ret 000032d0 b guard variable for dragon::IntlCommon::mbstowcs(std::string const&)::ret 000032e0 b guard variable for dragon::IntlCommon::wcstombs(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&)::ret 00000d40 t dragon::IntlCommon::convert(void*, std::string const&) 00000db0 t dragon::IntlCommon::convert(std::string const&, std::string const&, std::string const&) 00001070 t dragon::IntlCommon::mbstowcs(std::string const&) 00000e60 t dragon::IntlCommon::wcstombs(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&) 000013b0 t dragon::DragonExcept::getLineNum() 000013a0 t dragon::DragonExcept::getFileName() 000013c0 t dragon::DragonExcept::getDescription() 00001570 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, dragon::DragonExcept::DragonCode, unsigned int) 000013d0 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, std::string&) 00001650 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, dragon::DragonExcept::DragonCode, unsigned int) 000014a0 t dragon::DragonExcept::DragonExcept(std::string, unsigned int, bool, std::string&) 00001920 t dragon::DragonExcept::~DragonExcept() 00001730 t dragon::DragonExcept::~DragonExcept() 00001b10 t dragon::DragonExcept::~DragonExcept() U __gnu_cxx::__exchange_and_add(int volatile*, int)@@GLIBCXX_3.4 U std::exception::what() const@@GLIBCXX_3.4 U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::_Rep::_S_empty_rep_storage@@GLIBCXX_3.4 U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::assign(wchar_t const*, unsigned int)@@GLIBCXX_3.4 U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::reserve(unsigned int)@@GLIBCXX_3.4 U std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::~basic_string()@@GLIBCXX_3.4 U std::string::_Rep::_M_destroy(std::allocator<char> const&)@@GLIBCXX_3.4 U std::string::_Rep::_S_empty_rep_storage@@GLIBCXX_3.4 U std::string::append(char const*, unsigned int)@@GLIBCXX_3.4 U std::string::assign(char const*, unsigned int)@@GLIBCXX_3.4 U std::string::assign(std::string const&)@@GLIBCXX_3.4 U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)@@GLIBCXX_3.4 U std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()@@GLIBCXX_3.4 U std::exception::~exception()@@GLIBCXX_3.4 U std::terminate()@@GLIBCXX_3.4 000030fc d typeinfo for dragon::DragonExcept U typeinfo for std::exception@@GLIBCXX_3.4 00001d5c r typeinfo name for dragon::DragonExcept U vtable for __cxxabiv1::__si_class_type_info@@CXXABI_1.3 000030e8 d vtable for dragon::DragonExcept 000032c8 b dragon::IntlCommon::convert(void*, std::string const&)::ret 000032b8 b dragon::IntlCommon::convert(std::string const&, std::string const&, std::string const&)::ret 000032d8 b dragon::IntlCommon::mbstowcs(std::string const&)::ret 000032e8 b dragon::IntlCommon::wcstombs(std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > const&)::ret U operator delete[](void*)@@GLIBCXX_3.4 U operator delete(void*)@@GLIBCXX_3.4 U operator new[](unsigned int)@@GLIBCXX_3.4 000030cc d __CTOR_END__ 000030c8 d __CTOR_LIST__ 000030d4 d __DTOR_END__ 000030d0 d __DTOR_LIST__ 00001f78 r __FRAME_END__ 000030d8 d __JCR_END__ 000030d8 d __JCR_LIST__ 000032a8 A __bss_start U __ctype_get_mb_cur_max@@GLIBC_2.0 U __cxa_allocate_exception@@CXXABI_1.3 U __cxa_atexit@@GLIBC_2.1.3 U __cxa_call_unexpected@@CXXABI_1.3 w __cxa_finalize@@GLIBC_2.1.3 U __cxa_throw@@CXXABI_1.3 00001d00 t __do_global_ctors_aux 00000c60 t __do_global_dtors_aux 000030e0 d __dso_handle U __errno_location@@GLIBC_2.0 w __gmon_start__ U __gxx_personality_v0@@CXXABI_1.3 00000d17 t __i686.get_pc_thunk.bx 000032a8 A _edata 000032ec A _end 00001d38 T _fini 00000a48 T _init 000032a8 b completed.5731 000032ac b dtor_idx.5733 00000ce0 t frame_dummy U iconv_close@@GLIBC_2.1 U iconv_open@@GLIBC_2.1 00000d20 t main U mbrtowc@@GLIBC_2.0 w pthread_cancel U strlen@@GLIBC_2.0 U wcrtomb@@GLIBC_2.0 U wcslen@@GLIBC_2.0 [lugia@lugia-castle ~]$
ルギア君「この dragon:: *2で始まる関数群が、僕が作ったモノなんだが、これを、」
#include "DragonExcept" #include <iostream> using namespace std; int main(int argc, char** argv) { try { throw dragon::DragonExcept("", 0, false, dragon::DragonExcept::SeeErrno, 0); return 0; } catch(dragon::DragonExcept& ex) { cout << argv[0] << ": "; if(ex.getFileName() != "") { cout << ex.getFileName(); if(ex.getLineNum() != 0) { cout << "(" << ex.getLineNum() << ")"; } cout << ": "; } cout << ex.getDescription(); } }
[ 80%] Building CXX object cdragon/src/CMakeFiles/cdragon.dir/main.cpp.o /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:26: 警告: unused parameter ‘argc’ Linking CXX executable cdragon CMakeFiles/cdragon.dir/main.cpp.o: In function `main': /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:28: undefined reference to `dragon::DragonExcept::DragonExcept(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned int, bool, dragon::DragonExcept::DragonCode, unsigned int)' /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:28: undefined reference to `dragon::DragonExcept::~DragonExcept()' /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:28: undefined reference to `typeinfo for dragon::DragonExcept' /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:32: undefined reference to `dragon::DragonExcept::getFileName()' /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:33: undefined reference to `dragon::DragonExcept::getFileName()' /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:34: undefined reference to `dragon::DragonExcept::getLineNum()' /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:35: undefined reference to `dragon::DragonExcept::getLineNum()' /home/lugia/Projects/DragonScript/trunk/cdragon/src/main.cpp:39: undefined reference to `dragon::DragonExcept::getDescription()' CMakeFiles/cdragon.dir/main.cpp.o:(.gcc_except_table+0x28): undefined reference to `typeinfo for dragon::DragonExcept' collect2: ld はステータス 1 で終了しました make[2]: *** [cdragon/src/cdragon] エラー 1 make[1]: *** [cdragon/src/CMakeFiles/cdragon.dir/all] エラー 2 make: *** [all] エラー 2
ルギア君「となってしまうんだ・・・。」
キリルン「うーん、私にはわかりませんね。」
ルギア君「CMakeLists.txt を弄れば何とかなるのかな・・・」
キリルン「さあ。試しに名前空間を止めてみるとかはどうでしょうか。」
ルギア君「やり甲斐はあるかね・・・」
・・・
ルギア君「アーカイブにしたらとりあえずできた。」
キリルン「でも、共有ライブラリのほうが良いんでしょ?」
ルギア君「うーん、どうだろ。」
キリルン「速さに関してはわからないということですか?」
ルギア君「そうだな。」
/usr/bin/c++ -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-exceptions -fno-check-new -fno-common -Woverloaded-virtual -fno-threadsafe-statics -fvisibility=hidden -fvisibility-inlines-hidden -fmessage-length=0 -fexceptions -O2 -g -DNDEBUG -DQT_NO_DEBUG -fPIC -Wl,--enable-new-dtags CMakeFiles/cdragon.dir/main.cpp.o CMakeFiles/cdragon.dir/cdragon.cpp.o -o cdragon -rdynamic -L/usr/lib/kde4/devel -L/home/lugia/Projects/DragonScript_build/lib ../../lib/libdragonscript.a -Wl,-rpath,/usr/lib/kde4/devel:/home/lugia/Projects/DragonScript_build/lib
ルギア君「共有ライブラリにすると libdragonscript.a が libdragonscript.so になるだけなんだがね。」
キリルン「ふうん。」
・・・
ルギア君「あ、わかった!」
キリルン「何?」
ルギア君「CMake で KDE4 を使おうとするといろいろオプションがつくけど。」
キリルン「はい。」
ルギア君「この中のどこかに原因があるな。」
/usr/bin/c++ -fmessage-length=0 -fexceptions -fPIC CMakeFiles/cdragon.dir/main.cpp.o CMakeFiles/cdragon.dir/cdragon.cpp.o -o cdragon -rdynamic -L/home/lugia/Projects/DragonScript_build/lib ../../libdragonscript/src/libdragonscript.so -Wl,-rpath,/home/lugia/Projects/DragonScript_build/lib:/home/lugia/Projects/DragonScript_build/libdragonscript/src /usr/bin/c++ -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-exceptions -fno-check-new -fno-common -Woverloaded-virtual -fno-threadsafe-statics -fvisibility=hidden -fvisibility-inlines-hidden -fmessage-length=0 -fexceptions -O2 -g -DNDEBUG -DQT_NO_DEBUG -fPIC -Wl,--enable-new-dtags CMakeFiles/cdragon.dir/main.cpp.o CMakeFiles/cdragon.dir/cdragon.cpp.o -o cdragon -rdynamic -L/usr/lib/kde4/devel -L/home/lugia/Projects/DragonScript_build/lib ../../lib/libdragonscript.so -Wl,-rpath,/usr/lib/kde4/devel:/home/lugia/Projects/DragonScript_build/lib
ルギア君「上が KDE4 なし、下があり。」
キリルン「確かに、いっぱいくっついていますね。」
ルギア君「ひとつづつ調べてみるか。」
だいぶ遅い追記
この原因は -fvisibility=hidden
でした。このオプションをつけて共有ライブラリをビルドするには、ソースコード中で、エクスポートする関数名を指定する必要があります。
普通なら、
#include <iostream> int foo() { std::cout << "エクスポートする関数\n"; return 0; }
こう書く関数を、
#include <iostream> __attribute__((visibility("default"))) int foo() { std::cout << "エクスポートする関数\n"; return 0; }
と、追加して、
$ g++ -shared -fvisibility=hidden -o foo.so foo.cpp
でビルドします。
この __attribute__
記述は gcc 専用なので、他のコンパイラでもコンパイルが通るようにするためには、マクロを使って gcc の場合だけ設定されるようにすると良いでしょう。詳しい方法と、Windows の対応については このページ を見てちょ。
visibility
のデフォルトは "default" (基本は公開) なのですが、非公開の関数やクラスにつけるよりも __declspec(...)
をつける必要のある Windows (正確には VC) との関係の都合上、こっちのほうがやりやすいみたいです。
ちなみに、gcc であれば、Cygwin や MinGW のデフォルトもやはり "default" なので、__declspec(...)
をつけなくても DLL を作れます。ちょこっと作りたいときには便利(かも)。