Inheritance in NeoLua?

Feb 11, 2015 at 6:51 AM
Hello,
So I have a class in C# which I've registered into the Lua environment with the following code:
env["TestBot"] = LuaType.GetType(typeof(TestBot));
What I would like to do now is create a child class TestChild in Lua, (with overrides and/or additional functions) which I can then pass back and use as a TestBot.

My best attempt was this code:
function TestChild(pi, ark)
    local obj = TestBot(pi, ark)
    function obj:archeopteryx()
        print("hork")
    end
    return obj
end
(ah... ignore the idiosyncrasies; I get bored writing test code if I don't do things like that.)
Anyway, this returns the exception:
An unhandled exception of type 'Neo.IronLua.LuaRuntimeException' occurred in Neo.Lua.dll

Additional information: No conversion defined from TestBot to LuaTable.
So clearly this doesn't work the way I think. I tried a few variations but none of them have worked. And forgive me for saying so, but the documentation just confused me. sweatdrop
So anyway, the big question is: Is there currently a way to do this sort of inheritance in NeoLua, and if so, how?
Coordinator
Feb 11, 2015 at 7:59 AM
Hello,

real inheritance is not possible with Lua.
But what you can do, is extent your TestBot.

First, where is the problem in your code:
    function obj:archeopteryx() 
This line will be transformed to
((LuaTable)obj).DefineMethodLight("archeopteryx", function(self))
.
The reason is, that the DLR only knows SetMember. With no possiblity to mark the function as a method. Btw. the cast raises the exception.
obj.archeopteryx = function(self)
This line will generate the SetMember call on any dynamic object. But it is your responsiblity to take care of the call.

Ok, this is just for the Background.

The possibilities, to reach the goal:
  • The easiest way to find a solution for you problem is just to inherit TestBot from LuaTable. More Infos
  • The harder way is to implement IDynamicMetaObjectProvider, what I will not recomment (a lot stuff to learn).
I hope that helps.
Feb 11, 2015 at 8:46 PM
That did help quite a bit :) ... but I'm not quite there yet :/

I've now got TestBot inheriting from LuaTable, with the fields and methods declared as LuaMembers.
But, I'm still having a problem overriding methods. I managed to figure a way that works, but it feels like a workaround:
[LuaMember("ToString")]
public override string ToString()
{
    Func<TestBot, LuaResult> luaFunc = this["ToString"] as Func<TestBot, LuaResult>;
    return luaFunc(this).ToString();
}
with the original definition of the method declared in lua in the constructor:
Lua lua = new Lua();
LuaGlobal env = lua.CreateEnvironment();
env["this"] = this;

env.DoChunk("function this:ToString() return self.ark .. \" \" .. self.pi end", "ToString.lua");
(It's creating its own environment because I wanted the constructor to only have the two arguments, so I could create new objects in Lua.)
So while this works, I can't help but feel there's a better way to do it. The documentation you linked me to gave me the impression that declaring a method as a LuaMember would allow Lua scripts to override the method by creating a function with the same name.
In case it matters, here's the Lua code I'm using to extend TestBot:
function TestChild(pi, ark)
    local obj = TestBot(pi, ark)
    function obj:archeopteryx()
        print("hork")
    end
    function obj:ToString()
        return self.pi .. "++ = " .. self.ark
    end
    return obj
end
Coordinator
Feb 12, 2015 at 10:56 AM
At first.
All functional languages do not know any about inheritance. So, there is no real declaration. But inheritance is possible to simulate.

A pure Lua example:
function CreateA()
   local t = {};

   function t:method()
     print("base");
   end;

  return t;
end;

function CreateB()
  local t = CreateA();

  function t:method()
     local base = t.method;
     print("b");
     base();
  end;

  return t;
end;
Hopefully, I did not make any mistake :).

To mix this up with host code is complicated. But possible. The problem is the host site.
public class A : LuaTable
{
  [LuaMember("Method")]
  public void Method()
  {
  }
}
function CreateB()
   local t = clr.Namespace.A();
   function t:Method()
     local base = t.Method;
      ....
   end;
  return t;
end;
Calling from Lua is easy:
t:Method(); 
This works, because LuaMember is just initializing the value.

If you call the method from c#, is a little bit more complicated.

The overload/override is done during runtime and will be not resolved during the compile process (because the c# compiler doesn't know anything about your code). That is the reason why you can't call the member direct. Except, you do, what you did in your first example (you did simulate the dynamic call).

The better way is to use the dynamic interface.
t.Method(); -- will call always your c# method
((dynamic)t).Method(); -- will be resolved the call during runtime, and will use the lua method (CallSite will be created)
Hope I could make it clear.

Btw. I know, there is room for improvment. But currently I have not a idea how.
Marked as answer by littlebeast on 2/14/2015 at 4:03 PM
Feb 14, 2015 at 11:06 PM
Hmm, yeah, I'd prefer if I could use something a little more strongly typed than dynamic. But I'm not sure what, if any, means there would be to do this in C#. (Not that I'm an expert - I honestly didn't realize, previously, that dynamic was anything beyond "a variable you can stuff any type into".)

Any case, this should work for what I'm doing, so thank you. :)