Solusi paling sederhana adalah dengan mendefinisikan __stdcall tanpa syarat di Linux.
stdcall BUKAN hanya konvensi panggilan; selain menjadi konvensi pemanggilan, ini memungkinkan isomorfisme antara objek C dan C++. Berikut contohnya:
#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; itu tidak sama dengan cdecl membuat kelas C++ tidak dapat digunakan dari C pada platform yang menggunakan konvensi ini karena untuk mengirim "Ini" ke suatu metode, Anda harus menyetel register ecx ke alamat "Ini" daripada hanya mendorongnya, dan juga jika Anda ingin mengimplementasikan kelas dalam C yang dapat dikenali oleh C++, metode ini perlu mendapatkan penunjuk ini dari register ecx yang tidak dapat diakses oleh C tanpa perakitan sebaris atau yang setara.
stdcall memiliki properti bagus ini sehingga kelas yang menggunakan stdcall dapat dengan mudah digunakan secara bersamaan dari C atau C++ tanpa melakukan apa pun.
Jadi Anda hanya bisa #define __stdcall
selama Anda tidak berurusan dengan __thiscall; meskipun mungkin ada beberapa perbedaan halus lainnya.
Berikut tautan ke deskripsi __stdcall di MSDN:http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.80).aspx
Ini hanya digunakan untuk memanggil fungsi WinAPI. Untuk mem-porting aplikasi Windows seperti itu ke Linux, Anda memerlukan lebih dari sekadar mendefinisikan __stdcall ke nol:
#ifndef WIN32 // or something like that...
#define __stdcall
#endif
Anda juga perlu memanggil fungsi API khusus Linux alih-alih fungsi Win32 API. Bergantung pada bagian tertentu dari Win32 API dan ukuran aplikasi (jumlah kode), itu bisa berada di mana saja antara cukup sulit dan menakutkan.
Fungsi spesifik mana yang ditandai oleh aplikasi sebagai __stdcall?
Memang, port Windows GCC harus memiliki __stdcall, karena seharusnya dapat menghasilkan kode yang sesuai untuk platform Win32. Tetapi karena di Linux hanya ada satu konvensi pemanggilan standar dan bertepatan dengan keluaran kompiler default, pernyataan ini tidak diperlukan.
Alasan aplikasi Anda tidak dapat dikompilasi di Linux hampir pasti karena fakta bahwa aplikasi tersebut mereferensikan fungsi Win32 API yang tidak ditentukan di Linux -- Anda perlu menemukan rekanan Linux yang sesuai. Win32 API dan Linux GLibc API sangat jauh berbeda dan tidak dapat diganti dengan mudah.
Mungkin cara termudah untuk mem-porting aplikasi Anda ke Linux adalah dengan menggunakan Wine, yaitu memodifikasi kode Windows sedemikian rupa, sehingga berjalan lancar di bawah Wine di Linux. Ini adalah cara bahkan aplikasi yang paling kompleks, seperti game komputer modern, dibuat untuk dijalankan di Linux.
Tentu saja, jika Anda benar-benar ingin berjalan secara native di Linux, maka porting adalah satu-satunya cara.