Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ -- additional -- ] Lambda and std::function #59

Open
DominikMS opened this issue Jun 8, 2014 · 12 comments
Open

[ -- additional -- ] Lambda and std::function #59

DominikMS opened this issue Jun 8, 2014 · 12 comments

Comments

@DominikMS
Copy link

Example:

    g_lua.
        Class<ClassTest>("ClassTest").
            method("emptyRet", std::function<void(int)>([](int x)
                    {
                        std::cout << "[hello void std::function] " << x << std::endl;
                    } )).
            method("boolRet", std::function<bool(int)>([](int x)
                    {
                        std::cout << "[hello return std::function] " << x << std::endl;
                        return true;
                    } )).
            method("lambda", [](int x) { std::cout << "[hello lambda]" << std::endl; return true; } );
g_lua.func("getBoolTest", std::function<bool(int x)>( [](){ return true; } ));

Todo:

[Fixed] Can't bind std::function under LuaGlue.func()
[Fixed] Can't bind and lambda under LuaGlue.func()

Code:

Add file: LuaGlueStdFuncMethod.h

#ifndef LUAGLUE_STDFUNC_METHOD_H_GUARD
#define LUAGLUE_STDFUNC_METHOD_H_GUARD

#include <string>
#include <tuple>
#include <utility>

#include "LuaGlue/LuaGlueObject.h"
#include "LuaGlue/LuaGlueApplyTuple.h"
#include "LuaGlue/LuaGlueBase.h"


template<typename _Class>
class LuaGlueClass;

class LuaGlueMethodBase;

template<typename _Ret, typename _Class, typename... _Args>
class LuaGlueStdFuncMethod : public LuaGlueMethodBase
{
    private:
        typedef std::tuple<typename std::remove_const<typename std::remove_reference<_Args>::type>::type...> ArgsTuple;

        typedef _Class ClassType;
        typedef _Ret ReturnType;
        typedef std::function<_Ret(_Args...)> MethodType;

        LuaGlueClass<_Class> *glueClass;
        std::string name_;
        MethodType fn;

        ArgsTuple args;
        static const unsigned int Arg_Count_ = sizeof...(_Args);

    public:
        LuaGlueStdFuncMethod(LuaGlueClass<_Class> *luaClass, const std::string &name, MethodType &&fn) :
            glueClass(luaClass), name_(name), fn(std::forward<decltype(fn)>(fn))
        { }

        virtual ~LuaGlueStdFuncMethod() { }

        virtual bool glue(LuaGlueBase *luaGlue)
        {
            lua_pushlightuserdata(luaGlue->state(), this);
            lua_pushcclosure(luaGlue->state(), &lua_call_func, 1);
            lua_setfield(luaGlue->state(), -2, name_.c_str());
            return true;
        }

        static int lua_call_func(lua_State *state)
        {
            auto mimp = (LuaGlueStdFuncMethod<_Ret, _Class, _Args...> *)lua_touserdata(state, lua_upvalueindex(1));
            return mimp->invoke(state);
        }

        int invoke(lua_State *state)
        {
            ReturnType ret = applyTuple<_Ret, _Args...>(glueClass->luaGlue(), state, fn, args);
            lua_pop(state, (int)Arg_Count_);

            stack<_Ret>::put(glueClass->luaGlue(), state, ret);
            return 1;
        }
};

template<typename _Class, typename... _Args>
class LuaGlueStdFuncMethod<void, _Class, _Args...> : public LuaGlueMethodBase
{
    private:
        typedef std::tuple<typename std::remove_const<typename std::remove_reference<_Args>::type>::type...> ArgsTuple;

        typedef _Class ClassType;
        typedef std::function<void(_Args...)> MethodType;

        LuaGlueClass<_Class> *glueClass;
        std::string name_;
        MethodType fn;

        ArgsTuple args;
        static const unsigned int Arg_Count_ = sizeof...(_Args);

