2013-10-02 6 views
5

ottengo il seguente errore:E2009 tipi incompatibili: 'liste di parametri differiscono'

E2009 Incompatible types: 'Parameter lists differ'

Comunque io non sono d'accordo, guardando le definizioni che posso vedere alcuna differenza.

Looks the same to me...

Ecco la definizione di record:

type 
    TFastDiv = record 
    private 
    ... 
    DivideFunction: function (const Buffer: TFastDiv; x: integer): integer; 

Ed ecco la funzione Mod voglio assegnare:

function dividefixedi32(const Buffer: TFastDiv; x: integer): integer; 
asm 

La seguente assegnazione emette l'errore:

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 

Cosa c'è di sbagliato nel mio codice?

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

Funziona bene per me quando lo provo. Senza vedere un [SSCCE] (http: // sscce.org), suppongo che ci siano probabilmente più tipi 'TFastDiv' in ambito che sono in conflitto tra loro. –

+0

No, c'è solo sulla definizione del record TFastDiv (confermata con find in files). – Johan

+0

@RemyLebeau, ho aggiunto un SSCCE – Johan

risposta

5

Prima di tutto, qualche consiglio generale. Il tuo SSCCE è povero. Non è né corto né autonomo. Questo è in realtà piuttosto importante. Rendere il codice dimostrativo il più breve possibile aiuta spesso a capire il problema. Questo è sicuramente il caso qui.

Ecco il mio prendere su uno 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. 

Questo non riesce a compilare con E2009. Puoi farlo compilare in diversi modi. Ad esempio, la rimozione dei risultati del membro data nella compilazione riuscita.

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. 

In XE3 si può fare compilare aggiungendo l'attributo [ref] al parametro di tipo procedurale. Per essere espliciti, questo compila in 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. 

Questo ci dà un forte indizio su ciò che il compilatore sta facendo. Un parametro record const non decorato viene passato per valore o per riferimento. Se il record è abbastanza piccolo da inserirsi in un registro, verrà passato per valore.

Quando il compilatore sta elaborando il record, non ha completato completamente le dimensioni del record. Immagino che internamente al compilatore ci sia una variabile contenente la dimensione del record. Finché la dichiarazione del record non è completa, suppongo che questa variabile di dimensione sia zero. Quindi il compilatore decide che il parametro const del tipo di record verrà passato in base al valore in un registro. Quando viene rilevata la procedura myproc, è nota la dimensione reale del record. Non si adatta a un registro e quindi il compilatore riconosce una discrepanza. Il tipo nel record riceve il suo parametro in base al valore, ma quello che viene offerto per l'assegnazione passa il parametro per riferimento.

In effetti, è possibile rimuovere [ref] dalla dichiarazione di myproc e il programma si compila ancora.

Questo spiega anche perché è stato rilevato che l'utilizzo di un parametro var ha comportato una compilazione corretta. Ciò ovviamente costringe il parametro ad essere passato per riferimento.

Se è possibile passare a XE3 o versione successiva, la soluzione è ovvia: utilizzare [ref] per forzare la mano del compilatore.

Se non è possibile passare a XE3, forse un parametro non codificato const rappresenta la soluzione migliore. Questo costringe anche il compilatore a passare il parametro per riferimento.

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. 

lettori abituali dei miei post qui su Stack Overflow sapranno che io sono un grande sostenitore di overloading degli operatori sui record tipo di valore. Uso questa funzionalità estesamente e risulta in un codice efficiente e altamente leggibile. Tuttavia, quando si inizia a spingere forte con tipi più complessi e interdipendenti, la progettazione e l'implementazione si rompono.

Il difetto evidenziato in questa domanda è un buon esempio. Non è inusuale aspettarsi che il compilatore possa gestirlo. È molto ragionevole aspettarsi che un tipo possa riferirsi a se stesso.

Un altro esempio in cui l'implementazione consente al programmatore di scendere quando si desidera inserire un const del tipo di record in tale record. Ad esempio, si consideri questo tipo:

type 
    TComplex = record 
    public 
    R, I: Double; 
    const 
    Zero: TComplex = (R: 0.0, I: 0.0); 
    end; 

Questo non riesce a compilare alla dichiarazione di Zero con E2086 Tipo 'TComplex' non è ancora completamente definito.

Un'altra limitazione è l'incapacità del tipo A di fare riferimento al tipo B e viceversa. Possiamo fare dichiarazioni in avanti per le classi, ma non per le registrazioni. Capisco che l'implementazione del compilatore avrebbe bisogno di essere modificata per supportare questo, ma è certamente possibile ottenere.

E c'è di più. Perché non è possibile consentire l'ereditarietà per i record? Non voglio il polimorfismo, voglio solo ereditare i membri dei dati e i metodi del record. E non ho nemmeno bisogno che lo sia un comportamento che si ottiene con le classi. Cioè non mi importa se TDerivedRecord non è un TBaseRecord. Tutto ciò che voglio è ereditare membri e funzioni per evitare la duplicazione.

Tristemente, a mio parere, questa è una funzionalità che è stata eseguita al 90% e manca semplicemente la tenera, amorevole cura necessaria per portarla a completamento.

+0

che lo spiega perfettamente. La risposta di Remy mi ha messo fuori strada, pensando che la distinzione fosse in "programma" vs "unità" quando in realtà era in "membri dati" vs "nessun membro dati". Hai appena fatto la mia giornata: - |), grazie. – Johan

+0

Sembra funzionare anche con un assegnamento non codificato 'rec.proc: = @ myproc' ({$ T-}). Il compilatore probabilmente non gli piace molto però, a volte causa un errore interno. –

+0

@SertacAkyuz Mi chiedo se il compilatore passa per ref o per valore quando lo fai –

2

Soluzione

Se cambio il codice in questo modo:

definizione record di

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

funzione definitio n

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

Anche il problema va via.

Si noti che se cambio lo var di nuovo in const il problema si ripresenta.