Nejjednodušším řešením je pouze podmíněně definovat __stdcall to nothing na Linuxu.
stdcall NENÍ jen konvence volání; kromě toho, že jde o konvenci volání, umožňuje izomorfismus mezi objekty C a C++. Zde je příklad:
#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; není to stejné, jako když cdecl dělá třídy C++ nepoužitelnými z C na platformách používajících tuto konvenci, protože pro odeslání „Toto“ do metody musíte nastavit registr ecx na adresu „Toto“ a ne jen tlačit, a podobně pokud chcete implementovat třídu v C, kterou C++ dokáže rozpoznat, metoda bude muset získat tento ukazatel z registru ecx, který není pro C přístupný bez inline sestavení nebo ekvivalentu.
stdcall má tu příjemnou vlastnost, že třídy, které používají stdcall, mohou být snadno současně použitelné z C nebo C++, aniž by s nimi něco dělaly.
Můžete tedy pouze #define __stdcall
pokud se nebudete zabývat __thiscall; i když mohou existovat nějaké další jemné rozdíly.
Zde je odkaz na popis __stdcall na MSDN:http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.80).aspx
Používá se pouze k volání funkcí WinAPI. Chcete-li přenést takovou aplikaci Windows do systému Linux, potřebujete mnohem více než jen definovat __stdcall to nothing:
#ifndef WIN32 // or something like that...
#define __stdcall
#endif
Také byste museli volat funkce API specifické pro Linux místo funkcí Win32 API. V závislosti na konkrétní části Win32 API a velikosti aplikace (množství kódu) může být kdekoli mezi středně obtížnou a skličující.
Které konkrétní funkce jsou v aplikaci označeny jako __stdcall?
Port Windows GCC skutečně musí mít __stdcall, protože má být schopen generovat vyhovující kód pro platformu Win32. Ale protože pod Linuxem existuje pouze jedna standardní konvence volání a shoduje se s výchozím výstupem kompilátoru, není toto prohlášení potřeba.
Důvod, proč se vaše aplikace nekompiluje pod Linuxem, je téměř jistě způsoben tím, že odkazuje na funkce Win32 API, které nejsou definovány pod Linuxem – musíte najít vhodné linuxové protějšky. Win32 API a Linux GLibc API se velmi liší a nelze je snadno nahradit.
Pravděpodobně nejjednodušší způsob, jak přenést aplikaci do Linuxu, by bylo použít Wine, tedy upravit kód Windows tak, aby fungovala pod Wine v Linuxu hladce. To je způsob, jakým byly pod Linuxem vytvořeny i ty nejsložitější aplikace, jako jsou moderní počítačové hry.
Samozřejmě, pokud opravdu chcete, aby to běželo nativně pod Linuxem, pak je portování jedinou cestou.