[fpc-pascal] Header translation, argument list, array of const?

kyan alfasud.ti at gmail.com
Sat Jun 30 03:24:23 CEST 2012


On Sat, Jun 30, 2012 at 12:21 AM, Bernd <prof7bit at gmail.com> wrote:
> Hello, I need to call the following function:
>
>
> /**
>  * Notifies Purple that a buddy's status has been activated.
>  *
>  * This is meant to be called from protocol plugins.
>  *
>  * @param account   The account the user is on.
>  * @param name      The name of the buddy.
>  * @param status_id The status ID.
>  * @param ...       A NULL-terminated list of attribute IDs and values,
>  *                  beginning with the value for @a attr_id.
>  */
> void purple_prpl_got_user_status(PurpleAccount *account, const char *name,
>                                                           const char *status_id, ...) G_GNUC_NULL_TERMINATED;
>
>
> I have translated it as follows:
>
> procedure purple_prpl_got_user_status(account: PPurpleAccount;
>  name_, status_id: PChar; par3: array of const); cdecl; external LIBPURPLE;

That would be:

procedure purple_prpl_got_user_status(account: PPurpleAccount;
 name_, status_id: PChar); varargs; cdecl; external LIBPURPLE;

"varargs" is a compiler directive that reflects the ellipsis "..." in
C. It is only valid in external function declarations for static
linking (as in your case) or function prototypes for dynamic linking
using LoadLibrary()/GetProcAddress(). It is always combined with cdecl
(read on) and you cannot implement a varargs cdecl function (you get a
compiler error if you try), only declare a prototype and link to an
external one written in C.

I am not 100% sure FPC supports the varargs directive but I've found
at least one varargs cdecl function (gtk_tree_path_new_from_indices in
gtk2extrah.inc), so it probably does.

> How is this such a list of arguments actually implemented? is it on
> the stack? What structure does it have? Can I somehow manually put
> stuff onto the stack to make it think it is an empty list? maybe pass
> another integer and pass 0 instead of this list?

I am no C programmer but as I understand it the caller can pass as
many variable parameters of any type (actually it has to pass what the
function expects it to). The parameters are pushed into the stack
after fixed parameters and the callee (function) will retrieve them
out of the stack with pointer arithmetic using the address past the
end of the last fixed parameter (in your case status_id) as a starting
point and increment the running pointer according to every expected
variable parameter's type size. So the number and type of the variable
parameters must be known and respected by both caller and function and
is usually deduced by the values of the fixed parameters. A well known
such function with variable parameters is printf() where the type and
number of variable parameters is deduced by the placeholders in the
format string passed as the fixed parameter. In your case I think that
the value of the status_id parameter dictates the number and type of
the extra parameters. You'll have to read the documentation of the
function.

Stack cleanup is possible although the parameter stack has an unknown
size at compile time because of the cdecl calling convention where the
caller must allocate AND cleanup the stack and the caller knows the
number and types of the variable params at compile-time, the function
doesn't. In other calling conventions where the caller allocates the
stack but the function has to clean it up (e.g. pascal/stdcall) the
ellipsis/varargs cannot work.

A pascal open array of const which is the closest concept to a C
ellipsis may have similar semantics but its binary representation on
the parameter stack is completely different. First of all, since it
has to work in all calling conventions and not just cdecl the high
bound of passed array elements is pushed in the stack after a pointer
to an array of TVarRec structures that each contains a type attribute
and either a value or a pointer to the value of the parameter. So the
open array doesn't need to be the last parameter like the C ellipsis
and there can be more than one in a routine signature. So your
declaration of purple_prpl_got_user_status

procedure purple_prpl_got_user_status(account: PPurpleAccount;
  name_, status_id: PChar;
  par3: array of const);

is equivalent to:

procedure purple_prpl_got_user_status(account: PPurpleAccount;
  name_, status_id: PChar;
  par3: PVarRecArray; par3HighBound: Integer);

So the caller pushes only a pointer to a TVarRec array and an integer
while the function expects a continuous "stream" of parameters. If you
pass [] the compiler generates code to push a nil pointer (zero
PtrInt) and a high bound of -1. If the C function can handle these
values on the stack it *sort of* works by accident, but it will
probably fail if it tries to read past the -1 high bound for a
specific value of status_id that requires more variable parameters.

For details on open array parameters read this article:
http://rvelthuis.de/articles/articles-openarr.html

(Disclaimer: the description of open arrays apply to Delphi but my
guess is that the implementation in FPC is similar).

HTH

Constantine.



More information about the fpc-pascal mailing list