2014-06-18 41 views
4

爲什麼我收到爲什麼編譯器堅持我的函數是內聯的呢?

[DCC錯誤] ProjectCOWArray.dpr(23):E2426聯函數不能有ASM塊

program ProjectCOWArray; 

{$APPTYPE CONSOLE} 

{$R *.res} 

type 
    PRefCount = ^TRefCount; 
    TRefCount = array[0..1] of integer; 


    TCOWArray<T> = record 
    private 
    fData: TArray<T>; 
    private 
    procedure IncRefCount; <<-- not inline 
    end; 


{ TCOWArray<T> } 

procedure TCOWArray<T>.IncRefCount; 
asm 
    {$if defined(win32)} 
    mov eax,fData; 
    lock inc dword ptr [eax - 8]; 
    {$ifend} 
    {$if defined(win64)} 
    mov rax,fData; 
    lock inc dword ptr[rax -12]; 
    {$ifend} 
end; 

begin 
end. 

德爾福XE2沒有AtomicIncrement,所以怎麼辦我解決了這個問題?
它想保留彙編程序,否則我不能在lock前綴,我不想使用InterlockedIncrement,因爲這是一個WinAPI函數,我不希望這種開銷。

+0

也許你應該選擇什麼是你對問題 - 編譯「內聯彙編錯誤」或用於互鎖遞增替代搜索。 – Kromster

+2

很顯然,問題是關於編譯器消息@Krom。有關增量代碼的部分是搶佔題外評論,詢問他爲什麼在第一個地方使用匯編程序而不是使用API​​。 –

+0

FWIW我不明白你爲什麼想直接干預裁判計數。沒有必要這樣做。 –

回答

6

這是因爲泛型功能是在內聯引擎的頂部實現的。適用於內聯函數的相同限制適用於泛型函數。編譯器編寫者只是沒有采取額外的步驟來使錯誤信息專用於泛型而不是內聯函數。

我認爲調用InterlockedIncrement可能是您的最佳選擇,對於沒有AtomicIncrement內在的Delphi版本。或者,也可以創建您自己的AtomicIncrement版本,該版本僅在Delphi不包含它的版本中定義。並且該函數可以用asm編寫。那麼,它顯然必須用asm編寫。

{$IFNDEF AtomicFunctionsAvailable} 
function AtomicIncrement(var Target: Integer): Integer; 
asm 
    .... 
end; 
{$ENDIF} 

或者作爲@TLama建議,您可以使用TInterlockedSystem.SyncObjs單位提供的原子操作。

說了這麼多話,我就沒有必要用這種方式干涉內部。無論何時寫入數組,都要調用SetLength(...)來實現寫入數組的副本。舉例來說,這裏的一對寫入陣列實現一個非常簡單的複製:

unit COWArray; 

interface 

type 
    TCOWArray<T> = record 
    private 
    FItems: TArray<T>; 
    function GetLength: Integer; 
    procedure SetLength(Value: Integer); 
    function GetItem(Index: Integer): T; 
    procedure SetItem(Index: Integer; const Value: T); 
    public 
    class function New(const Values: array of T): TCOWArray<T>; static; 
    property Length: Integer read GetLength write SetLength; 
    property Items[Index: Integer]: T read GetItem write SetItem; default; 
    end; 

implementation 

function TCOWArray<T>.GetLength: Integer; 
begin 
    Result := System.Length(FItems); 
end; 

procedure TCOWArray<T>.SetLength(Value: Integer); 
begin 
    System.SetLength(FItems, Value); // SetLength enforces uniqueness 
end; 

function TCOWArray<T>.GetItem(Index: Integer): T; 
begin 
    Result := FItems[Index]; 
end; 

procedure TCOWArray<T>.SetItem(Index: Integer; const Value: T); 
begin 
    System.SetLength(FItems, System.Length(FItems)); // SetLength enforces uniqueness 
    FItems[Index] := Value; 
end; 

class function TCOWArray<T>.New(const Values: array of T): TCOWArray<T>; 
var 
    i: Integer; 
begin 
    System.SetLength(Result.FItems, System.Length(Values)); 
    for i := 0 to high(Values) do 
    Result.FItems[i] := Values[i]; 
end; 

end. 
+0

是的,'SetLength()'完成這項工作。 –

+0

@LURD我知道你以前做過COW工作。這是廣泛的你如何處理它? –

+2

是的,每一次嘗試寫入內部的'FItems'數組必須調用'SetLength'。沒有必要深入挖掘,以獲得更快的性能。 –

-1

感謝大衛解釋所有重要的爲什麼

一個可能的解決方案是解除AtomicIncrement出通用的記錄,併成爲一個黑客攻擊。

program ProjectCOWArray; 

{$APPTYPE CONSOLE} 

{$R *.res} 

type 
    PRefCount = ^TRefCount; 
    TRefCount = array[0..1] of integer; 

    TCOWArray<T> = record 
    private 
    fData: TArray<T>; 
    private 
    procedure IncRefCount; 
    end; 

{ TCOWArray<T> } 

type TAnyOldDynArray = array of integer; 

procedure AtomicIncrementDynArrayRefCount(const AnArray: TAnyOldDynArray); 
asm 
    {$if defined(win32)} 
    mov eax,AnArray; 
    lock inc dword ptr [eax - 8]; 
    {$else} 
    mov rax,AnArray; 
    lock inc dword ptr [rax -12]; 
    {$ifend} 
end; 

procedure TCOWArray<T>.IncRefCount; 
begin 
    AtomicIncrementDynArrayRefCount(TAnyOldDynArray(fData)); 
end; 

begin 
end. 
4

即使你可以打電話InterlockedIncrement,你仍然必須實現對數組的引用計數字段的引用的障礙。不過,你並不需要它。相反,您可以讓編譯器爲您完成工作。聲明另一個數組變量並分配給它以遞增計數。然後鍵入 - 將其轉換爲非託管類型並將其設置回空指針。即使變量超出範圍,引用計數也將保持不變。

var 
    dummy: TArray<T>; 
begin 
    dummy := fData; // increment reference count 
    Pointer(dummy) := nil; // circumvent decrement 
end; 

該技術適用於遞增任何事物的引用計數,包括數組,字符串和接口。

要減小隻有一行:

begin 
    Pointer(dummy) := fData; 
    // reference count decrements automatically at scope's end 
end; 
+0

哇羅布,那很美。一個更好的黑客。 – Johan

相關問題