2013-10-02 51 views
5

我得到以下錯誤:E2009不兼容的類型:「參數列表不同,」

E2009 Incompatible types: 'Parameter lists differ'

但是我不同意,看我看不出有什麼區別的定義。

Looks the same to me...

這裏的記錄定義:

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 

我的代碼有什麼問題?

SSCCE

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. 
+1

當我嘗試它時適合我。沒有看到[SSCCE](http:// sscce。org),我的猜測是,可能存在多個相互衝突的'TFastDiv'類型。 –

+0

沒有隻有TFastDiv記錄定義(通過查找文件進行確認)。 – Johan

+0

@RemyLebeau,我已經添加了一個SSCCE – Johan

回答

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時,記錄的真實大小是已知的。它不適合寄存器,因此編譯器會識別出不匹配。記錄中的類型按值接收其參數,但是爲賦值提供的參數通過引用傳遞參數。

的確,您可以從myproc聲明中刪除[ref],並且該程序仍在編譯。

這也解釋了爲什麼您發現使用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. 

經常在這裏堆棧溢出我的帖子的讀者都知道,我的運算符重載價值類型記錄的堅定支持者。我廣泛使用這個特性,並且它產生了高效且高度可讀的代碼。但是,當你開始用更復雜和相互依存的類型來努力時,設計和實現就會崩潰。

這個問題突出的缺陷就是一個很好的例子。期望編譯器能夠處理這個問題並不罕見。期望類型能夠引用自己是非常合理的。

另一個實現讓程序員關閉的例子是當你希望在該記錄中輸入一個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%的功能,只是缺少了完成它所需的溫柔和愛心的關懷。

+0

這完美地解釋了它。雷米的回答使我偏離了軌道,認爲這個區別在'程序'和'單元'之間,實際上它是'數據成員'和'沒有數據成員'。你剛剛做了我的一天: - |),謝謝。 – Johan

+0

似乎也可以使用無類型的賦值'rec.proc:= @ myproc'({$ T-})。編譯器可能不太喜歡它,但偶爾會導致內部錯誤。 –

+0

@SertacAkyuz我不知道編譯器是通過ref還是通過值來通過 –

2

解決方法

如果我改變,像這樣的代碼:

記錄定義

type 
    TFastDiv = record 
    private 
    ... 
    DivideFunction: function (var Buffer: TFastDiv; x: cardinal): cardinal; 
           ^^^ 

功能definitio n

function dividefixedu32(var Buffer: TFastDiv; x: Cardinal): cardinal; // unsigned 
asm //     ^^^ 

問題也消失了。

請注意,如果我將var更改回const,問題會再次出現。