    public:
        LuaGlueStdFuncMethod(LuaGlueClass<_Class> *luaClass, const std::string &name, MethodType &&fn) :
            glueClass(luaClass), name_(name), fn(std::forward<decltype(fn)>(fn))
        { }

        virtual ~LuaGlueStdFuncMethod() { }

        virtual bool glue(LuaGlueBase *luaGlue)
        {
            lua_pushlightuserdata(luaGlue->state(), this);
            lua_pushcclosure(luaGlue->state(), &lua_call_func, 1);
            lua_setfield(luaGlue->state(), -2, name_.c_str());
            return true;
        }

        static int lua_call_func(lua_State *state)
        {
            auto mimp = (LuaGlueStdFuncMethod<void, _Class, _Args...> *)lua_touserdata(state, lua_upvalueindex(1));
            return mimp->invoke(state);
        }

        int invoke(lua_State *state)
        {
            applyTuple<void, _Args...>(glueClass->luaGlue(), state, fn, args);
            lua_pop(state, (int)Arg_Count_);
            return 0;
        }
};

#endif /* LUAGLUE_STDFUNC_METHOD_H_GUARD */

Edit file: LuaGluaClass.h

        // std::function and lambda method
        template<typename _Ret, typename... _Args>
        LuaGlueClass<_Class> &method(const std::string &name, std::function<_Ret(_Args...)> fn)
        {
            auto impl = new LuaGlueStdFuncMethod<_Ret, _Class, _Args...>(this, name, std::forward<decltype(fn)>(fn));
            methods.addSymbol(name.c_str(), impl);

            return *this;
        }


        template<typename F>
        struct BindLambda;
        template<typename Lambda, typename Ret, typename... Args>
        struct BindLambda<Ret(Lambda::*)(Args...) const>
        {
            static std::function<Ret(const Args...)> call(const Lambda& lambda)
            {
                return std::function<Ret(Args...)>(lambda);
            }
        };

        template<typename Lambda>
        LuaGlueClass<_Class> &method(const std::string &name, const Lambda& lambda)
        {
            typedef decltype(&Lambda::operator()) F;

            method(name, BindLambda<F>::call(lambda));
            return *this;
        }
        // end std::function and lambda method

Fix:

Edit LuaGluFunction.h[142]

ReturnType ret = applyTuple(g, state, fn_, args);

Edit LuaGluFunction.h[157]

auto mimp = (LuaGlueFunction<std::function<_Ret(_Args...)>> *)lua_touserdata(state, lua_upvalueindex(1));

Edit LuaGluFunction.h[191]

applyTuple<void, _Args...>(g, state, fn_, args);

Add LuaGlue.h

        //---   std::function   ---//
        template<typename Ret_, typename... Args_>
        LuaGlue &func(const std::string &name, std::function<Ret_(Args_...)> fn)
        {
            auto new_func = new LuaGlueFunction<std::function<Ret_(Args_...)>>(this, name, fn);
            functions.addSymbol(name.c_str(), new_func);
            return *this;
        }

        //---   Lambda   ---//
        template<typename F>
        struct BindLambda;
        template<typename Lambda, typename Ret, typename... Args>
        struct BindLambda<Ret(Lambda::*)(Args...) const>
        {
            static std::function<Ret(Args...)> call(const Lambda& lambda)
            {
                return std::function<Ret(Args...)>(lambda);
            }
        };

        template<typename Lambda>
        LuaGlue &func(const std::string &name, const Lambda& lambda)
        {
            typedef decltype(&Lambda::operator()) F;

            func(name, BindLambda<F>::call(lambda));
            return *this;
        }
@DominikMS DominikMS changed the title Support for lambda Support for lambda and non class Jun 8, 2014
@DominikMS DominikMS changed the title Support for lambda and non class Support for lambda Jun 8, 2014
@DominikMS DominikMS changed the title Support for lambda Support for lambda and unsigned Jun 8, 2014
@DominikMS DominikMS changed the title Support for lambda and unsigned Support for lambda and other numbers than int32_t Jun 8, 2014
@Tomasu
Copy link
Owner

