web-dev-qa-db-ja.com

LinuxにSTDCALLはありますか?

WindowsアプリをLinuxに移植しようとしています。このアプリケーションは、一部の関数を___stdcall_属性でマークします。しかし、友人から、stdcallはWindowsでのみ使用され、Linuxでは意味がないと言われました(ただし、Windows GCCには存在します)。私はそれについてグーグルを検索しようとしました、そして、LinuxでIS stdacllがあるといういくつかの結果状態を得ました。

そう... ??

その上、GCCの場合、そのための2つの実装を見ました:__attribute__((__stdcall__))__attribute__((stdcall))(stdcallの近くに下線なし)。どちらが好ましいですか(Linuxに適用する場合)?

ありがとう!

19
Break Point

最も簡単な解決策は、Linuxで条件付きで__stdcallを何にも定義しないことです。

10
dicroce

これが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でネイティブに実行したい場合は、移植が唯一の方法です。

10
Andrei Sosnin

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を処理しない限り;他にも微妙な違いがあるかもしれませんが。

1
Dmitry