Map generic c# functions of registered types to lua functions#344
Map generic c# functions of registered types to lua functions#344imi0 wants to merge 1 commit intomoonsharp-devs:masterfrom
Conversation
…the generic arguments as starting parameters
|
Thanks for the contribution. Admittedly, I'm having a bit of trouble understanding the use case here. It looks like you're exposing a reference to the C# class as a global, then users have to know to obtain that C# class reference and pass it as a parameter, and that type has to match the type of a subsequent parameter etc. (wherever that generic is consumed in the list). Given that you need to manually expose types, and users have to know to pass those types, it doesn't seem very flexible / safe. This is also confusing because it seems to only support one generic parameter, and doesn't account for things like I can see why you may want something like this for your own convenience, but it doesn't really seem like the kind of thing that ought to live in MoonSharp itself. |
Yea, the usefulness heavily depends on how lua scripts are integrated in the application. If you only allow a very limited number of types and carefully providing selected functions only, this feature has almost no benefit, as you usually just provide a function with the otherwise-generic parameter as first parameter yourself, if you need that functionality. I.e. instead of providing
you could just do
The more your application treats lua as a "scripting against lots and lots of classes while minimizing the effort on the C# integration side", the better the benefit gets, as you can just re-use existing functions and don't have to introduce wrappers or proxy types all over the place. (And this is our use case ;) ). So the main benefit is to just be able to re-use existing classes that happen to add generic functions and not having to write wrapper functions yourself. Oh, another benefit is, that some generic functions can not easily be re-written as "Foobar(Type)", if they need to be generated differently by the C# compiler for different T's and/or because they are calling other generic functions on the T themself. To fix that would occur a manual, explicit reflection+MakeGenericMethod call in the wrapper function: Sure - it doesn't hurt that much if its only a dozen of those wrapper lines, but this gets old quickly.
Well, it doesn't have to be exposed as a global variable. That was just the easiest way to do it in a as-simple-as-possible test ;). But I thought using an userdata on "Type" is kind-of normal if you want to reference a type in lua? How else would you express a C# type in lua?
Yea.. I know. In the current form, it is "explicit-declaration-only". In the Foobar above (that has no parameters), you would also have to declare the concrete type in C#, but yes: the "even cooler" feature would be to support type deduction like this:
maps to just calling "obj:foobar(t)" in lua function and the type for T is deduced from "t"'s type. I might try on that feature later, but well... tbh.. I am not 100% sure how to tackle that immediately, robust and straight forward.. (I don't think C# brings any stock functionality to deduce types on a generic MethodInfo's. Roslyn probably does, but I am so NOT going to import a whole compiler DLL just for that feature :-D) IMHO it might still be beneficial to explicitly specify the generic type, even if MoonSharp would support type deduction. Because you can also do this in C# calls.
But what would be the alternative? IF you have C# code that would fit a scheme where you pass a Type parameter, how else would you model that? I mean.. sure: If you never even want to have a function that takes any System.Type, then you probably also never want to have a function exposed that are generic (because that is kindof semantically similar to "passing a type"). And again: the fact that I "manually" set the types in the test cases as global variables doesn't mean they always have to be manually set this way ;). The types could have been returned from other functions or obtained via another global function (or by an exposed get_type function). Hm.. thinking of it.. I could allow any userdata object to be passed instead of the type parameters and then just take its type. But in my opinion, that makes things a lot more confusing and vastly increases the mismatches or ambiguous calls.
Huh? I should support multiple generic arguments just fine, see test Interop_GenericMethods in UserDataMethodsTests.cs, line 740 calling GenericStaticDescribeTypes with two generic types. Did I made a mistake somewhere? (I started with only one argument for simplicity but then though: That's just unnecessary restricting and changed it later to support any number of generic arguments).
Well, it kindof does. It will fail with an exception when you try to create the concrete generic method passing the wrong type that does not fit the constraints. (Line 70 of GenericMethodMemberDescriptor.cs) But yes, the error message could be improved here to either parse the C# exception or do some manual constraint verification inside the Execute (but that would occur a runtime penalty, so...)
You think so? Hm.. I have to admit that I don't really got the overall place of where MoonSharp wants to live. I mean.. NLua clearly goes towards "here you have C#, just map everything as fast as possible and don't bother me with details". This PR would fit perfectly there. :-D MoonSharp indeed strongly discourages "binding of everything" and encourages dedicated, carefully constructed interfaces. But nevertheless, it still provides mechanisms to "broadly fast-bind lots of existing classes" (e.g. by providing IRegistrationPolicy where coders can easily decide to auto-binding whole sets of functions). Hence, I thought it might actually be a good fit for these scenarios. And if the class you expose does not have exposed generic functions anyway, the feature would not change anything either. |
|
My instinct is that an explicit boundary between C# and Lua is worth the boilerplate. You get a reviewable API surface, refactors don't silently break scripts, and edge cases like generic constraints or AOT/IL2CPP behaviour stop being the binding layer's problem. For the cases where you genuinely need a generic helper in Lua, a hand-written Foo(Type t, ...) wrapper feels like the right place to pay that cost. |
This adds support for generic C# methods.
Generic method type arguments are passed from Lua as Type userdata arguments before other parameters. Basic overload resolution support against non-generic overloads and params arrays in generics.
I also added some tests.