WindowsアプリをLinuxに移植しようとしています。このアプリケーションは、一部の関数を___stdcall
_属性でマークします。しかし、友人から、stdcallはWindowsでのみ使用され、Linuxでは意味がないと言われました(ただし、Windows GCCには存在します)。私はそれについてグーグルを検索しようとしました、そして、LinuxでIS stdacllがあるといういくつかの結果状態を得ました。
そう... ??
その上、GCCの場合、そのための2つの実装を見ました:__attribute__((__stdcall__))
と__attribute__((stdcall))
(stdcallの近くに下線なし)。どちらが好ましいですか(Linuxに適用する場合)?
ありがとう!
最も簡単な解決策は、Linuxで条件付きで__stdcallを何にも定義しないことです。
これがMSDNの__stdcallの説明へのリンクです: http://msdn.Microsoft.com/en-us/library/zxk0tw93(VS.80).aspx
WinAPI関数を呼び出すためにのみ使用されます。このようなWindowsアプリケーションをLinuxに移植するには、__ stdcallを何も定義しないだけでは不十分です。
#ifndef WIN32 // or something like that...
#define __stdcall
#endif
また、Win32 API関数の代わりに、Linux固有のAPI関数を呼び出す必要があります。 Win32 APIの特定の部分とアプリケーションのサイズ(コードの量)に応じて、それは適度に難しいものから困難なものまでのどこかになります。
アプリによって__stdcallとしてマークされている特定の関数はどれですか?
実際、GCCのWindowsポートには__stdcallが必要です。これは、Win32プラットフォームに準拠するコードを生成できるはずだからです。ただし、Linuxでは標準の呼び出し規約が1つしかなく、デフォルトのコンパイラ出力と一致するため、このステートメントは必要ありません。
アプリケーションがLinuxでコンパイルされない理由は、ほぼ確実に、Linuxで定義されていないWin32API関数を参照しているためです。適切なLinuxの対応物を見つける必要があります。 Win32APIとLinuxGLibc APIは大きく異なり、簡単に置き換えることはできません。
おそらく、アプリをLinuxに移植する最も簡単な方法は、Wineを使用することです。つまり、LinuxのWineでスムーズに実行されるようにWindowsコードを変更します。これは、最新のコンピュータゲームのような最も複雑なアプリケーションでさえ、Linuxで実行できるようにする方法です。
もちろん、本当にLinuxでネイティブに実行したい場合は、移植が唯一の方法です。
stdcallは単なる呼び出し規約ではありません。呼び出し規約であることに加えて、CオブジェクトとC++オブジェクト間の同型を可能にします。次に例を示します。
#define _CRT_SECURE_NO_WARNINGS // disable marking use of strcpy as error.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
class ICdeclGreeter {
public:
virtual ~ICdeclGreeter(){}
virtual void setGreeting(const char *greeting) = 0;
virtual void greet() = 0;
};
class IStdcallGreeter {
public:
virtual __stdcall ~IStdcallGreeter(){}
virtual void __stdcall setGreeting(const char *greeting) = 0;
virtual void __stdcall greet() = 0;
};
class CdeclGreeter : public ICdeclGreeter {
public:
char *greeting;
~CdeclGreeter() {
if (greeting != nullptr) {
free(greeting);
puts("[CdeclGreeter] destroyed");
}
}
void setGreeting(const char *greeting) {
this->greeting = (char *)malloc(strlen(greeting) + 1);
strcpy(this->greeting, greeting);
}
void greet() {
puts(greeting);
}
};
class StdcallGreeter : public IStdcallGreeter {
public:
char *greeting;
__stdcall ~StdcallGreeter() {
if (greeting != nullptr) {
free(greeting);
puts("[StdcallGreeter] destroyed");
}
}
void __stdcall setGreeting(const char *greeting) {
this->greeting = (char *)malloc(strlen(greeting) + 1);
strcpy(this->greeting, greeting);
}
void __stdcall greet() {
puts(greeting);
}
};
typedef struct pureC_StdcallGreeter pureC_StdcallGreeter;
typedef struct pureC_StdcallGreeterVtbl {
void (__stdcall *dtor)(pureC_StdcallGreeter *This);
void (__stdcall *setGreeting)(pureC_StdcallGreeter *This, const char *greeting);
void (__stdcall *greet)(pureC_StdcallGreeter *This);
} pureC_IStdcallGreeterVtbl;
struct pureC_StdcallGreeter {
pureC_IStdcallGreeterVtbl *lpVtbl;
char *greeting;
int length;
};
/* naive attempt at porting a c++ class to C;
on x86, thiscall passes This via ecx register rather than
first argument; this register cannot be accessed in C without
inline Assembly or calling a reinterpretation of byte array
as a function. there is no "This" argument in any of below. */
typedef struct pureC_CdeclGreeter pureC_CdeclGreeter;
typedef struct pureC_CdeclGreeterVtbl {
void (*dtor)(pureC_CdeclGreeter *This);
void (*setGreeting)(pureC_CdeclGreeter *This, const char *greeting);
void (*greet)(pureC_CdeclGreeter *This);
} pureC_CdeclGreeterVtbl;
struct pureC_CdeclGreeter {
pureC_CdeclGreeterVtbl *lpVtbl;
char *greeting;
int length;
};
void test() {
ICdeclGreeter *g = new CdeclGreeter;
g->setGreeting("hi");
g->greet();
IStdcallGreeter *g2 = new StdcallGreeter;
g2->setGreeting("hi");
g2->greet();
// we can pass pointers to our object to pure C using this interface,
// and it can still use it without doing anything to it.
pureC_StdcallGreeter *g3 = (pureC_StdcallGreeter *)g2;
g3->lpVtbl->setGreeting(g3, "hello, world!");
g3->lpVtbl->greet(g3);
g3->lpVtbl->dtor(g3);
free(g2);
/*
// cdecl passes this via ecx in x86, and not as the first argument;
// this means that this argument cannot be accessed in C without
// inline Assembly or equivelent. Trying to run code below will cause a runtime error.
pureC_CdeclGreeter *g4 = (pureC_CdeclGreeter *)g;
g4->lpVtbl->setGreeting(g4, "hello, world!");
g4->lpVtbl->greet(g4);
g4->lpVtbl->dtor(g4);
free(g);
*/
delete g;
}
int main(int argc, char **argv)
{
test();
system("pause");
return 0;
}
TLDR; 「This」をメソッドに送信するには、ecxレジスタを単にプッシュするのではなく「This」のアドレスに設定する必要があるため、cdeclがこの規則を使用するプラットフォーム上のCからC++クラスを使用できないようにするのと同じではありません。 C++が認識できるクラスをCで実装する場合、メソッドは、インラインアセンブリまたは同等のものがないとCにアクセスできないecxレジスタからこのポインタを取得する必要があります。
stdcallには、stdcallを使用するクラスが、何もしなくてもCまたはC++から簡単に同時に使用できるというNiceプロパティがあります。
したがって、#define __stdcall
__thiscallを処理しない限り;他にも微妙な違いがあるかもしれませんが。