Idea for named function arguments

3 views
Skip to first unread message

Stephan Weinberger

unread,
Mar 12, 2024, 12:48:37 PMMar 12
to LDMud Talk
Hello everyone,

Looking at the recently implemented 'struct compile_string_options' I
had an idea, how named function arguments might be implemented using
existing functionality in the driver:

The function argument list could be interpreted as a struct, with all
members containing references to the respective local variables. Calling
the function would then just initialize the struct with the values
provided between the brackets.

This should be fully backwards compatible to positional arguments, but
would also allow to use 'name: value' to set a subset of arguments.
Voilá: named function arguments.

The only things that will need some adjustments would be the 'varargs'
keyword and possibly the '...' operator.

Does this sound feasible or am I missing something?

regards,
  Invis

Gnomi

unread,
Mar 12, 2024, 2:22:35 PMMar 12
to ldmud...@googlegroups.com
Hi Invisible,

I'm not sure I understand your proposal.

So we have a function call via some forwarding function like call_other:

ob->fun(b: 10);

At this point we would pack that into a struct, which would need to be an
anonymous struct, because we don't know the target functions signature.
Then we do that call_other:

call_other(ob, "fun", (<anon_struct #1> b: 10));

On callee site, we then have a function like:

void fun(int a, int b);

And when called with an anonymous struct, we look up each member and assign
the arguments we have in there.

Is this a correct summary?

If so, I think there needs to be some disambiguation between passing this
generated argument struct and passing a real struct as an argument.
In the same line: How would a call_other sefun look like?

With best regards,
Gnomi

Stephan Weinberger

unread,
Mar 12, 2024, 5:42:50 PMMar 12
to ldmud...@googlegroups.com
Hi Gnomi,

TL;DR: call_other() ruins everything.


On 12.03.24 19:22, Gnomi wrote:
> Hi Invisible,
>
> I'm not sure I understand your proposal.

I was thinking about it more from the "receiving side", i.e. the moment
before a function is actually executed in the driver.


For local calls it would be rather straightforward:

    void func(int a, int b);

would internally be more like

    void func(<anon> int a, int b);

with a and b referencing the local function variables a and b on the stack.

(I'm writing func(<>...) here, instead of func( (<>...) ), to denote
that this struct is never visible on the LPC side, but only internally
generated from the function prototype, and only used to associate the
parameters on the stack with their respective names.)


The parser would then just have to treat everything in the brackets of a
function call as a struct initialization, thereby writing the values
into the local variables.


So in a world without call_other() & Co it could have been relatively easy.


> So we have a function call via some forwarding function like call_other:
>
> ob->fun(b: 10);


Yes, call_other is a very special case. The problem here is mostly in
the 'varargs' part, because when forwarding a function call it cannot
just be resolved to an array of values any longer (as that would lose
the argument names). So this would limit 'varargs' parameters to
positional only - which kinda defeats the point of having named
arguments in the first place, since call_other() is so widely used in LPC.

To make this work would require a replacement for 'varargs' that would
not return an array, but a new datatype for an argument-list, which is
an array/struct blend (as at this point we don't know the target
function's signature we have to preserve the provided argument list
as-is, including argument names as well as unnamed arguments in the
correct order, so that they can later be mapped onto the function
prototype correctly).
This would in turn also require extending the '...' operator to handle
this new argument-list datatype, which would then also have to properly
merge these arguments with the others provided in the call, as well as
map the result onto the actual implicitly generated struct from the
function at the time of the function call - which both open up a whole
new can of worms each, because now we can also have collisions between
positional and named arguments at runtime.


And at this point we're drifting more and more away from just using the
existing concepts of structs. :-(



> If so, I think there needs to be some disambiguation between passing this
> generated argument struct and passing a real struct as an argument.

The "real" struct would just be a field *inside* the implicitly
generated struct (which would directly reference the correct location on
the stack to end up in the correct variable), so no problem here.


> In the same line: How would a call_other sefun look like?

A simul_efun::call_other wouldn't be anything special per se, but of
course the varargs-problem still remains the same.
Plus the first two parameters of call_other & Co would need to have
reserved names, to prevent collisions.



regards,
  Invis
Reply all
Reply to author
Forward
0 new messages