我收到以下错误:
E2009不兼容的类型:'参数列表不同'
但是我不同意,看看定义,我看不出任何区别。
这是记录定义:
type
TFastDiv = record
private
...
DivideFunction: function (const Buffer: TFastDiv; x: integer): integer;
这是我要分配的Mod功能:
function dividefixedi32(const Buffer: TFastDiv; x: integer): integer;
asm
以下分配发出错误:
class operator TFastDiv.Implicit(a: integer): TFastDiv;
begin
if (a = 0) then begin
raise EDivByZero.Create('Setting a zero divider is a division by zero error')
at ReturnAddress;
end;
Result.FSign:= Math.sign(a);
case Result.FSign of
-1: begin
SetDivisorI32(Result, a);
Result.DivideFunction:= dividefixedi32; <<-- error E2009
我的代码出了什么问题?
unit SSCCE;
interface
uses Math;
type
TFastDiv = record
private
FBuffer: UInt64; // The reciprocal of the divider
FDivider: integer; // The divider itself (need with modulus etc).
FSign: TValueSign;
DivideFunction: function (const Buffer: TFastDiv; x: integer): integer;
ModFunction: function (const Buffer: TFastDiv; x: integer): integer;
public
class operator Implicit(a: integer): TFastDiv;
end;
implementation
uses SysUtils;
function dividefixedi32(const Buffer: TFastDiv; x: integer): integer; forward;
class operator TFastDiv.Implicit(a: integer): TFastDiv;
begin
if (a = 0) then begin raise EDivByZero.Create('Setting a zero divider is a division by zero error') at ReturnAddress; end;
Result.FSign:= Math.sign(a);
case Result.FSign of
-1: begin
//SetDivisorI32(Result, a);
Result.DivideFunction:= dividefixedi32;
end; {-1:}
1: begin
//SetDivisorU32(Result.FBuffer, a);
end; {1:}
end; {case}
Result.FDivider:= a;
end;
function dividefixedi32(const Buffer: TFastDiv; x: integer): integer;
asm
mov eax, edx
mov r8d, edx // x
mov r9, rcx // Buffer
imul dword [r9] // m
lea eax, [rdx+r8] // r8 = r8 or rsi
mov ecx, [r9+4] // shift count
sar eax, cl
sar r8d, 31 // sign(x)
sub eax, r8d
ret
end;
end.
答案 0 :(得分:5)
首先,一些一般性建议。你的SSCCE很差。它既不短也不自足。这实际上非常重要。使演示代码尽可能短,可以帮助您理解问题。这绝对是这种情况。
这是我对SSCCE的看法:
program soq19147523_version1;
type
TRecord = record
data: Integer;
proc: procedure(const rec: TRecord);
end;
procedure myproc(const rec: TRecord);
begin
end;
procedure foo;
var
rec: TRecord;
begin
rec.proc := myproc; // fail, E2009
end;
begin
end.
无法使用E2009进行编译。你可以通过多种方式编译它。例如,删除data
成员会导致编译成功。
program soq19147523_version2;
type
TRecord = record
proc: procedure(const rec: TRecord);
end;
procedure myproc(const rec: TRecord);
begin
end;
procedure foo;
var
rec: TRecord;
begin
rec.proc := myproc; // compiles
end;
begin
end.
在XE3中,您可以通过将[ref]
属性添加到过程类型的参数来进行编译。为了明确,这在XE3中编译:
program soq19147523_version3;
type
TRecord = record
data: Integer;
proc: procedure(const [ref] rec: TRecord);
end;
procedure myproc(const [ref] rec: TRecord);
begin
end;
procedure foo;
var
rec: TRecord;
begin
rec.proc := myproc; // compiles in XE3, no [ref] in XE2
end;
begin
end.
这为我们提供了关于编译器正在做什么的强有力线索。未修饰的const
记录参数按值或按引用传递。如果记录足够小以适合寄存器,它将按值传递。
当编译器处理记录时,它还没有完全确定记录的大小。我猜测在编译器内部有一个包含记录大小的变量。在记录声明完成之前,我认为这个大小变量为零。因此编译器决定记录类型的const
参数将通过寄存器中的值传递。遇到过程myproc
时,记录的真实大小是已知的。它不适合寄存器,因此编译器识别出不匹配。记录中的类型按值接收其参数,但是为分配提供的参数通过引用传递参数。
实际上,您可以从[ref]
的声明中删除myproc
,程序仍会编译。
这也解释了为什么您发现使用var
参数导致编译成功。这显然会强制参数通过引用传递。
如果你可以转移到XE3或更高版本,那么解决方案是显而易见的:使用[ref]
来强制编译器。
如果您无法转移到XE3,那么可能无类型const
参数是最佳解决方案。这也会强制编译器通过引用传递参数。
program soq19147523_version4;
type
TRecord = record
data: Integer;
proc: procedure(const rec{: TRecord});
end;
procedure myproc(const rec{: TRecord});
begin
Writeln(TRecord(rec).data);
end;
procedure foo;
var
rec: TRecord;
begin
rec.proc := myproc;
end;
begin
end.
我在Stack Overflow上发帖的经常读者会知道我是操作符重载值类型记录的主要支持者。我广泛使用此功能,它可以生成高效且高度可读的代码。但是,当你开始用更复杂和相互依赖的类型进行努力时,设计和实现就会崩溃。
这个问题中突出的缺陷是一个很好的例子。期望编译器可以处理这种情况并不罕见。期望一种类型能够引用自身是非常合理的。
实现让程序员失望的另一个例子是当你希望在该记录中放入const
记录类型时。例如,请考虑以下类型:
type
TComplex = record
public
R, I: Double;
const
Zero: TComplex = (R: 0.0, I: 0.0);
end;
无法在Zero
的声明中编译,而E2086类型'TComplex'尚未完全定义。
另一个限制是类型A无法引用类型B,反之亦然。我们可以为类做出前向声明,但不能为记录做出声明。我知道需要修改编译器实现来支持这一点,但它当然有可能实现。
还有更多。为什么不能允许继承记录?我不想要多态,我只想继承记录的数据成员和方法。而且我甚至不需要是一种行为。如果TDerivedRecord
不是TBaseRecord
,我不介意。我想要的只是继承成员和函数以避免重复。
可悲的是,在我看来,这是一项已经完成了90%的功能,并且只是缺少完成它所需要的温柔,爱护。
答案 1 :(得分:2)
如果我更改代码:
记录定义
type
TFastDiv = record
private
...
DivideFunction: function (var Buffer: TFastDiv; x: cardinal): cardinal;
^^^
功能定义
function dividefixedu32(var Buffer: TFastDiv; x: Cardinal): cardinal; // unsigned
asm // ^^^
问题也消失了。
请注意,如果我将var
更改回const
,则问题会再次出现。