2013-10-02 1 views
5

나는 다음과 같은 오류가 발생합니다 '매개 변수 목록은 다르다'. 여기 E2009 호환되지 않는 유형 : 나는 차이를 볼 수있는 정의를보고,</p> <blockquote> <p>E2009 Incompatible types: 'Parameter lists differ'</p> </blockquote> <p>그러나 나는 동의 :

Looks the same to me...

레코드 정의입니다 :

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

그리고 여기 할당 할 MOD 함수입니다 :

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 
:

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

다음 할당 오류를 발행

내 코드가 잘못되었습니다.

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가 열악합니다. 그것은 짧지도 자기 포함되지도 않습니다. 이것은 실제로 중요합니다. 데모 코드를 가능한 한 짧게 만드는 것이 자주 문제를 이해하는 데 도움이됩니다. 그것은 분명히 여기에 해당됩니다.

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과 함께 컴파일에 실패 :

은 여기 SSCCE에 내 걸릴입니다. 여러 가지 방법으로 컴파일 할 수 있습니다. 예를 들어, 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 레코드 매개 변수는 값 또는 참조로 전달됩니다. 레코드가 레지스터에 맞을만큼 작 으면 값으로 전달됩니다.

컴파일러에서 레코드를 처리 할 때 레코드 크기를 완전히 확정하지 않았습니다. 나는 내부적으로 컴파일러에서 레코드의 크기를 포함하는 변수가 있다고 추측합니다. 레코드의 선언이 완료 될 때까지이 크기 변수가 0이라고 가정합니다. 따라서 컴파일러는 레코드 유형의 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; 

이 E2086 유형 'TComplex는'아직 완전하게 정의되지와 Zero의 선언에 컴파일에 실패 예를 들어,이 유형을 고려하십시오.

또 다른 제한 사항은 유형 A가 유형 B를 참조 할 수 없으며 그 반대도 마찬가지입니다. 클래스에 대해서는 선언을 할 수 있지만 레코드는 선언 할 수 없습니다. 나는 이것을 지원하기 위해 컴파일러 구현을 수정해야한다는 것을 이해하지만 달성하기는 확실히 가능하다.

그리고 더 많은 것이 있습니다. 레코드 상속을 허용 할 수없는 이유는 무엇입니까? 나는 다형성을 원하지 않으며, 단지 데이터 멤버와 레코드의 메소드를 상속 받기를 원합니다. 그리고 난 심지어 당신이 클래스와 함께 얻을 행동입니다 필요가 없습니다. TDerivedRecordTBaseRecord이 아니라면 괜찮습니다. 내가 원하는 것은 중복을 피하기 위해 회원과 기능을 상속받는 것뿐입니다.

슬프게도, 이것은 내 마음에 90 % 완성 된 기능이며 완성까지 걸리는 부드러운 사랑의 보살핌을 놓치고 있습니다.

+0

완벽하게 설명되어 있습니다. Remy의 대답은 실제로 '데이터 멤버'와 '데이터 멤버가 없음'에서 '프로그램'대 '유닛'이라는 구별이 있다고 생각하면서 오프 트랙으로 전환되었습니다. 당신은 방금 내 하루를 보냈습니다 : -). – 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 //     ^^^ 

문제는 해결됩니다.

varconst으로 다시 변경하면 문제가 다시 발생합니다.