Selene icon indicating copy to clipboard operation
Selene copied to clipboard

Add suport to abstract class

Open alextrevisan opened this issue 10 years ago • 4 comments

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.

alextrevisan avatar Aug 05 '14 19:08 alextrevisan

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

jeremyong avatar Aug 09 '14 20:08 jeremyong

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;
}

alextrevisan avatar Aug 27 '14 21:08 alextrevisan

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).

jeremyong avatar Aug 28 '14 06:08 jeremyong

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;
}

alextrevisan avatar Sep 08 '14 22:09 alextrevisan