以前にこのblogでちょこっと書いたけど、仕事で組んでいるプログラムの話。
元がUNIX+C言語で開発されたプログラムをWindowsに移植するというプロジェクトで、UNIXとWindowsで同一ソースにしたいという要望がありました。
なるべく#ifdefを使わずにソースを見やすくするという方針だったので、あれこれ苦労しました。
ようやくテストフェーズになったので、メモとして残しておきます。誰得。
1 マクロで何とかする
LINUXとWindowsで同名関数に機能差がある場合とか。
#ifdef (_MSC_VER)
#define atol _atoi64 // Windows(32/64bit)ではlong型が32bitなので戻り値が桁落ちするのを防ぐ
#endif
LINUXとWindowsで変数の型に差がある場合とか。
例えばTCP/IPの部分。LINUXだとソケットはint型だがWindowsはSOCKET型。
こんなときは#typedefで型を宣言する。
#ifdef (_MSC_VER)
#typedef type_socket SOCKET
#else
#typedef type_socket int
#endif
そしてソースで型宣言している部分を書き換える。
// int socketfd;
type_socket socketfd;
これはint/long/size_t/__int64のwarning潰しにも使える。
LINUXとWindowsで同名関数の引数型に差がある場合もマクロでOK。
#ifdef (_MSC_VER)
#define bind(a, b, c) bind(a, b, (int)c)
#endif
2 代替関数を作成する。
LINUXに存在してWindowsに存在しない関数は代替関数を作成する。
int random(void) {
UINT r = 0;
rand_s(&r);
return (r & INT_MAX); // Red Hat Linux64bitの戻り値0~INT_MAXに合わせる
}
pthreadやカタログ、正規表現などはこのパターン。
正規表現はここだけcppで格好悪いけどstd::tr1を使った。
3 細かなwarningに目をつぶる
Windowsだと、strcpy()などの関数を使うと「新しい関数使え」とwarningが出る。
コンパイルオプションでその警告を抑制できる。
4 ビルド時に関数のシンボル情報を要求されたら
exeとプラグインdllの関係でexe側の関数をdllがコールする場合。LINUXは問題なくビルドできるが、Windowsはシンボル情報を要求されてリンクエラーになる。
exeをビルドするときにlibファイルも生成し、そのlibファイルをdllのプロジェクトに取り込ませるという手法がある。
以下はサンプルソースというかイメージ。
/*----- test.exeのソース start -----*/
#include
#include
// prototype
__declspec(dllexport) int test_api(int mode);
int main(void)
{
HMODULE hmod = NULL;
hmod = LoadLibrary("plugin.dll");
if (hmod == NULL) {
printf("LoadLibrary() failed.\n");
} else {
// 処理は略
FreeLibrary(hmod);
}
test_api(1);
return 0;
}
/*----- test.exeのソース end -----*/
/*----- plugin.dllのソース start -----*/
#include
// prototype test.exeの関数
#pragma comment(lib, "test.lib")
extern int test_api(int mode);
// prototype 外部に公開する関数
__declspec(dllexport) int userexit(char *val);
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
return TRUE;
}
int userexit(char *val)
{
test_api(2);
}
/*----- plugin.dllのソース end -----*/
ちなみに、この手法だとtest.exeをリネームして実行したときにplugin.dllの動的ロードに失敗する。
なぜdllロードに失敗するのかは、ネットを検索してもうまい説明が見つからないんだよなぁ……
元がUNIX+C言語で開発されたプログラムをWindowsに移植するというプロジェクトで、UNIXとWindowsで同一ソースにしたいという要望がありました。
なるべく#ifdefを使わずにソースを見やすくするという方針だったので、あれこれ苦労しました。
ようやくテストフェーズになったので、メモとして残しておきます。誰得。
1 マクロで何とかする
LINUXとWindowsで同名関数に機能差がある場合とか。
#ifdef (_MSC_VER)
#define atol _atoi64 // Windows(32/64bit)ではlong型が32bitなので戻り値が桁落ちするのを防ぐ
#endif
LINUXとWindowsで変数の型に差がある場合とか。
例えばTCP/IPの部分。LINUXだとソケットはint型だがWindowsはSOCKET型。
こんなときは#typedefで型を宣言する。
#ifdef (_MSC_VER)
#typedef type_socket SOCKET
#else
#typedef type_socket int
#endif
そしてソースで型宣言している部分を書き換える。
// int socketfd;
type_socket socketfd;
これはint/long/size_t/__int64のwarning潰しにも使える。
LINUXとWindowsで同名関数の引数型に差がある場合もマクロでOK。
#ifdef (_MSC_VER)
#define bind(a, b, c) bind(a, b, (int)c)
#endif
2 代替関数を作成する。
LINUXに存在してWindowsに存在しない関数は代替関数を作成する。
int random(void) {
UINT r = 0;
rand_s(&r);
return (r & INT_MAX); // Red Hat Linux64bitの戻り値0~INT_MAXに合わせる
}
pthreadやカタログ、正規表現などはこのパターン。
正規表現はここだけcppで格好悪いけどstd::tr1を使った。
3 細かなwarningに目をつぶる
Windowsだと、strcpy()などの関数を使うと「新しい関数使え」とwarningが出る。
コンパイルオプションでその警告を抑制できる。
4 ビルド時に関数のシンボル情報を要求されたら
exeとプラグインdllの関係でexe側の関数をdllがコールする場合。LINUXは問題なくビルドできるが、Windowsはシンボル情報を要求されてリンクエラーになる。
exeをビルドするときにlibファイルも生成し、そのlibファイルをdllのプロジェクトに取り込ませるという手法がある。
以下はサンプルソースというかイメージ。
/*----- test.exeのソース start -----*/
#include
#include
// prototype
__declspec(dllexport) int test_api(int mode);
int main(void)
{
HMODULE hmod = NULL;
hmod = LoadLibrary("plugin.dll");
if (hmod == NULL) {
printf("LoadLibrary() failed.\n");
} else {
// 処理は略
FreeLibrary(hmod);
}
test_api(1);
return 0;
}
/*----- test.exeのソース end -----*/
/*----- plugin.dllのソース start -----*/
#include
// prototype test.exeの関数
#pragma comment(lib, "test.lib")
extern int test_api(int mode);
// prototype 外部に公開する関数
__declspec(dllexport) int userexit(char *val);
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
return TRUE;
}
int userexit(char *val)
{
test_api(2);
}
/*----- plugin.dllのソース end -----*/
ちなみに、この手法だとtest.exeをリネームして実行したときにplugin.dllの動的ロードに失敗する。
なぜdllロードに失敗するのかは、ネットを検索してもうまい説明が見つからないんだよなぁ……
コメント