Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler insist my function is inline when it's not?

Why am I getting

[DCC Error] ProjectCOWArray.dpr(23): E2426 Inline function must not have asm block

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.

Delphi XE2 does not have AtomicIncrement, so how do I solve this issue?
It'd like to retain the assembler, because otherwise I cannot have the lock prefix in and I do not want to use InterlockedIncrement because that's a WinAPI function and I do not want that kind of overhead.

like image 646
Johan Avatar asked Jan 10 '23 09:01

Johan


1 Answers

This is because the generics functionality is implemented on top of the inlining engine. The same restrictions that apply to inline functions apply to generic functions. The compiler writers just haven't taken the extra step to make the error messages specific to generics rather than inline functions.

I think that calling InterlockedIncrement is probably your best option, for Delphi versions that don't have the AtomicIncrement intrinsic. Or, alternatively, create your own version of AtomicIncrement, that is only defined in versions of Delphi that do not include it. And that function can be written in asm. Well, it clearly has to be written in asm of course.

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

Or as @TLama suggests, you can use TInterlocked from the System.SyncObjs unit to provide atomic operations.

With all that said, I see no need to meddle with the internals in this way. Implement a copy of write array by calling SetLength(...) whenever you write to the array. For instance, here's a very simple copy on write array implementation:

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.
like image 52
David Heffernan Avatar answered Feb 12 '23 00:02

David Heffernan