ebitengine / purego

Apache License 2.0
2.1k stars 66 forks source link

Are variadic functions supported? #116

Open craig65535 opened 1 year ago

craig65535 commented 1 year ago

The source has a comment about a special case for variadic functions -

// There is a special case when the last argument of fptr is a variadic interface (or []interface}
// it will be expanded into a call to the C function as if it had the arguments in that slice.
// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
// This is not the same as C variadic.

Does this mean they are not supported? I made my own library in C and am unable to find a way to get my function to see any of the variadic parameters.

void printSomething(int a, const char *fmt, ...)
{
    va_list ap;
    printf("start %d\n", a);
    printf("fmt %s\n", fmt);
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
    printf("\n");
    printf("end %d\n", a);
}

Calling code:

    var printSomething func(int, string, []interface{})
    purego.RegisterLibFunc(&printSomething, libcraig, `printSomething`)
    printSomething(5, `%s`, []interface{}{`foo`})

This prints:

start 5
fmt %s
(null)
end 5
craig65535 commented 1 year ago

I should mention I'm using macOS (darwin/arm64).

TotallyGamerJet commented 1 year ago

That is correct. Variadic functions are not supported and there are currently no plans to add them.

craig65535 commented 1 year ago

Thanks for the response.

I think the documentation for RegisterFunc/RegisterLibFunc could be improved because that wasn't clear to me. But I'm not sure what to change it to, as there is probably some use for the current arrangement with []interface{} that I'm not aware of.

TotallyGamerJet commented 1 year ago

The current implementation is used to implement the Send method in the objc package. This way the user of the method can call any objc method without having to create a func variable, link it to the C function and then call it.

lsy88 commented 9 months ago

How do I convert Go []string to C char **

TotallyGamerJet commented 9 months ago

Create a slice of byte. Convert each string to null terminated byte. Then pass the address of the slice to the function. So the signature would be &slice[0] and the type **byte

lsy88 commented 9 months ago

Please understand that I may be a little unclear in the description, for the case of a C function parameter char **, what type should be used in Go?

TotallyGamerJet commented 9 months ago

**byte like I said

lsy88 commented 9 months ago

I'm sorry that I can't solve this matter according to your suggestion C:

int test(char *path, char **result, int *len)

Go:

var test func(path string, result **byte, rlen *int) int32
...
purego.RegisterLibFunc(&test, lic, "test")
...
func Test(path string) {
  var lenRes int
  result := make([]*byte, 0)
  _ = test(path, &result[0], &lenRes)
}
...

I'm not sure if that's what you said, but I still haven't gotten the correct data return, The correct result data should be a string, but I only got one byte, what should I do to get the returned string

TotallyGamerJet commented 9 months ago

You actually got all the code right so far. You just need to turn the *byte back into a string. There's no easy way to do this atm. There's discussion in https://github.com/ebitengine/purego/issues/121 about making it more accessible. You can also just copy the code that's inside purego/internal/strings. Ofc you'll have to do that for each pointer in the slice

EDIT: Oh it's a result no a parameter. So instead of using a slice just create a *byte and take the address of that.


I don't really want to busy up this issue more as your question doesn't relate to variadic functions. If you have more questions about this please ask on the discord.

JupiterRider commented 4 months ago

@craig65535 @TotallyGamerJet My libffi wrapper supports variadic functions now. See ffi.PrepCifVar.

Unfortunately ffi doesn't support MacOS yet.