OOLUA::push && OOLUA::pull self-defined type (not using raw pointer or shared_ptr)

Issue #10 invalid
Oscar Zhao
created an issue

by OOLUA::push (OOLUA::pull), I can push (pull) a basic-type variable in (out of) the lua stack, but not for variable with self-defined type. Perhaps the problem is similar to issue #8. Now, using the two function templates, the functions I need is implemented in a strange way, I think. The code and the comments in it give more details.

#include <iostream>
#include "oolua.h"

using std::cout;
using std::endl;
using std::ios;

class Apple
{
public:
    Apple():m_weight(200), m_color("red"){}
    Apple(double weight, std::string color): m_weight(weight),m_color(color){}
    double getWeight() const { return m_weight; }
    std::string getColor() const { return m_color;}
    ~Apple(){}
private:
    double m_weight;
    std::string m_color;
};

// proxy
OOLUA_PROXY(Apple)
    OOLUA_CTORS(
        OOLUA_CTOR(double, std::string)
    )
    OOLUA_MFUNC_CONST(getWeight)
    OOLUA_MFUNC_CONST(getColor)
OOLUA_PROXY_END
// export
OOLUA_EXPORT_FUNCTIONS(Apple)
OOLUA_EXPORT_FUNCTIONS_CONST(Apple, getWeight, getColor)

void stackDump(lua_State* L)
{
    int i;
    int top = lua_gettop(L);
    if(0 == top)
    {
        cout << "Empty Stack\n";
        return;
    }
    cout << "Stack Content: ";
    for (i = 1; i <= top; i++)
    {
        int t = lua_type(L, i);
        switch(t){
            case LUA_TSTRING:
                std::cout << "'" << lua_tostring(L, i) << "'";
                break;
            case LUA_TBOOLEAN:
                std::cout << lua_toboolean(L, i)? "true": "false";
                break;
            case LUA_TNUMBER:
                std::cout << lua_tonumber(L, i);
                break;
            default:
                std::cout << lua_typename(L, t);
                break;
        }

        std::cout << "    "; // put a separator
    }
    std::cout << std::endl;
}

void printApple(const Apple & app)
{
    cout << "Weight: " << app.getWeight() << " kg\n";
    cout << "Color:  " << app.getColor() << endl;
}

int main()
{
    OOLUA::Script* vm = new OOLUA::Script;
    vm->register_class<Apple>();

    vm->run_chunk("function func() return Apple.new(100, 'cyan') end");
    vm->call("func");
    stackDump(*vm);
    // first, create an object in lua script, and pull it into a raw pointer
    // it works, but I do not know where the memory goes, auto delete???
    Apple * rawPtr;
    OOLUA::pull(*vm, rawPtr);
    printApple(*rawPtr);
    // delete cannot be used
    //delete result; 

    stackDump(*vm);

    // second, create a shared_ptr, push and pull
    // auto delete!!!
    boost::shared_ptr<Apple> sharedPtr = boost::shared_ptr<Apple>(new Apple(100, "cyan"));
    OOLUA::push(*vm, sharedPtr);
    stackDump(*vm);
    boost::shared_ptr<Apple> sharedPtr2;
    OOLUA::pull(*vm, sharedPtr2);
    printApple(*sharedPtr2);

    stackDump(*vm);

    // third, create an object in cpp, push and pull
    // does not work

    Apple appObj(100, "cyan");
    OOLUA::push(*vm, appObj);
    stackDump(*vm);
    Apple appObj2;
    OOLUA::pull(*vm, appObj2);

    stackDump(*vm);


    // fourth, create an object in cpp, push its address and pull into a raw pointer
    // it works, but I do not know where the memory goes, same to curcumstance first
    Apple appObj3(100, "cyan");
    OOLUA::push(*vm, &appObj3);
    stackDump(*vm);
    Apple * appPtr3;
    OOLUA::pull(*vm, appPtr3);
    printApple(*appPtr3);

    stackDump(*vm);
    // cannot delete appPtr3
    //delete appPtr3;

    delete(vm);
    system("pause");
    return 0;
}

the "third" part of code report the following error:

