glscwin を C++ プログラムから使う 【まずプロトタイプ宣言を用意しましたが】 DK3.C という UNIX+X 環境上で動作するように作られたプログラムを動かそ うとして、まず関数プロトタイプがインクルード・ファイル glsc.h にないこ とに気がついた。 そこで石川君に glscwin で利用可能な関数のプロトタイプ宣言を記述した ファイルを作ってもらった。glschead.h がそれである。後で glsc.h から include しようと考えている。 ところがそれだけでは駄目だった. mathpc00% g++mg DK3.C c:/glsc/libglscd.a(glsc_win.o)(.text+0x194):glsc_win.c: undefined reference to `g_main' collect2: ld returned 1 exit status mathpc00% のような g_main が見つからないというエラーが出る。 【fakemain.c という解決策】 調べてみたら次のことが分かった。 (1) glsc.h の中で #define main() g_main() としてある。つまり関数 main() の名前を g_main() に変えている (かなり強引なことをしているわけ だ)。実は WinMain() が glsc_win.c に定義されている (あー、なるほど。ウィ ンドウを用いるプログラムとしては WinMain() から実行が始めねばならない)。 (2) glsc_win.c の中で hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) g_main, NULL, 0, &dwThreadId); のようにしてある。 つまりユーザーの書いたソース・プログラム中の main() (プリプロセッサー を通った後は g_main() という名前) をスレッドとして実行するということだ。 (3) 一般に GCC では、C 言語プログラム中の関数 int g_main() は、_g_main という名前に変換されるが (先頭にアンダースコアをつけるというルール)、 C++ 言語プログラム中の int g_main() は、_g_main__Fv という名前に変換さ れる (先頭にアンダースコアがつくだけではなく、関数の引数などの情報を付 加した名前になるのだったと記憶している)。それで DK3.C の中の main() (glsc.h にあるマクロの影響で、プリプロセッサーによって g_main() に置換 される) は、アセンブリー言語レベルでは _g_main__Fv という名前になって しまうため、_ g_main という名前を期待している GLSC のライブラリィは呼 び出す先が見つからなくなってしまうわけだ。 main() という名前だけは特別扱いで C でも C++ でも、アセンブリー言語 に落としたとき _g_main という名前になるので、#define main() g_main() をやめようとしたが (当然 CreateThread() の方も直す)、それは何故かうま く動かなかった。それで次のようにした。glscwin のライブラリィの中に (ア センブリー言語レベルで)_g_main__Fv を呼び出す _g_main というサブルーチ ンを忍び込ませる。リンカーによる名前の検索はユーザー作成の .o ファイル が先でライブラリーは後のはずだから、次のような仕組みでこれはうまく行く はずである。 (a) glscwin を C プログラムから利用する場合。ユーザーの書いた main() はアセンブリー言語レベルで _g_main になるので、CreateThread() は自然に この関数を見つけることができる。 (b) glscwin を C++ プログラムから利用する場合。ユーザーの書いた main() はアセンブリー言語レベルで _g_main__Fv になるので、CreateThread() が呼 び出そうとしている _g_main はユーザーの書いたプログラム中には存在しな いことになる。そのため glscwin のライブラリィの中まで検索が進み、忍び 込ませておいた_g_main が発見され、そこから間接的に _g_main__Fv (ユーザー のプログラムの中の main()) が呼び出されることになる。 -------------------- fakemain.c ------------------- int g_main() { extern int g_main__Fv(); g_main__Fv(); } --------------------------------------------------- 【しあげ】 glsc.h の尻尾に // C++ 用にプロトタイプ宣言 #include "glschead.h" という 2 行を書き加えた。これで解決のはずがぼろぼろエラー。おっと、 glschead.h で関数の型を void にしてあるが、何も書いていない場合は int だよね。というわけで、行頭の void をすべて int に置換した。今度は g_contln2() の型と衝突。こちらも直す。