Tomasu commented Jun 8, 2014

Hm that's not a bad idea. It would cut down on the number of stack specializations. Though I'm not sure that is valid for both 32bit and 64bit targets. signedness can become an issue depending on the compiler/platform.

@Tomasu
Copy link
Owner

Tomasu commented Jun 8, 2014

Lamda wise, you can pass them into a std::function and pass that around no? (not that I wont add lambda support, but it is a work around)

@Tomasu
Copy link
Owner

Tomasu commented Jun 8, 2014

That's ok. I'll see about adding lambda support .

As for your setRect example, that's a bit more interesting. I haven't added passing table's back to c++ yet. But your example would likely never be possible as written. setRect would have to take a map or a LuaGlueTable (when ever it gets added). Right now you can just make setRect take x,y,w,h as individual parameters. Theres a "func" overload to put a wrapper around a given method. It's technically called a decorator and lets you transform the inputs to different forms. Helpful for cases like this... it'd look something like: (which probably wont work as shown)

class Rect {
    public:
        void setRect(Rect r);
};

void main(int, char **argv)
{
    LuaGlue state;
    state.Class<Rect>("Rect").
    method("setRect", std::function<void(Rect r,int x,int y,int w, int h)>([]{ r.setRect({ .x = x, .y = y, .w = w, .h = h }); }));

    state.open().glue();

    // ....

    return;
}

I /could/ try and figure out if table properties match a given object's registered properties. I'll look into that as well. It'd be slower than passing things as individual arguments, but it would be handy.

@Tomasu
Copy link
Owner

Tomasu commented Jun 8, 2014

It seems std::function support in g.invokeFunction is broken at the moment. I'm working on a fix. And it may allow binding to lamdas.. but internally it'll turn things into std::function's I think.

@Tomasu
Copy link
Owner

Tomasu commented Jun 9, 2014

Ok, bare lambda if possible, aren't simple (as far as I know, you can't "detect" the type of a lambda in any way). So just wrap them in a std::function for now. I've also added a stdfunc example to test a couple uses of them. I've also fixed some uses of std::function.

@Tomasu
Copy link
Owner

Tomasu commented Jun 14, 2014

Hm, ok. I'll put that on the list of things to add. more complete support for std::function would be useful, like allowing non direct properties to get handed two std::functions to "get" and "set" them.

@Tomasu
Copy link
Owner

Tomasu commented Jun 14, 2014

I was trying to add raw lambda support earlier, but clearly my C++11 knowledge is poor :(. Thats fairly clever :o I'll play with that a bit.

I'm working on a large refactor of the Class/Type support, so I may not get to these requests right away. I have no time frame for when I'll get the big changes in and working. I'll do bug fixes on master in the mean time, but new features and big additions will have to wait till I've got the new branch merged with master.

@Tomasu
Copy link
Owner

Tomasu commented Jun 14, 2014

Also, you know what would help me, is if you could split all of your feature requests into separate issues? That would really help me keep track of them :)

@Tomasu
Copy link
Owner

Tomasu commented Jun 14, 2014

There should be a "Add Labels" column beside the issue form with labels in it you can click, one is "enhancement". if it's not there, I'll have to tag em.

@Tomasu
Copy link
Owner

Tomasu commented Jun 14, 2014

Ok. Not a problem.

@Tomasu
Copy link
Owner

Tomasu commented Jun 14, 2014

Hm, I'd prefer to have issues on each one. Much easier for me to keep track of your requests.

@Tomasu
Copy link
Owner

Tomasu commented Jun 14, 2014

No, one per group of similar additions. like one for the integer additions, one for the std::function stuff, etc. go back and look at your other sumissions as well and make sure each one is only about one thing.

@DominikMS DominikMS changed the title Support for lambda and other numbers than int32_t [ -- additional -- ] Lambda and std::function Jun 14, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants