Advertisement

Support for registering a variadic function

Started by October 26, 2024 02:21 PM
11 comments, last by WitchLord 3 days, 9 hours ago

Currently, registering a printf/format-like function in AngelScript is bothering. A workaround is using function overloads with parameters from less to more. I tried to modify the source code to implement variadic function support. This is the current version:

As you can see, the function is registered using int sumi(int …). The script can use sumi(100, 10, 1) to invoke it, getting the result 111 (shown by the debugger). Once this is complete, maybe developer can register a format function in declaration like format(const string&in fmt, ?&in …).

My current solution is adding a new flag IsVariadic for function. Function with this flag will treat the last parameters as a special one, allowing any number of arguments of this type passed to the target function. The argument count is pushed to stack as the hidden first argument using PshC4. This will be handled by a sightly modified asCGeneric. The user code can use gen->GetArgCount() and other interfaces to retrieve arguments.

For example, this is the C++ code of sumi_generic in the above screenshot:

void sumi_generic(asIScriptGeneric* gen)
{
	int result = 0;

	int count = gen->GetArgCount();
	for (int i = 0; i < count; ++i)
	{
		int val = (int)gen->GetArgDWord(i);
		result += val;
	}

	gen->SetReturnDWord(result);
}

However, it seems that it needs some further consideration when playing with named arguments. Should allow that special parameter to have a name? Thus, given a function int func(int a, int b, int… args) , we can probably write int result = func(args:1, args:2, args:3, a:0xA, b:0xB);.

None

Nice. This was another feature I was planning to add for 2.38 😉 I'm happy to receive some help with it.

Why did you decide to use the syntax func(int …)?. Just func(…) would be enough.

I hadn't thought about named arguments should supporting variadics. The variadic must anyway be the last argument in the function, and you cannot have more than one variadic argument in a function.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

I think it might be helpful to allow typed variadic function. The host won't need to check types if you want to register a function like int sum(int…) or float average(float…). But func(…) can be equivalent to func(? &in …) or something similar for simplicity when registering a printf -like function (I believe this is the most common case for using a variadic function).

None

I see. That's a good idea.

I suggest however that we solve the generic case first, with just …, that can take any argument type. The other specialized cases can be done later.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

WitchLord said:

I suggest however that we solve the generic case first, with just …, that can take any argument type. The other specialized cases can be done later.

According to my experience during implementing this feature, the int... was actually easier to implement than ?&in … (or ...) , because it didn't need to consider those type ids on stack.

Currently, I've complete the support for variable type. Now developer can register a function like string format(const string&in fmt, const ?&in ...) . I also implemented a simple format for the string addon. Here is some demonstration:

// ExecuteString:
string result = format('{} {} {}', 123, true, 'hello');
print(result);

Screenshot of the output:

I am more familiar with the C++20 <format> than the C-style printf, so I implement it using the syntax from the C++ standard library. But it would be easy for anyone to implement a printf-like function, as registering variadic function will no longer be a problem.

Next step will be 1) making some syntactic sugar, for example, func(?&in…) (or func(const ?&in …) ?) can be simplified to func(…); and 2) test registering a class method with variadic parameters (ensuring the additional stack value for argument count doesn't mess up with object pointer).

Further consideration: should allow the script to have a variadic function? Or just make the variadic function only available for registering from host application.

None

It looks good. Looking forward to seeing the implementation.

Adding support for implementing script functions with … is another topic. I'm not planning on adding that feature, at least not without strong arguments for why it would be necessary. It's the same with the templates that also cannot be declared within the scripts.

Something that I do feel is necessary though, is the ability to use … with output parameters. Something like:

parse("{} {}", inputString, out firstValue, out secondValue);

However, this is something that can be left for a future implementation.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

WitchLord said:

Something that I do feel is necessary though, is the ability to use … with output parameters. Something like:

parse("{} {}", inputString, out firstValue, out secondValue);

In theory, registering a parse function with parse(const string&in input, const string&in fmt, ?&out…) won't be a problem. The only difference on stack between ordinary and variadic function is the hidden argument count. Maybe I can try to implement a simple scan/parse tomorrow.

None

HenryAWE said:

Maybe I can try to implement a simple scan/parse tomorrow.

I've implemented a simple uint scan(const string&in str, ?&out …) and everything seems well.

The screenshot of output:

The basic functionalities of variadic function has been completed. I'll send the patch via email within a few days. I'll be very busy until January, 2025, which means I probably won't have enough time to work on this feature, so I decided to send the patch as early as possible.

Here is a list of probably noteworthy things for future development:

  1. The order of overloading. Maybe the variadic function should be the last to match during overloading resolution. If I remember correctly, this is also how the C++ overloading rule treats C-style ... variadic function.
  2. Should the constructor/factory registered with variadic support accepts list-initialization if a list constructor/factory doesn't exist.
  3. Interoperability with templated function. I've noticed that 2.38 WIP has support for templated function. Maybe there can be a more complex void func<class… Ts>() ?

None

Looks good.

I'm looking forward to the patch.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

@witchlord The patch has been sent. I've only tested some basic situation (format for variadic input, scan for variadic output, and variadic parameters for object method). I didn't have enough time to test special cases, like variadic constructor/factory, object behaviors, or object types with opCall.

None

Advertisement