Cesium
Cesium copied to clipboard
Functions with varargs support
We need varargs support, since almost any simple tutorial use printf. Without that support we cannot go to public probably, otherwise Cesium always look as toy project.
Look for the number 196 in the code to find clues to implement this feature.
I agree on that point. Any ideas how such a function could work? The problem is that sometimes it's hard to distinguish a vararg function from the call-site point of view: it may look (and even be declared?) as a normal function.
Could we reuse the existing facilities of vararg functions in the CLI? Do these facilities even exist?
https://github.com/dotnet/runtime/issues/10478 that's not possible today, even if it would be possible in the future. So plan would be to mimic this. I think we can pass variadic arguments as object[] and that's it. That's will make implementing va_start ambiguous when used incorrectly, but I think that fine. That unlock interop scenarios, which is what's important at this stage. If variadic function would be declared as regular one, and defined as variadic that's we should be able resolve at "link" time, and that's pretty advanced and arcane scenario. I do not think we can afford think too much about it, give that we do not know space very well
I think we can pass variadic arguments as
object[]and that's it.
To be honest, I'm not sure we can put a pointer into an object[]. Is a pointer even an object? 🤔
Maybe some workaround using stack arrays or linked lists will be required.
I do find some interesting sample which definitely will complicate our life.
#include <stdio.h>
#include <stdarg.h>
void function1(char* s, ...)
{
va_list ap;
int tmp;
va_start(ap, s);
tmp = va_arg(ap, int);
printf(s, tmp);
va_end(ap);
}
void function2(char* s, int d)
{
printf(s, d);
}
typedef void (*functionX_t)(char*, int);
typedef void (*functionY_t)(char*, ...);
int main(int argc, char* argv[])
{
int x = 42;
/* swap! */
functionX_t functionX = (functionX_t) function1;
functionY_t functionY = (functionY_t) function2;
function1("%d\n", x);
function2("%d\n", x);
functionX("%d\n", x);
functionY("%d\n", x);
return 0;
}
What does the standard say about this? I'm not sure these signatures are technically compatible.
We can use __arglist and ArgIterator, which are built into DotNet (or copy the implementation, hehehehehe). They work well on Windows, but not so well on Unix. It would also solve the problem with 4 or 8 bytes in VA_ARG.
Well, I'd say we want something that will reliably work on all the supported platforms and runtimes.
We can switch to native va_list. I messed around with linux and windows and made a universal va_list that works on both operating systems. Source: https://gist.github.com/BadRyuner/170d7900d1860730192fdfabd12921ce (produces same output on windows 11 and debian 12) But a problem appears with functions that uses ... instead of va_list (e.g. printf instead of vprintf) This is the only problem blocking the way to a full transition to natively-like variadic functions, which works perfectly with native functions. For dotnet functions, we can use calli hax to get all arguments (RCX, RDX, R8, R9) with a stack pointer and initialize the native va_list or use old va_list. But here pinvoke is the problem. For extern dllimport dotnet creates a wrapper method that nullifies unnecessary pointers and stack to protect the environment. Accordingly, additional arguments will not pass there (if we are talking about ... and not va_list). My solution: use NativeLibrary.Load and NativeLibrary.GetExport for further use in calli, which can send any number of arguments. In doing so, calli does not use wrappers unlike PInvoke. But here another problem arises... NetStandard 2.0 does not provide a NativeLibrary class. Here we have to either create two separate classes for PInvoke (LoadLibrary & GetProcAddress - Windows; dlopen & dlsym - Linux/MacOS) or drop it.
Also msvc generates its own wrapper over printf, where it packs the arguments into a va_list and calls vprintf.
I do not like the solution involving any such hacks. Remember that we have to support at least 9 OS/CPU combinations (x86/x64/ARM64 macOS/Win/Lin). I do not believe there's a reliable solution for all those at once.
And why would we want it anyway? Currently we aren't targeting that class of interop.