jeremyong / Selene

Simple C++11 friendly header-only bindings to Lua
zlib License
813 stars 117 forks source link

Add suport to abstract class #46

Open alextrevisan opened 10 years ago

alextrevisan commented 10 years ago

I'm trying to add some abstract classes Ogre3D library, but without success.

It would be good to have support for this.

Ogre3D have some abstract classes, like Ogre::SceneManager, and we create a instance using Ogre::Root::createSceneManager and return a scene managers like Octree, BSP or Generic.

jeremyong commented 10 years ago

Can you provide a simple test case demonstrating what you would want and the functionality you expect?

alextrevisan commented 10 years ago

here a simple test case

#include <iostream>
#include <selene.h>
class SceneManager
{
    public:
    virtual int GetX() = 0;
};
class Octree: public SceneManager {
    public:
    int x;
    Octree() {
        x = 0;
    }
    int GetX() {
        return x;
    }
};
class Root {
    public:
    int x;
    Root(int num) { x = num; printf("created BAR\n"); }
    void SetX(int x2) {
        x = x2;
    }
    SceneManager *createOctreeSceneManager()
    {
        return new Octree();
    }
};
int main()
{
    sel::State state;
    state["Root"].SetClass<Root, int>("createOctreeSceneManager", &Root::createOctreeSceneManager);
    //ERROR
    state["SceneManager"].SetClass<SceneManager, int>("GetX", &SceneManager::GetX);
    return 0;
}
jeremyong commented 10 years ago

OK I see. This fails currently because the SetClass function in Selene tries to automatically create a constructor. I can understand how this could be useful and I can probably add it as a separate method (SetAbstractClass).

alextrevisan commented 10 years ago

Hi, I tried to make some changes but when I call 'print(sm:GetX())', i get nothing , and I don't know why.

//=========================Selector.h
template <typename T, typename... Funs>
    void SetAbstractClass(Funs... funs) {
        _traverse();
        auto fun_tuple = std::make_tuple(funs...);
        auto push = [this, &fun_tuple]() {
            typename detail::_indices_builder<sizeof...(Funs)>::type d;
            _registry.RegisterAbstractClass<T>(_name, fun_tuple, d);
        };
        _put(push);
        lua_settop(_state, 0);
    }
//=========================Registry.h
template <typename T, typename... Funs, size_t... N>
    void RegisterAbstractClass(const std::string &name, std::tuple<Funs...> funs,
                       detail::_indices<N...>) {
        RegisterAbstractClassWorker<T>(name, std::get<N>(funs)...);
    }
    template <typename T, typename... Funs>
    void RegisterAbstractClassWorker(const std::string &name, Funs... funs) {
        auto tmp = std::unique_ptr<BaseClass>(
            new AbstractClass<T, Funs...>
            {_state, _metatables, name, funs...});
        _classes.push_back(std::move(tmp));
    }
//=========================Added on Class.h

template <typename T,
          typename... Members>
class AbstractClass : public BaseClass {
private:
    bool _should_erase = true;
    std::string _name;
    std::string _metatable_name;
    //std::unique_ptr<A> _ctor;
    std::unique_ptr<Dtor<T>> _dtor;
    using Funs = std::vector<std::unique_ptr<BaseFun>>;
    Funs _funs;
    MetatableRegistry& _meta_registry;

    /*void _register_ctor(lua_State *state) {
        _ctor.reset(new A(state, _metatable_name.c_str()));
    }*/

    void _register_dtor(lua_State *state) {
        _dtor.reset(new Dtor<T>(state, _metatable_name.c_str()));
    }

    template <typename M>
    void _register_member(lua_State *state,
                          const char *member_name,
                          M T::*member) {
        _register_member(state, member_name, member,
                         typename std::is_const<M>::type{});
    }

    template <typename M>
    void _register_member(lua_State *state,
                          const char *member_name,
                          M T::*member,
                          std::false_type) {
        std::function<M(T*)> lambda_get = [member](T *t) {
            return t->*member;
        };
        _funs.emplace_back(
            new ClassFun<1, T, M>
            {state, std::string{member_name}, _metatable_name.c_str(), lambda_get});

        std::function<void(T*, M)> lambda_set = [member](T *t, M value) {
            (t->*member) = value;
        };
        _funs.emplace_back(
            new ClassFun<0, T, void, M>
            {state, std::string("set_") + member_name,
                    _metatable_name.c_str(), lambda_set});
    }

