11

Delphi has:

var : pass by reference; parameter is both input and output.
out : pass by reference; parameter is output only.
const: pass by ..... well it depends; parameter is input only.
in : pass by reference; parameter is input only and will not be changed there is no "in".

I don't mind that there is no spoon, but I miss in; considering the following code, is there a cleaner way of doing this?

type TFastDiv = record 
strict private
  FBuffer: Int64;
  other fields
....

//Must be `var` because `const` would pass a Int64 by value
//                      |||
//                      VVV
function DivideFixedI32(var Buffer: Int64; x: integer): integer;
asm  
  mov  r9,rcx                   
  imul dword ptr[rcx]    // do stuff with the buffer
  ..
  mov     ecx, [r9+4]    // do other stuff with the rest of the buffer  

{Changing the code to imul ecx;...;shr r9,32;mov ecx,r9d would allow pass by value, but let's assume the code must not be changed.}

class operator TFastDiv.IntDivide(x:integer; const buffer:TFastDiv):integer;
begin
  Result:= DivideFixedI32(Int64((@buffer.FBuffer)^), abs(x)); <<-- Ugly
  if (x < 0) then Result:= - Result;
end;

DivideFixed will never change the buffer. The whole point of the routine is that buffer is a precalculated value that does not change.

In the class operator I declare buffer as const, because the record must not change.

The question is:
If I insist on declaring the buffer parameter in IntDivide as const is there a cleaner way of coding or am I stuck in the pointer_to/points_to hack?

Johan
  • 74,508
  • 24
  • 191
  • 319
  • What is `in`? What you are really missing is C++ `const MyType& value` – David Heffernan Sep 30 '13 at 12:41
  • `in` would be the opposite of `out`, pass by reference but parameter is guaranteed to not change; and yes, a pointer (aka pass_by_reference) that will not be changed inside the routine. – Johan Sep 30 '13 at 12:46

2 Answers2

15

Newer compiler versions (from XE3 onwards) support the [Ref] decorator:

procedure Foo(const [Ref] Arg1: Integer; [Ref] const Arg2: Byte);

Example adapted from the documentation, which emphasises the [Ref] can go either before or after the const keyword.

Johan
  • 74,508
  • 24
  • 191
  • 319
Chris Rolliston
  • 4,788
  • 1
  • 16
  • 20
6

The only option (pre Delphi XE3) if you want to ensure pass-by-reference, is to pass something big.
i.e. bigger than sizeof(pointer)

type TFastDiv = record 
strict private
  FBuffer: Int64;   <<-- make sure this is the first member
  other fields
....

function DivideFixedI32(const Buffer: TFastDiv; x: integer): integer;
...
class operator TFastDiv.IntDivide(x:integer; const buffer:TFastDiv):integer;
begin
  Result:= DivideFixedI32(buffer, abs(x));

This line in the Delphi help file:

Sets, records, and static arrays of 1, 2, or 4 bytes are passed as 8-bit, 16-bit, and 32bit values. Larger sets, records, and static arrays are passed as 32-bit pointers to the value. An exception to this rule is that records are always passed directly on the stack under the cdecl, stdcall, and safecall conventions; the size of a record passed this way is rounded upward to the nearest double-word boundary.

is misleading and should be changed to/read as:

Sets, records, and static arrays up to SizeOf(pointer) are passed as 8-bit, 16-bit, and 32bit values (64 bit values on x64). Sets, records, and static arrays larger than SizeOf(Pointer) are passed as pointers to the value. An exception to this rule is that records are always passed directly on the stack under the cdecl, stdcall, and safecall conventions; the size of a record passed this way is rounded upward to the nearest SizeOf(pointer) boundary.

LU RD
  • 34,438
  • 5
  • 88
  • 296
Johan
  • 74,508
  • 24
  • 191
  • 319