http://www.chami.com/tips/delphi/112696D.html

Sometimes it's necessary to pass undefined number of [different type] variables to a function

-- look at Format() function in Delphi and *printf() functions in C/C++ for example.

Once you analyze the following code, you'll be on your way to creating mysterious variable parameter functions...

//
// FunctionWithVarArgs()
//
// skeleton for a function that
// can accept vairable number of
// multi-type variables
//
// here are some examples on how
// to call this function:
//
// FunctionWithVarArgs(
// [ , True, , '', '' ] );
//
// FunctionWithVarArgs(
// [ 'one', ] );
//
// FunctionWithVarArgs( [] );
//
procedure FunctionWithVarArgs(
const ArgsList : array of const );
var
ArgsListTyped :
array[..$FFF0 div SizeOf(TVarRec)]
of TVarRec absolute ArgsList;
n : integer;
begin
for n := Low( ArgsList ) to
High( ArgsList ) do
begin
with ArgsListTyped[ n ] do
begin
case VType of
vtInteger : begin
{handle VInteger here} end;
vtBoolean : begin
{handle VBoolean here} end;
vtChar : begin
{handle VChar here} end;
vtExtended : begin
{handle VExtended here} end;
vtString : begin
{handle VString here} end;
vtPointer : begin
{handle VPointer here} end;
vtPChar : begin
{handle VPChar here} end;
vtObject : begin
{handle VObject here} end;
vtClass : begin
{handle VClass here} end;
vtWideChar : begin
{handle VWideChar here} end;
vtPWideChar : begin
{handle VPWideChar here} end;
vtAnsiString: begin
{handle VAnsiString here} end;
vtCurrency : begin
{handle VCurrency here} end;
vtVariant : begin
{handle VVariant here} end;
else begin
{handle unknown type here} end;
end;
end;
end;
end; //
// example function created using
// the above skeleton
//
// AddNumbers() will return the
// sum of all the integers passed
// to it
//
// AddNumbers( [, , ] )
// will return
//
//
function AddNumbers(
const ArgsList : array of const )
: integer;
var
ArgsListTyped :
array[..$FFF0 div SizeOf(TVarRec)]
of TVarRec absolute ArgsList;
n : integer;
begin
Result := ;
for n := Low( ArgsList ) to
High( ArgsList ) do
begin
with ArgsListTyped[ n ] do
begin
case VType of
vtInteger : Result := Result + VInteger;
end;
end;
end;
end;

http://stackoverflow.com/questions/8281436/how-can-i-create-a-function-with-an-arbitrary-number-of-parameters

I want to create a function that receive multiples strings as parameters. Like the function printf("Hello %s",name); of C. but I don't want to pass a ready array, it wouldn't be readable.

Edit1.text:=lang('Hello');

Edit2.text:=lang('Welcome to {1} guest',place);

Edit3.text:=lang('Hi {1}, is your {2} time in {3}','Victor','first','Disney');

output should be:

Hello
Welcome to Disney guest
Hi Victor is your first time in Disney

how I create the function TForm1.lang(parameters:String):String;, I did a research, but I can't get it work.

I need to access the parameters[] and the parameters.length also.

I'm needing this to turn my App to multilang.

Here's an example function of how you can do this:

function TForm1.lang(s: String; params: array of String): String;
var
i: Integer;
begin
for i := to High(params) do
begin
ShowMessage(params[i]);
end;
end;

Call it like this:

lang('My format string', ['this', 'that']);

or like this:

var
b: String;
begin
b := 'this';
lang('My format string', [b, 'that']);
end;
procedure DoSomething(args : Array of String);
Var
Index : Integer;
Begin
for index := Low(args) to High(args) Do
ShowMessage(args[Index]);
End; DoSomething(['Param1','Param2']);

How can a function with 'varargs' retrieve the contents of the stack?

Normally, in Delphi one would declare a function with a variable number of arguments using the 'array of const' method.

However, for compatibility with code written in C, there's an much-unknown 'varargs' directive

