question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

C# method override in Lua

See original GitHub issue

NeoLua Version: 5.3 (Nuget 1.3.10)

Hi, I’m just starting to use NeoLua, which is pretty good, and I fall into an issue… I want to know if it’s possible to override a virtual method defined in C# from Lua.

I’ve tried to do like this

Component = clr.AlienEngine.Core.Gaming.LuaComponent;
cmp = Component();

cmp.Update = function()
    print "Updated";
end;

where LuaComponent is just a proxy of the abstract Component class for the use in lua code.

When I want to execute this code, I got the error 'LuaComponent:Update' is not writable.

I’ve also tried to do:

Component = clr.AlienEngine.Core.Gaming.LuaComponent;
cmp = Component();

function cmp:Update()
    print "Updated";
end;

and I got the error No conversion defined from LuaComponent to LuaTable.

So please I want to know if it’s possible or if there is another way to achieve this goal. Thanks

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
neolithoscommented, Aug 9, 2019

That looks good. Just for the list _overrides had choose a more generic way.

Good work. I leave this issue open for other users. May be some times, I or someone other will create a wiki page for this example.

1reaction
na2axlcommented, Aug 9, 2019

Hi @neolithos, sorry for the late response. Few days ago, I’ve implemented a solution following your advice, it’s totally more clean now and everything is managed from C#, thanks again. The new implementation is:

The new ScriptBridge class:

namespace AlienEngine.Core.Scripting.Lua
{
    public interface IBridgeClass
    {
        LuaTable Self { get; set; }
    }
}
namespace AlienEngine.Core.Scripting
{
    public class ScriptBridge<T> : LuaTable
        where T : class, IBridgeClass
    {
        private static readonly BindingFlags BindingFlag = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

        private static readonly PropertyInfo[] ReflectionProperties = typeof(T).GetProperties(BindingFlag);

        private static readonly MethodInfo[] ReflectionMethods = typeof(T).GetMethods(BindingFlag);

        private T _that;

        private LuaTable _inner;

        private List<string> _overridable;

        public ScriptBridge(T that, List<string> overridable)
        {
            _that = that;
            _overridable = overridable;

            _inner = new LuaTable();

            _that.Self = this;
        }

        protected override bool OnNewIndex(object key, object value)
        {
            // Check if we are trying to overwrite an existing key
            if (_inner.ContainsKey(key))
                goto SetInner;

            string memberName = key as string;

            if (memberName != null)
            {
                // Check if we are trying to overload a native method
                if (_overridable.Contains(memberName))
                {
                    _setProperty($"Lua{memberName}", value);
                    goto Return;
                }

                // Check if we are trying to redefine a property
                if (_hasProperty(memberName))
                {
                    _setProperty(memberName, value);
                    goto Return;
                }
            }

SetInner:
            _inner[key] = value;

Return:
            return true;
        }

        protected override object OnIndex(object key)
        {
            // Check if it's a custom member
            if (_inner.ContainsKey(key))
                return _inner[key];

            string memberName = key as string;

            if (memberName == null)
                return null;

            // Check if it's a native property access
            if (_hasProperty(memberName))
                return _getProperty(memberName);

            // Check if it's a native method call
            if (_hasMethod(memberName))
                return new Func<LuaTable, object[], object>((obj, args) => _callMethod(memberName, args));

            return null;
        }

        private bool _hasProperty(string name)
        {
            foreach (PropertyInfo property in ReflectionProperties)
            {
                if (property.Name == name)
                {
                    return true;
                }
            }

            return false;
        }

        private void _setProperty(string name, object value)
        {
            typeof(T).GetProperty(name, BindingFlag)?.SetValue(_that, value);
        }

        private object _getProperty(string name)
        {
            return typeof(T).GetProperty(name, BindingFlag)?.GetValue(_that);
        }

        private bool _hasMethod(string name)
        {
            foreach (MethodInfo method in ReflectionMethods)
            {
                if (method.Name == name)
                {
                    return true;
                }
            }

            return false;
        }

        private object _callMethod(string name, params object[] args)
        {
            // Execute the method
            return _getMethod(name, args)?.Invoke(_that, args);
        }

        private MethodInfo _getMethod(string name, params object[] args)
        {
            return typeof(T).GetMethod
            (
                name,
                BindingFlag,
                Type.DefaultBinder,
                args.Select(arg => arg.GetType()).ToArray(),
                null
            );
        }
    }
}

And the new way to use this:

namespace AlienEngine.Core.Gaming
{
    internal class LuaComponentInternal : Component, IBridgeClass
    {
        public LuaTable Self { get; set; }

        public Action<object> LuaLoad { get; set; }

        public Action<object> LuaStart { get; set; }

        public Action<object> LuaBeforeUpdate { get; set; }

        public Action<object> LuaUpdate { get; set; }

        public Action<object> LuaAfterUpdate { get; set; }

        public Action<object> LuaStop { get; set; }

        public Action<object> LuaUnload { get; set; }

        public Action<object, bool> LuaDispose { get; set; }

        public override void Load()
        {
            LuaLoad?.Invoke(Self);
        }

        public override void Start()
        {
            LuaStart?.Invoke(Self);

            base.Start();
        }

        public override void BeforeUpdate()
        {
            LuaBeforeUpdate?.Invoke(Self);
        }

        public override void Update()
        {
            LuaUpdate?.Invoke(Self);
        }

        public override void AfterUpdate()
        {
            LuaAfterUpdate?.Invoke(Self);
        }

        public override void Stop()
        {
            LuaStop?.Invoke(Self);

            base.Stop();
        }

        public override void Unload()
        {
            LuaUnload?.Invoke(Self);
        }

        protected override void Dispose(bool disposing)
        {
            LuaDispose?.Invoke(Self, disposing);
        }
    }

    public class LuaComponent : LuaTable
    {
        protected override LuaResult OnCall(object[] args)
        {
            return new LuaResult
            (
                new ScriptBridge<LuaComponentInternal>
                (
                    new LuaComponentInternal(),
                    new List<string>
                    {
                        "Load",
                        "Start",
                        "BeforeUpdate",
                        "Update",
                        "AfterUpdate",
                        "Stop",
                        "Unload",
                        "Dispose"
                    }
                )
            );
        }
    }
}
Component = clr.AlienEngine.Core.Gaming.LuaComponent()

function LuaTestComponent()
    local cmp = Component()

    cmp.Name = "LuaTestComponent"

    function cmp:Start() : void
        print ("Component " .. self.Name .. " started")
    end

    function cmp:Update() : void
        print ("\tComponent " .. self.Name .. " updated")
    end

    function cmp:Stop() : void
        print ("Component " .. self.Name .. " stoped")
    end

    return cmp
end
Read more comments on GitHub >

github_iconTop Results From Across the Web

Overriding c++ method in lua and call it back in c++
AFAIK, when you call methods (or functions) in C++, you are referring to an address where the instruction is located. And there is...
Read more >
override function metatable with lua code · Issue #583
Hi, I was wondering if it is possible to override a usertype function metatable at runtime using Lua code? I'm trying to bind...
Read more >
(Solved) class.lua doesn't override and call parent methods ...
can someone tell me why my class.lua instead of overriding and inheriting it's parents methods though self.base.<methodname> it seems to use ...
Read more >
Thread: Override lua Function
Is there a way to override a function in lua similar to how you can apply a merge property of join to an...
Read more >
Override a local function/variable from another function
How can I override a local function/variable that's in a different function/environment? e.g. (yes, its Lua 5.1): function override(orig, replacement)
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found