    template <typename M>
    void _register_member(lua_State *state,
                          const char *member_name,
                          M T::*member,
                          std::true_type) {
        std::function<M(T*)> lambda_get = [member](T *t) {
            return t->*member;
        };
        _funs.emplace_back(
            new ClassFun<1, T, M>
            {state, std::string{member_name},
                    _metatable_name.c_str(), lambda_get});
    }

    template <typename Ret, typename... Args>
    void _register_member(lua_State *state,
                          const char *fun_name,
                          Ret(T::*fun)(Args...)) {
        std::function<Ret(T*, Args...)> lambda = [fun](T *t, Args... args) {
            return (t->*fun)(args...);
        };
        constexpr int arity = detail::_arity<Ret>::value;
        _funs.emplace_back(
            new ClassFun<arity, T, Ret, Args...>
            {state, std::string(fun_name), _metatable_name.c_str(), lambda});
    }

    template <typename Ret, typename... Args>
    void _register_member(lua_State *state,
                          const char *fun_name,
                          Ret(T::*fun)(Args...) const) {
        std::function<Ret(const T*, Args...)> lambda =
            [fun](const T *t, Args... args) {
                return (t->*fun)(args...);
            };
        constexpr int arity = detail::_arity<Ret>::value;
        _funs.emplace_back(
            new ClassFun<arity, const T, Ret, Args...>
            {state, std::string(fun_name), _metatable_name.c_str(), lambda});
    }

    void _register_members(lua_State *state) {}

    template <typename M, typename... Ms>
    void _register_members(lua_State *state,
                           const char *name,
                           M member,
                           Ms... members) {
        _register_member(state, name, member);
        _register_members(state, members...);
    }

public:
    AbstractClass(lua_State *state,
          MetatableRegistry &meta_registry,
          const std::string &name,
          Members... members) : _name(name), _meta_registry(meta_registry) {
        _metatable_name = _name + "_lib";
        _meta_registry.Insert(typeid(T), _metatable_name);
        luaL_newmetatable(state, _metatable_name.c_str());
        _register_dtor(state);
        //*removed* _register_ctor(state);
        _register_members(state, members...);
        lua_pushvalue(state, -1);
        lua_setfield(state, -1, "__index");
    }
    ~AbstractClass() {
        if (_should_erase) _meta_registry.Erase(typeid(T));
    }
    AbstractClass(const AbstractClass &) = delete;
    AbstractClass& operator=(const AbstractClass &) = delete;
    AbstractClass(AbstractClass &&other)
        : _should_erase{true}
        , _name{std::move(other._name)}
        , _metatable_name{std::move(other._metatable_name)}
        // *removed* , _ctor{std::move(other._ctor)}
        , _dtor{std::move(other._dtor)}
        , _funs{std::move(other._funs)}
        , _meta_registry{other._meta_registry} {
        other._should_erase = false;
    }
    AbstractClass& operator=(AbstractClass &&other) {
        if (&other == this) return *this;
        _name = std::move(other._name);
        _metatable_name = std::move(other._metatable_name);
        //*removed* _ctor = std::move(other._ctor);
        _dtor = std::move(other._dtor);
        _funs = std::move(other._funs);
        _meta_registry = other._meta_registry;
        other._should_erase = false;
        _should_erase = true;
        return *this;
    }
//=========================main.cpp
#include <iostream>
#include <selene.h>
class SceneManager
{
    public:
    virtual int GetX() = 0;
};
class Octree: public SceneManager {
    public:
    int x;
    Octree() {
        x = 0;
    }
    int GetX() {
        return x;
    }
};
class Root {
    public:
    int x;
    Root(int num) { x = num; printf("created Root\n"); }
    void SetX(int x2) {
        x = x2;
    }
    SceneManager *createOctreeSceneManager()
    {
         printf("created OctreeSceneManager\n");
        return new Octree();
    }
};
int main()
{
    sel::State state;
    state["Root"].SetClass<Root, int>("createOctreeSceneManager", &Root::createOctreeSceneManager);
    state["SceneManager"].SetAbstractClass<SceneManager>("GetX", &SceneManager::GetX);
    state("root = Root.new(5); sm = root:createOctreeSceneManager(); print(sm:GetX())");
    //prints:
    //created Root
    //created OctreeSceneManager
    return 0;
}