C++ Linux 中有 STDCALL 吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3054257/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there STDCALL in Linux?
提问by Break Point
I'm trying to port a Windows app to Linux. This appplication marks some functions with the __stdcall
attribute. However, i was told by a friend that stdcall is used only on windows and has no meaning in linux (but DOES exist in Windows GCC).
I tried to search Google about that, and got some results state that there IS stdacll in Linux.
我正在尝试将 Windows 应用程序移植到 Linux。此应用程序使用__stdcall
属性标记一些功能。但是,一位朋友告诉我,stdcall 仅在 Windows 上使用,在 linux 中没有任何意义(但在 Windows GCC 中确实存在)。我试图在谷歌上搜索一下,并得到一些结果表明 Linux 中有 stdacll。
So... ??
所以... ??
Besides, for GCC I saw 2 implementations for that: __attribute__((__stdcall__))
and __attribute__((stdcall))
(without the underscores near stdcall).
Which one is preferred (If applied to Linux at all)?
此外,对于 GCC,我看到了 2 个实现:__attribute__((__stdcall__))
和__attribute__((stdcall))
(在 stdcall 附近没有下划线)。哪一个是首选(如果完全适用于 Linux)?
Thanks!
谢谢!
采纳答案by dicroce
The simplest solution is to just define __stdcall to nothing conditionally on Linux.
最简单的解决方案是在 Linux 上有条件地将 __stdcall 定义为空。
回答by Andrei Sosnin
Here's a link to __stdcall description on MSDN: http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.80).aspx
这是 MSDN 上 __stdcall 描述的链接:http: //msdn.microsoft.com/en-us/library/zxk0tw93(VS.80) .aspx
It's only used to call WinAPI functions. To port such a Windows application to Linux, you need much more than just defining __stdcall to nothing:
它仅用于调用 WinAPI 函数。要将这样的 Windows 应用程序移植到 Linux,您需要的不仅仅是将 __stdcall 定义为空:
#ifndef WIN32 // or something like that...
#define __stdcall
#endif
You would also need to call the Linux-specific API functions instead of Win32 API ones. Depending on the particular part of Win32 API and the size of the application (amount of code), it can be anywhere between moderately difficult and daunting.
您还需要调用 Linux 特定的 API 函数而不是 Win32 API 函数。根据 Win32 API 的特定部分和应用程序的大小(代码量),它可以介于中等难度和令人生畏之间。
Which specific functions are marked by the app as __stdcall?
应用程序将哪些特定功能标记为 __stdcall?
Indeed, Windows port of GCC has to have __stdcall, because it's supposed to be able to generate conforming code for the Win32 platform. But since under Linux there is only one standard calling convention and it coincides with the default compiler output, this statement is not needed.
实际上,GCC 的 Windows 端口必须具有 __stdcall,因为它应该能够为 Win32 平台生成符合要求的代码。但由于在 Linux 下只有一个标准调用约定,并且与默认编译器输出一致,因此不需要此语句。
The reason your application is not compiling under Linux is almost certainly due to the fact, that it references Win32 API functions that are not defined under Linux -- you need to find appropriate Linux counterparts. Win32 API and Linux GLibc API-s are very much different and cannot be substituted easily.
您的应用程序没有在 Linux 下编译的原因几乎可以肯定是因为它引用了未在 Linux 下定义的 Win32 API 函数——您需要找到合适的 Linux 对应物。Win32 API 和 Linux GLibc API-s 非常不同,不能轻易替换。
Probably the easiest way to port your app to Linux would be to use Wine, i.e. modifying the Windows code in such a way, that it runs smoothly under Wine in Linux. This is the way even the most complex applications, like modern computer games, have been made to run under Linux.
将您的应用程序移植到 Linux 的最简单方法可能是使用 Wine,即以这样一种方式修改 Windows 代码,使其在 Linux 中的 Wine 下顺利运行。即使是最复杂的应用程序,如现代计算机游戏,也是在 Linux 下运行的方式。
Of course, if you really want it to be running natively under Linux, then porting is the only way to go.
当然,如果你真的希望它在 Linux 下本地运行,那么移植是唯一的出路。
回答by Dmitry
stdcall is NOT just a calling convention; in addition to being a calling convention, it allows an isomorphism between C and C++ objects. Here's an example:
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; it's not the same as cdecl makes C++ classes not usable from C on platforms using this convention because in order to send "This" to a method, you must set ecx register to address of "This" rather than just pushing it, and likewise if you want to implement a class in C that C++ can recognize, the method will need to get This pointer from ecx register which is not accessible to C without inline assemby or equivelent.
TLDR;这与 cdecl 使 C++ 类无法在使用此约定的平台上从 C 使用的不同,因为为了将“This”发送到方法,您必须将 ecx 寄存器设置为“This”的地址,而不仅仅是推送它,同样如果你想在 C 中实现一个 C++ 可以识别的类,该方法需要从 ecx 寄存器中获取 This 指针,如果没有内联汇编或等效,C 就无法访问该指针。
stdcall has this nice property that classes that use stdcall can easily be simultaneously usable from C or C++ without doing anything to them.
stdcall 有一个很好的特性,即使用 stdcall 的类可以很容易地同时从 C 或 C++ 使用,而无需对它们做任何事情。
So you can only #define __stdcall
as long as you don't deal with __thiscall; although there might be some other subtle distinctions.
所以你只能#define __stdcall
在不处理 __thiscall 的情况下;尽管可能还有其他一些细微的区别。