satoren / kaguya

C++ binding to Lua
Boost Software License 1.0
345 stars 70 forks source link

UserdataMetatable enable custom gc function. #28

Closed aster2013 closed 8 years ago

aster2013 commented 8 years ago

Sometimes user want to custom __gc function for some userdata, for example: I have a RefCounted object, when GC, it may call following function.

void RefCountedGC(RefCounted* obj)
{
    obj->ref --;
    if (obj->ref == 0)
        delete obj;
}

So it is better add a argument to UserdataMetatable enable user custom the gc funciton.

UserdataMetatable(bool registerGC= true)
{
    if (registerGC)
    {
        addStaticFunction("__gc", &class_userdata::destructor<ObjectWrapperBase>);
    }
satoren commented 8 years ago

Why don't you use shared_ptr?

state["data"] = std::shared_ptr<RefCounted>(refcounted_ptr,RefCountedGC) ;
aster2013 commented 8 years ago

it is an existed game engine, it use tolua to binding cpp code to lua, as you know tolua is slow and has memory leak with ref object.

satoren commented 8 years ago

How unique_ptr? example: e0e4009aff982cbf223ef50cf37d62efd34f80fe

satoren commented 8 years ago

What are you doing to care? Performance cost of unique_ptr is less than shared_ptr, and neary equal raw pointer. Them member function invoke cost is nearly equal.

member function invoke performance benchmark results:

data type seconds benchmark function name
allocated by lua_newuserdata data with plain API 1.675 original_api_no_type_check::simple_get_set
allocated by lua_newuserdata data 1.554 kaguya_api_benchmark__::simple_get_set
raw pointer 1.729 kaguya_api_benchmark__::simple_get_set_raw_ptr
shared_ptr 1.784 kaguya_api_benchmark__::simple_get_set_shared_ptr
unique_ptr 1.773 kaguya_api_benchmark__::simple_get_set_unique_ptr
aster2013 commented 8 years ago

I am working on the Urho3D game engine, Urho3D/new-luascript-test branch, If you have spare time, please take a look on it.

satoren commented 8 years ago

Wow, interesting work.

satoren commented 8 years ago

Please import e9214b955f1d021c1bc5c5c1fec484f3ecd51eef this fix and specialized for Urho3D::SharedPtr. e.g.

    // Urho3D::SharedPtr<T>
    template<typename T> struct lua_type_traits<Urho3D::SharedPtr<T> > {
        typedef Urho3D::SharedPtr<T>& push_type;
        typedef Urho3D::SharedPtr<T>& get_type;
        typedef Urho3D::SharedPtr<T> type;

        //If need Urho3D::SharedPtr type get.
        static bool strictCheckType(lua_State* l, int index)
        {
            return object_wrapper<type>(l, index, false) != 0;
        }

        //If need Urho3D::SharedPtr type get.
        static bool checkType(lua_State* l, int index)
        {
            return object_wrapper<type>(l, index) != 0 ||
                lua_isnil(l, index);
        }

        //If need Urho3D::SharedPtr type get.
        static get_type get(lua_State* l, int index)
        {
            type* pointer = get_pointer(l, index, types::typetag<type>());
            if (!pointer)
            {
                throw LuaTypeMismatch("type mismatch!!");
            }
            return *pointer;
        }

        static int push(lua_State* l, push_type v)
        {
            if (v)
            {
                typedef ObjectSmartPointerWrapper<type,T> wrapper_type;
                void *storage = lua_newuserdata(l, sizeof(wrapper_type));
                new(storage) wrapper_type(v);
                class_userdata::setmetatable<T>(l);
            }
            else
            {
                lua_pushnil(l);
            }
            return 1;
        }
    };
aster2013 commented 8 years ago

What I want is like this:

template<typename T>
SharedPtr<T> KCreateObject()
{
    SharedPtr<T> tPtr(new T(globalContext));
    return tPtr;
}

template<typename T>
void KReleaseObject(T* t)
{
    if (t)
        t->ReleaseRef();
}

template<typename T> struct lua_type_traits<Urho3D::SharedPtr<T>>
{
    typedef const Urho3D::SharedPtr<T>& push_type;

    static int push(lua_State* l, push_type p)
    {
        T* o = p;
        o->AddRef();

            // Push the raw pointer to Lua
        return util::push_args(l, o);
    }
};

So current I disable the default gc function in UserdataMetatable constructor, and add custom gc function.

lua["KImage"].setClass(UserdataMetatable<Image, Resource>(false)  // don't use defaut gc function
    .addStaticFunction("new", &KCreateObject<Image>) // Custom new function
    .addStaticFunction("__gc", &KReleaseObject<Image>) // Custom gc function

    // ...
satoren commented 8 years ago

Why want? kaguya is wrapped for all user data by ObjectWrapper. That is raw-pointer too. Default gcfunction is call ObjectWrapperBase’s destructor.Your code has not been called.

aster2013 commented 8 years ago

Thanks, you are right. I have write a shared ptr object wrapper. Please check it

satoren commented 8 years ago

OK!