1>------ Build started: Project: Simulate_Call_Function, Configuration: Debug Win32 ------
1>Compiling...
1>main.cpp
1>Linking...
1>main.obj : error LNK2019: unresolved external symbol "bool __cdecl OOLUA::STRING::push<class Apple>(struct lua_State * const,class Apple const &)" (??$push@VApple@@@STRING@OOLUA@@YA_NQAUlua_State@@ABVApple@@@Z) referenced in function "public: static bool __cdecl OOLUA::INTERNAL::push_basic_type<class Apple,0,0>::push_imp(struct lua_State * const,class Apple const &,struct LVD::Int2type<0>)" (?push_imp@?$push_basic_type@VApple@@$0A@$0A@@INTERNAL@OOLUA@@SA_NQAUlua_State@@ABVApple@@U?$Int2type@$0A@@LVD@@@Z)
1>main.obj : error LNK2019: unresolved external symbol "bool __cdecl OOLUA::STRING::pull<class Apple>(struct lua_State * const,class Apple &)" (??$pull@VApple@@@STRING@OOLUA@@YA_NQAUlua_State@@AAVApple@@@Z) referenced in function "public: static bool __cdecl OOLUA::INTERNAL::pull_basic_type<class Apple,0,0>::pull_imp(struct lua_State * const,class Apple &,struct LVD::Int2type<0>)" (?pull_imp@?$pull_basic_type@VApple@@$0A@$0A@@INTERNAL@OOLUA@@SA_NQAUlua_State@@AAVApple@@U?$Int2type@$0A@@LVD@@@Z)
1>E:\work\Git\OOLua\OOLuaTest\_runtime\Debug\\Simulate_Call_Function.exe : fatal error LNK1120: 2 unresolved externals
1>Build log was saved at "file://E:\work\Git\OOLua\OOLuaTest\_builder\Debug\Simulate_Call_Function\BuildLog.htm"
1>Simulate_Call_Function - 3 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Comments (4)

  1. Oscar Zhao reporter

    I'm confused by how the memory goes when the ownership of proxy types trasfers. By using cpp_acquire_ptr, the ownership transfers from lua to cpp, so the programmer could release the memory when appropriate. By using lua_acquire_ptr, the ownership transfers from cpp to lua, so the release of memory are under the control of gc, in my understanding. In this way, I get some conclusions as well as questions, but I do not if they are correct. Conclusion one, if I "OOLUA::pull" the userdata into a raw pointer (case "first"), lua has the ownership of the data, so it's not valid to do "delete" operation to the raw pointer and gc would deal with the data; Conclusion two, in the documentation for ownership of proxy types[1], by using lua_acquire_ptr, the ownership of "stub" (type:Stub1) goes to lua, so the gc would release the memory ( but the destructor of "stub" also release the memory, confusing). My question: if I want to use OOLUA::pull to fetch data from the lua stack, the type of data can be basic types, self-defined types, or boost::shared_ptr, do I have to use cpp_acquire_ptr<T>? Could I use the way in case "second"? thanks

  2. Liam Devine repo owner

    Conclusion one, if I "OOLUA::pull" the userdata into a raw pointer (case "first"), lua has the ownership of the data, so it's not valid to do "delete" operation to the raw pointer and gc would deal with the data;

    You are correct to say that you can not call delete on it. However depending on the circumstances, such as your case, it can also may not be safe to dereference the pointer. You have returned a newly constructed pointer which has no references in Lua's alive object tree and the pull call will pop the instance of the stack. If the gc is called after the instance is removed from the stack it can be freed by the gc and therefore also C++. This is the reason for the trait which and why it sets the owner before the removal from the stack.

    Conclusion two, in the documentation for ownership of proxy types[1], by using lua_acquire_ptr, the ownership of "stub" (type:Stub1) goes to lua, so the gc would release the memory ( but the destructor of "stub" also release the memory, confusing).

    Hence why the code calls OOLUA::INTERNAL::userdata_gc_value(ud, false); which sets the value to false. Maybe I should change the example, the code in question is testing the internal gc value before it resets it so as not to delete a stack object. When the value is reset Lua will never try to call delete on that instance.

    My question: if I want to use OOLUA::pull to fetch data from the lua stack, the type of data can be basic types,

    You do not pull basic types by pointer.

    self-defined types, or boost::shared_ptr, do I have to use cpp_acquire_ptr<T>?

    Firstly let me ask you a question. If you were to pull a shared_ptr with an ownership trait, what would this mean and what would you expect to happen?

    self-defined types,

    You have to know your data and its life time. If you want to take control of the life time then use this trait and delete it when you are finished. However, you need to aware that you can create a situation in which you have a dangling pointer still in Lua as you can in C or C++.

    Could I use the way in case "second"?

    Yes, did you encounter a problem? You have pushed and pulled a shared_ptr and both shared_ptrs reference the same pointer. When the last shared_ptr which references this pointer goes out of scope it will be deleted in it's destructor(unless you have set a deleter function in the shared_ptr that does something different).

    Please note that this whole tracker thread has been about queries rather than bugs. Can we please keep queries to the mailing list and not the issue tracker which I request we reserve for bugs.

  3. Log in to comment