C ++ DLL def文件中的重载函数

时间:2020-03-05 18:42:50  来源:igfitidea点击:

我正在编写一个C / C ++ DLL,并希望导出在使用像这样的.def文件之前完成的某些功能。

LIBRARY "MyLib"
EXPORTS
  Foo
  Bar

使用这样定义的代码,例如:

int Foo(int a);
void Bar(int foo);

但是,如果我想像下面这样声明Foo()的重载方法,该怎么办:

int Foo(int a, int b);

由于def文件仅具有函数名称,而没有完整的原型,因此我看不到它将如何处理重载的函数。我们是否只使用一个条目,然后在将正确原型的函数指针传递给LoadLibrary()时指定所需的重载版本?

编辑:要清楚,这是在Windows上使用Visual Studio 2005

编辑:将非def(__declspec)方法标记为答案...我知道这实际上并不能解决我想要的使用def文件的问题,但是似乎没有使用def文件的(官方)解决方案。但是,如果有人知道我们没有重载的函数和def文件,这将使问题悬而未决。

解决方案

回答

在代码本身中,使用__declspec(dllexport)标记要导出的函数。例如:

#define DllExport __declspec(dllexport)

int DllExport  Foo( int a ) {
  // implementation
}
int DllExport Foo( int a, int b ) {
  // implementation
}

如果这样做,则无需在.def文件中列出功能。

或者,我们可以使用默认参数值,例如:

int Foo( int a, int b = -1 )

假定存在一个b值,我们可以使用该值指示未使用。如果-1是b的合法值,或者没有或者不应该使用默认值,则此方法将无效。

编辑(Adam Haile):已更正以使用__declspec作为__dllspec是不正确的,因此我可以将其标记为正式答案...它足够接近。

编辑(Graeme):糟糕,感谢我们纠正我的错字!

回答

函数重载是一种C ++功能,它依赖于名称处理(链接器错误消息中的隐秘功能名称)。

通过将变形的名称写入def文件,我可以使测试项目链接并运行:

LIBRARY "TestDLL"
EXPORTS
    ?Foo@@YAXH@Z
    ?Foo@@YAXHH@Z

似乎为

void Foo( int x );
void Foo( int x, int y );

因此,从错误消息中复制C ++函数名称,并将它们写入def文件中。但是,真正的问题是:为什么要使用def文件而不使用__declspec(dllexport)?

我使用VC ++ 2008进行了测试,这些杂乱的名称是不可移植的。

回答

没有语言或者版本不可知的方法来导出重载函数,因为随着每个编译器版本的发布,更改约定都会改变。

这就是为什么大多数WinXX函数具有有趣的名称(如* Ex或者* 2)的原因之一。

回答

没有正式的方法可以执行我们想要的操作,因为dll接口是C api。

编译器本身使用整改的名称作为解决方法,因此,当我们不想在代码中进行太多更改时,应使用整改的名称。

回答

我有一个类似的问题,所以我也想发布。

  • 通常使用
extern "C" __declspec(dllexport) void Foo();

导出函数名称就可以了。通常它将导出不需要修改的名称,而无需.def文件。但是,也有一些例外,例如__stdcall函数和重载的函数名。

  • 如果我们声明一个函数使用__stdcall约定(就像许多API函数一样),那么
extern "C" __declspec(dllexport) void __stdcall Foo();

将导出一个错误的名称,例如_Foo @ 4. 在这种情况下,我们可能需要将导出的名称显式映射到内部错误的名称。

A.如何导出一个完整的名称。在.def文件中添加

----
EXPORTS
    ; Explicit exports can go here

    Foo
-----

这将尝试为内部函数Foo找到"最佳匹配"并将其导出。在上述情况下,只有
一个foo这将创建映射

Foo = _Foo @ 4

如通过dumpbin / EXPORTS可以看到的

如果我们重载了一个函数名,则可能需要在.def文件中明确说明我们想要的函数
通过使用entryname [= internalname]语法指定错误的名称。例如

----
EXPORTS
    ; Explicit exports can go here

    Foo=_Foo@4
-----

B..def文件的替代方法是我们可以使用#pragma"就地"导出名称。

#pragma comment(linker, "/export:Foo=_Foo@4")

C.第三种选择是仅声明Foo的一个版本作为外部" C",以不加破坏地导出。有关详细信息,请参见此处。