Есть ли STDCALL в Linux? - PullRequest
       27

Есть ли STDCALL в Linux?

18 голосов
/ 16 июня 2010

Я пытаюсь перенести приложение Windows на Linux.Это приложение помечает некоторые функции атрибутом __stdcall.Однако мне сказал друг, что stdcall используется только в Windows и не имеет значения в Linux (но в Windows GCC существует).Я попытался найти в Google информацию об этом, и получил некоторые результаты, утверждая, что в Linux есть stdacll.

Итак ... ??

Кроме того, для GCC я видел 2 реализации для этого: __attribute__((__stdcall__)) и __attribute__((stdcall)) (без подчеркиваний возле стандартного вызова).Какой из них предпочтительнее (если вообще применяется к Linux)?

Спасибо!

Ответы [ 3 ]

10 голосов
/ 16 июня 2010

Самое простое решение - просто определить __stdcall на Linux в условных условиях.

9 голосов
/ 16 июня 2010

Вот ссылка на описание __stdcall в MSDN: http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.80).aspx

Используется только для вызова функций WinAPI. Чтобы портировать такое приложение Windows на Linux, вам нужно гораздо больше, чем просто определить __stdcall:

#ifndef WIN32 // or something like that...
#define __stdcall
#endif

Вам также потребуется вызывать специфичные для Linux функции API вместо Win32 API. В зависимости от конкретной части Win32 API и размера приложения (объема кода), это может быть где-то между умеренно сложным и пугающим.

Какие конкретные функции помечены приложением как __stdcall?

Действительно, порт GCC для Windows должен иметь __stdcall, поскольку он должен генерировать соответствующий код для платформы Win32. Но поскольку в Linux существует только одно стандартное соглашение о вызовах, и оно совпадает с выводом компилятора по умолчанию, этот оператор не нужен.

Причина, по которой ваше приложение не компилируется в Linux, почти наверняка связана с тем, что оно ссылается на функции Win32 API, которые не определены в Linux - вам необходимо найти соответствующие аналоги Linux. Win32 API и Linux GLibc API очень сильно отличаются друг от друга и не могут быть легко заменены.

Вероятно, самый простой способ перенести ваше приложение на Linux - это использовать Wine, то есть модифицировать код Windows таким образом, чтобы он нормально работал под Wine в Linux. Таким образом, даже самые сложные приложения, такие как современные компьютерные игры, были созданы для работы под Linux.

Конечно, если вы действительно хотите, чтобы он работал под Linux, то портирование - единственный путь.

1 голос
/ 23 февраля 2018

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;это не то же самое, что cdecl делает классы C ++ не пригодными для использования из C на платформах, использующих это соглашение, потому что для отправки «This» в метод, вы должны установить регистр ecx по адресу «This», а не просто нажимать его, и аналогично, еслиЕсли вы хотите реализовать класс C, который C ++ может распознать, метод должен получить этот указатель из регистра ecx, который не доступен для C без встроенной сборки или equivelent.

У stdcall есть это замечательное свойство, заключающееся в том, что классы, использующие stdcall, можно легко использовать одновременно из C или C ++, ничего не делая с ними.

Таким образом, вы можете только #define __stdcall, пока вы нене иметь дела с __thiscall;хотя могут быть и другие тонкие различия.

...