that can be added to a function declaration (I learned this while reading Rudy's excellent 'Pitfalls of convering' document).

As an example, one could have a function in C, declared like this :

void printf(const char *fmt, ...)

In Delphi, this would become :

procedure printf(const fmt: PChar); varargs;

My question is :

How can I get to the contents of the stack

when implementing a method which is defined with the 'varargs' directive?

I would expect that some tooling for this exists,

like Dephi translations of the va_start(), va_arg() and va_end() functions,

but I can't find this anywhere. Please help!

PS: Please don't drift off in discussions about the 'why'

or the 'array of const' alternative -

I need this to write C-like patches for functions inside Xbox games

(see the Delphi Xbox emulator project 'Dxbx' on sourceforge for details).

OK, I see the clarification in your question to mean that you need to implement a C import in Delphi.

In that case, you need to implement varargs yourself.

The basic knowledge needed is the C calling convention on the x86:

the stack grows downwards, and C pushes arguments from right to left.

Thus, a pointer to the last declared argument, after it is incremented by the size of the last declared argument,

will point to the tail argument list.

From then, it's simply a matter of reading the argument out and incrementing the pointer

by an appropriate size to move deeper into the stack.

The x86 stack in 32-bit mode is 4-byte aligned generally,

and this also means that bytes and words are passed as 32-bit integers.

Anyhow, here's a helper record in a demo program that shows how to read out data.

Note that Delphi seems to be passing Extended types in a very odd way;

however, you likely won't have to worry about that, as 10-byte floats aren't generally widely used in C,

and aren't even implemented in the latest MS C, IIRC.

{$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 -byte float; most C
// compilers pass all floating-point values as -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;
// -bit x86 stack is generally -byte aligned
FArgPtr := Align(FArgPtr + Size, );
end; class function TArgPtr.Align(Ptr: Pointer; Align: Integer): Pointer;
begin
Integer(Result) := (Integer(Ptr) + Align - ) and not (Align - );
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, );
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
#:
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', , , );
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());
// Singles passed as -byte floats.
MyDump('d', AsSingle());
end; begin
Go;
end.

pass unlimited number of paramters to procedure

in Delphi the procedure write can handle:

write(TF,st1)

and

write(TF,st1,st2,st3,st4);

I want to declare a procedure that can also do that, what is the syntax?

and the option of:

write(TF,[st1,st2,st3])

is less desirable, thow i know how to do that.

the main purpose was to pass ShortStrings into function, that would make a read call from file,

and would read at the length of the shortString as defined. however after passing it

as variant or in open array the shortstring loses its "size" and become 255,

which making this pass unusable, for me.

but the answer is still got if you want to pass open array.

First of all Inc and Write are bad examples because they both get special treatment from the compiler.

You can't write a function that behaves exactly like those two do yourself. There are alternatives you should investigate.

Take a look at overloads

You can create multiple versions of your method using varying number of parameters, and varying types. Something like this:

procedure MyInc(var i:Integer); overload;
procedyre MyInc(var i:Integer; const N:Integer); overload;
procedure MyInc(var i:Integer; const N1, N2: Integer); overload;
procedure MyInc(var i:Integer; const N1, N2, N3: Integer):overload;

This is feasible if the required number of overloads is not that large.

The compiler would probably handle lots of overloads easily,

but you'd probably not want to write them.

When the number of overloads becomes a problem you can switch to arrays:

Using Open Arrays as parameters

A function can take a parameter of type array of YourType,

and when you call that function you can pass as many parameters as you might need:

procedure MyInc(var i:Integer; Vals: array of Integer);

And then use it like this:

MyInc(i, []); // no parameters
MyInc(i, []);
MyInc(i, [, , , ]);

Just to complement Cosmin's answer: if the list of parameters are of different types,

you could use an variant open array parameter (also know as "array of const").

Example (from documentation):

function MakeStr(const Args: array of const): string;
var
I: Integer;
begin
Result := '';
for I := to High(Args) do
with Args[I] do
case VType of
vtInteger: Result := Result + IntToStr(VInteger);
vtBoolean: Result := Result + BoolToStr(VBoolean);
vtChar: Result := Result + VChar;
vtExtended: Result := Result + FloatToStr(VExtended^);
vtString: Result := Result + VString^;
vtPChar: Result := Result + VPChar;
vtObject: Result := Result + VObject.ClassName;
vtClass: Result := Result + VClass.ClassName;
vtAnsiString: Result := Result + string(VAnsiString);
vtCurrency: Result := Result + CurrToStr(VCurrency^);
vtVariant: Result := Result + string(VVariant^);
vtInt64: Result := Result + IntToStr(VInt64^);
end;
end;

For ilustrative purposes only:

Delphi supports a way of writing "real" variable arguments functions,

but it is really cumbersome and intended for use mainly for

declaring external C functions with variable arguments like printf,

as it involves playing some low-level dirty tricks for accessing the arguments in the stack.

It involves using cdecl and varargs modifiers:

procedure MyWrite_; cdecl;
begin
... some magic here ...
end; var
MyWrite: procedure; cdecl varargs = MyWrite_; begin
MyWrite();
MyWrite(, );
MyWrite(, , );
end;

How to create functions that can accept variable number of parameters such as Format的更多相关文章

  1. 【概率论】3-8:随机变量函数(Functions of a Random Variable)

    title: [概率论]3-8:随机变量函数(Functions of a Random Variable) categories: Mathematic Probability keywords: ...

  2. Variable number of arguments (Varargs)

    A parameter of a function (normally the last one) may be marked with vararg modifier: fun <T> ...

  3. copy contents of file with variable number in Matlab

    input : transient.case output: transient_1.case, transient_2.case, transient_3.case ... ************ ...

  4. Lua 变长参数(variable number of arguments)

    lua变长参数 function add ( ... ) for i, v in ipairs{...} do print(i, ' ', v) end end add(1, 2, 'sdf') lu ...

  5. [翻译] Macros with a Variable Number of Arguments - GCC

    可变参数宏(Variadic Macro) 在1999年的ISO C标准中,可以声明一个像函数一样接受可变参数的宏.定义这种宏的语法与函数的定义相似.这是一个例子: #define debug(for ...

  6. Four Ways to Create a Thread

    Blaise Pascal Magazine Rerun #5: Four Ways to Create a Thread   This article was originally written ...

  7. 5.24 Declaring Attributes of Functions【转】

    转自:https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html 5.24 Declaring Attributes o ...

  8. VB.NET vs. C#

    VB.NET Program Structure C# Imports System Namespace Hello    Class HelloWorld        Overloads Shar ...

  9. [转]c#.NET和VB.NET语法的比较

    本文转自:http://www.cnblogs.com/lify0407/archive/2007/08/01/838589.html c#.NET和VB.NET语法的比较   VB.NET C# C ...

随机推荐

  1. curl 发送请求的时候报错

    AWS HTTP error: cURL error 60: SSL certificate problem: unable to get local issuer certificate (see ...

  2. vue数据传递--我有特殊的实现技巧

    最近碰到了比较多的关于vue的eventBus的问题,之前定技术选型的时候也被问到了,vuex和eventBus的使用范围.所以简单的写一下.同时有一种特殊的实现方案. 有这么几种数据传递方式,vue ...

  3. 计算 Python (list or array) 相同索引元素相等的个数

    上代码: a = [2, 3, 3, 1, 1, 3, 3, 3, 2, 1, 3, 1, 3, 2, 2, 2, 3, 1, 2, 3, 2, 3, 1, 1, 2, 1, 1, 1, 2, 2, ...

  4. git与pycharm合并,珠联璧合

    前段时间提交代码都是各种手写push push push,好蠢,今天尝试了一下ide直接提交,爽的一匹,做个总结. 首先github帐号肯定要有. 看图,设置好帐号啥的 再设置git路径,这个应该自动 ...

  5. pip/conda国内镜像--安装包提速

    对于Python开发用户来讲,PIP安装软件包是家常便饭.但国外的源下载速度实在太慢,浪费时间.而且经常出现下载后安装出错问题.所以把PIP安装源替换成国内镜像,可以大幅提升下载速度,还可以提高安装成 ...

  6. 面试题13:在O(1)时间删除链表节点

    注意分情况讨论: 1. 要删除的不是尾节点 2. 链表只有一个节点 3. 链表中有多个节点,删除尾节点 void DeleteNode(ListNode** pListHead, ListNode* ...

  7. JS 数据类型转换以其他

    JavaScript 是一种弱类型的语言,也就是没有类型限制,变量可以随时被赋予任意值. 同时,在程序运行过程中,类型会被自动确认的.因此,这就是涉及到数据的类型转换.在 JS 的世界中,数据类型转换 ...

  8. js监测设备类型【安卓,ios,苹果微信,电脑pc】

    话不多说上代码: 1.判断是不是微信 function is_weixn(){ var ua = navigator.userAgent.toLowerCase(); if(ua.match(/Mic ...

  9. LoadRunner函数–lr_vuser_status_message

    http://wenku.baidu.com/link?url=KbKGldKUkam4VyH5Z2doesA0ovQpuwM9nX_SnVMtWjo6rJPxj9DqB51z_m1giMbVo5Db ...

  10. HDU - 4777 离线树状数组

    离线树状数组搞一搞. #include<bits/stdc++.h> #define LL long long #define fi first #define se second #de ...