works:programmer:delphi:varargs
Обработка varargs в Delphi
Вообще по натуральному это лучше сделать через перегрузку метода (overload), но бывают редкие случаи когда это сделать невозможно по той причине что перегрузка всё-же требует типизации параметров, по которой в конце концов компилятор поймёт к какому методу обращаться. Не всегда это возможно, например если данные приходят из вне программы или имеют запутанный тип данных.
Есть ещё `array of const`, но у того тоже не всегда всё гладко.
Что-же делать? В особых случаях нам на помощь может прийти старый добрый СИшный vaargs.
{$apptype console} type TArgPtr = record private FArgPtr: PByte; class function Align(Ptr: Pointer; Align: Integer): Pointer; static; public constructor Create(LastArg: Pointer; Size: Integer); // Read bytes, signed words etc. using Int32 // Make an unsigned version if necessary. function ReadInt32: Integer; // Exact floating-point semantics depend on C compiler. // Delphi compiler passes Extended as 10-byte float; most C // compilers pass all floating-point values as 8-byte floats. function ReadDouble: Double; function ReadExtended: Extended; function ReadPChar: PChar; procedure ReadArg(var Arg; Size: Integer); end; constructor TArgPtr.Create(LastArg: Pointer; Size: Integer); begin FArgPtr := LastArg; // 32-bit x86 stack is generally 4-byte aligned FArgPtr := Align(FArgPtr + Size, 4); end; class function TArgPtr.Align(Ptr: Pointer; Align: Integer): Pointer; begin Integer(Result) := (Integer(Ptr) + Align - 1) and not (Align - 1); end; function TArgPtr.ReadInt32: Integer; begin ReadArg(Result, SizeOf(Integer)); end; function TArgPtr.ReadDouble: Double; begin ReadArg(Result, SizeOf(Double)); end; function TArgPtr.ReadExtended: Extended; begin ReadArg(Result, SizeOf(Extended)); end; function TArgPtr.ReadPChar: PChar; begin ReadArg(Result, SizeOf(PChar)); end; procedure TArgPtr.ReadArg(var Arg; Size: Integer); begin Move(FArgPtr^, Arg, Size); FArgPtr := Align(FArgPtr + Size, 4); end; procedure Dump(const types: string); cdecl; var ap: TArgPtr; cp: PChar; begin cp := PChar(types); ap := TArgPtr.Create(@types, SizeOf(string)); while True do begin case cp^ of #0: begin Writeln; Exit; end; 'i': Write(ap.ReadInt32, ' '); 'd': Write(ap.ReadDouble, ' '); 'e': Write(ap.ReadExtended, ' '); 's': Write(ap.ReadPChar, ' '); else Writeln('Unknown format'); Exit; end; Inc(cp); end; end; type PDump = procedure(const types: string) cdecl varargs; var MyDump: PDump; function AsDouble(e: Extended): Double; begin Result := e; end; function AsSingle(e: Extended): Single; begin Result := e; end; procedure Go; begin MyDump := @Dump; MyDump('iii', 10, 20, 30); MyDump('sss', 'foo', 'bar', 'baz'); // Looks like Delphi passes Extended in byte-aligned // stack offset, very strange; thus this doesn't work. MyDump('e', 2.0); // These two are more reliable. MyDump('d', AsDouble(2)); // Singles passed as 8-byte floats. MyDump('d', AsSingle(2)); end; begin Go; end.
Пример получен с stackoverflow.
works/programmer/delphi/varargs.txt · Последнее изменение: 2024/09/05 03:55 — tuxapuk