使用扩展参数调用 Delphi 函数时出现 C++ 错误
Posted
技术标签:
【中文标题】使用扩展参数调用 Delphi 函数时出现 C++ 错误【英文标题】:C++ error when calling a Delphi function with Extended parameters 【发布时间】:2016-08-30 15:46:59 【问题描述】:在一个新的 Win32 项目中,我有以下 Delphi 函数:
procedure SetValue(value1, value2 : Extended);
begin
end;
在同一个项目中,但来自 C++ 单元,我调用了这个函数:
SetValue(10, 40);
当我使用 BCC32C (CLang) 编译时检查 value1
时,我得到 1.68132090507504896E-4932,这是不正确的。
用 BCC32(经典)编译,我得到 10。
在这两种情况下,第二个参数都是 40。
Extended
值和参数堆栈加载似乎有问题。
我使用 RAD Studio 10.1 Berlin。
我该如何解决这个问题?
更新
我没有包含声明,因为 hpp 是在编译时自动创建的。无论如何,声明是:
extern DELPHI_PACKAGE void __fastcall SetValue(System::Extended value1, System::Extended value2);
复制项目:
1-在 Rad Studio 中创建 C++ 项目
2-用上面的SetValue函数添加一个Delphi单元
3-从C++单元中,用#include添加hpp头并调用SetValue
就是这样。
我需要使用扩展类型。我正在使用外部 Delphi 库,因此无法更改类型。上面的代码是对问题的简化。实际上,问题在于调用该库的函数,该函数在参数中使用了 Extended。 Extended 在 Delphi 中是本机类型,但在 C++ 中它映射为 long double、10 字节(对于 Win32)。
【问题讨论】:
从外部应用程序调用该函数时不应该将其标记为cdecl吗? 如何在 C++ 代码中声明函数? 停止使用Extended
。这是个坏主意。使用Double
或Real
(Double
的别名)。您只需要展示调用约定和您的 C++ 代码,我们就有机会。为什么我们不能有minimal reproducible example?我在我见过的所有问题中只问了一半。当然,消息正在传递.......
@JoachimPileborg 当为 C++ 编译 Delphi 单元时,Delphi 编译器会自动生成 C++ .hpp
头文件。希望 OP 使用该标头而不是手动声明该函数。
@GuidoG 不,调用约定不需要是cdecl
。并且该函数没有被外部应用程序调用。 Delphi 代码可以直接在 C++Builder 项目中使用。
【参考方案1】:
这似乎是 BCC32C 编译器中的错误。我猜它没有正确扩展来处理Extended
,因为 Delphi 需要它。
如果你查看 CPU 窗口,那么 BCC32 编译器会生成:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B8F 6802400000 push $00004002
00401B94 68000000A0 push $a0000000
00401B99 6A00 push $00
00401B9B 6804400000 push $00004004
00401BA0 68000000A0 push $a0000000
00401BA5 6A00 push $00
00401BA7 E80C000000 call Unit12::SetVal(long double,long double)
这是正确的。它首先推送10
,然后以Extended
格式推送40
。注意每个Extended
在栈上占用12个字节。
但现在看看 BCC32C 编译器的输出:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B5D 89E0 mov eax,esp
00401B5F D90554F14E00 fld dword ptr [$004ef154]
00401B65 DB38 fstp tbyte ptr [eax]
00401B67 D90558F14E00 fld dword ptr [$004ef158]
00401B6D DB780A fstp tbyte ptr [eax+$0a]
00401B70 E81F000000 call Unit12::SetVal(long double,long double)
00401B75 83EC18 sub esp,$18
它首先读取 32 个单精度浮点数 40
并将其存储为 Extended
[ESP]
。到现在为止还挺好。但随后它会读取下一个 32 位单精度浮点数 10
(仍然可以),然后 将其存储在 [ESP+$0A]
,这显然是错误的(对于 Delphi)!它应该存储在[ESP+$0C]
!这就是为什么 first 值是错误的,Pascal 函数在 [ESP+$0C]
处读取,但 BCC32C 将其存储在 [ESP+$0A]
处。
所以这似乎是一个错误。举报为https://quality.embarcadero.com/browse/RSP-15737
请注意,这是 BCC32C 推送并期望此类值的正常方式。在同一模块中的 C++ 函数中,即也使用 BCC32C 编译,这很好用:
void __fastcall Bla(long double a, long double b)
printf("%Lf %Lf\n", a, b);
但是 Delphi 期望一个 10 字节的 Extended
占用堆栈上的 12 个字节,而不是像 BCC32C 那样的 10 个字节。
很奇怪,如果要调用的函数不是 Delphi __fastcall
函数,而是普通的 C++ (cdecl
) 函数,BCC32C 编译器将存储 Extended
s (long double
s ) 分别在[ESP+$0C]
和[ESP]
中。
解决方法
正如 David Heffernan 评论的那样,您可以在记录中传递多个扩展。或者,您可以将它们作为var
参数传递。这两种情况,都不是调用SetVal(10.0, 40.0);
那么简单。
【讨论】:
@David:包装确实会得到正确的对齐。 @David:很遗憾,包装不是一种选择。这个库真的很大,有数百个类,其中许多是派生类、它们之间的链接、接口等。构建一个包装器确实需要付出巨大的努力。我会考虑购买源并用另一种类型替换扩展类型。感谢您的帮助。 什么是图书馆不感兴趣? 我知道这是一个延伸,但你可以用 Delphi(可能更容易)或 C++ 的内置汇编器编写一个函数,正确对齐Extended
,然后调用你想要的函数直接调用。那么你可以称它为CallWith2Extendeds(&Unit12::SetValue, 10.0L, 40.0L);
。而不是SetValue(10.0L, 40.0L);
。
终于解决了。我可以直接使用 f->Height、f->Width 等属性,而不是调用 f->SetSize(Height, Width) 等因扩展对齐而失败的函数。谢谢你。你拯救了我的一周!。以上是关于使用扩展参数调用 Delphi 函数时出现 C++ 错误的主要内容,如果未能解决你的问题,请参考以下文章
构建 Numpy C++ 扩展;调用 PyArray_FROM_OTF 时出现段错误
我的delphi7在调试程序时出现问题啦,下面是截图,哪位大侠帮帮忙啊
在 Python 和 C++ 中使用 ctypes 时出现 Seg 错误