2

I'm using LuaInterface in C# and I "exported" some custom C# classes to be used in Lua. For instance:

local myVector = Vector2(10, 100)

But, when I want to use class operators like in this example:

local v1 = Vector2(1, 1)
local v2 = Vector2(2, 2)
local v3 = v1 + v2

I'm getting the following error: attempt to perform arithmetic on local 'p1' (a userdata value)

The C# variant of the class does have the + operator:

    public static cVector2 operator +(cVector2 vector1, cVector2 vector2)
    {
        return new cVector2(vector1.X + vector2.X, vector1.Y + vector2.Y);
    }

I know that you should make use of the Lua metatables and add a function to "__mul" for the * operator for example. But doesn't LuaInterface does that automatically? And if not, how could I automate this myself?

Erwin Okken
  • 233
  • 2
  • 17
  • 1
    I am surprised that after 8 months this has gotten no response. I'm adding a bounty, because your question is still valid. – selkathguy Apr 08 '14 at 13:48
  • You wrote `p1` in the error message. Is that supposed to be `v1`? There's no `p1` in any of the code shown here. – Mud Apr 08 '14 at 18:12

1 Answers1

2

But doesn't LuaInterface does that automatically?

No. You can see for yourself via:

for k,v in pairs(getmetatable(v1)) do
    print(k,v)
end

You'll see no __add metamethod.

if not, how could I automate this myself?

You'd have to modify the LuaInterface source to look for the operator+ method and add the __add metamethod. It simply doesn't do that now.

Given that you have the type proxy available (because you imported the type via import_type), you can access operator+, which is a static method on the type.

local v3 = Vector2.op_Addition(v1,v2)

To say v1 + v2 you'd need to modify the metamethod used by Vector2 object instance, but this requires creating an instance of the type:

local v1 = Vector2(1,1)
getmetatable(v1).__add = function(a,b) return Vector2.op_Addition(a,b) end

This affects the metamethod used by all instance, so you only need to do it once. Now you can write:

local v2 = Vector2(2,2)
local v3 = v1 + v2

Because you need an object to edit its metamethod, it would be hard to make this cleaner. If you modify your C# code to make sure that your class has a default constructor (i.e. no parameters), you could create a wrapper for import_type that does this:

function luanet.import_type_ex(typename)
    local T = luanet.import_type(typename)
    local mt = getmetatable(T())
    local function gethandler(name) return T[name] end
    local function addmethod(metamethodName, handlerName)
        local foundHandler, handler = pcall(gethandler, handlerName)
        if foundHandler then mt[metamethodName] = handler end
    end
    addmethod('__add', 'op_Addition')
    addmethod('__sub', 'op_Subtraction')
    addmethod('__mul', 'op_Multiply')
    addmethod('__div', 'op_Division')
    return T
end

You could extend that for other operators. Note that LuaInterface throws an exception if you try to access a member that doesn't exist (rather than returning nil), so we have to wrap the attempt to access a handler with pcall.

With that in place you could write:

Vector2 = luanet.import_type_ex('YourNamespace.Vector2')
local v1 = Vector2(10)
local v2 = Vector2(20)
local v3 = v1 + v2

Of course, this would work for other types that have overloaded operators.

LuaInterface is a bit of a hot mess. There are a few projects in the Lua world like it, where somebody at PUC-Rio does it as a research project, publishes a paper, then abandons it. They did it to see if they could, not because they actually use it.

Mud
  • 28,277
  • 11
  • 59
  • 92
  • Amazing answer, thank you. Are there any projects in the Lua world that are actually in use/still developed/standard? Also I will try out your `import_type_ex` suggestion. – selkathguy Apr 09 '14 at 14:42
  • Are there standard C# interfaces in the Lua world? No. There are forks of LuaInterface, but they add so much random bullshit I've never used them. I just work around the limitations of LuaInterface. As for `import_type_ex`, I noticed coming back to it that I'd added an unnecessary wrapper for `handler`. Removing it lets `addmethod` work for either unary or binary operators, so use the updated version. – Mud Apr 09 '14 at 16:26
  • Yeah I'm noticing that as well. Fortunately I have the LuaInterface source so I can extend it as needed. Your `import_type_ex` is working beautifully. However I'm not sure how to handle structs, as that method requires a parameterless constructor which I can't have in a struct in C#. Hmm. I might have to convert my struct to a class, but I'd rather keep them structs if I can. Another question: will that method for adding arithmetic operators handle the multiple combinations I have set up in C#? Like `Coordinate / Coordinate`, `Coordinate / Dimension`, etc? – selkathguy Apr 10 '14 at 13:51
  • Structs *always* have a parameterless constructor. You don't need to add one, and in fact can't. And yes, if the combination is valid in C#, it should be valid. – Mud Apr 10 '14 at 18:43
  • I hate to take your time but you have been very helpful. Then why do I get `"JASG.Coordinate does not contain constructor(.ctor) argument match"` from the site of your `local mt = getmetatable(T())` when `JASG.Coordinate` is a struct? – selkathguy Apr 10 '14 at 18:46
  • Probably another bug in LuaInterface, which I don't have time to investigate at the moment. It's not because structs can't contain parameterless constructors. They all do. Try it. – Mud Apr 10 '14 at 19:05
  • You were correct in that it's a shortcoming of LuaInterface. I have located the `callConstructor(IntPtr luaState)` method in the LuaInterface source code and appended a conditional check to see if the UnderlyingSystemType is a struct via some `IsValueType` and not `IsPrimitive`, etc.. If the code gets to that point then it hasn't found a constructor yet, and I use `Activator.CreateInstance()` to create the struct, which has been working flawlessly so far. – selkathguy Apr 12 '14 at 18:42
  • Or You can use NLua. NLua does this automatically – Vinicius Jarina Oct 24 '14 at 12:07