Can I force `const` to pass by reference (aka the missing `in` parameter)

匿名 (未验证) 提交于 2019-12-03 08:39:56

问题:

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?

回答1:

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.



回答2:

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!