Rapptz / sol

A C++11 Lua wrapper

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem with userdata and metatable

DominikMS opened this issue · comments

    ClassA* popClass()
    {
        return this;
    }

local A = ClassA:new()
local B = A:popClass()
B:test() -- here is crash

error code:
terminate called after throwing an instance of 'sol::error'
what(): lua: error: script.lua:4: attempt to index local 'B' (a userdata value)

What exactly do you expect the behaviour to be? As it is now, I can't imagine this being very helpful considering Lua's notion of a pointer is different than C++'s, i.e. you can't exactly modify the pointee and what have you.

This should be usefull example for:
local image = Image:new("ball.png") -- this is class
widget:setImage(image) -- crashed engine

Why image is classed? setting image of my widget by string and searching in base is stupid.
There is not only one problem.

How to fix this? In my old project i have used, but here is not working:

    template<class C>
    void pushValue(C* value)
    {
        CLASS_NAME(C)
        std::string metaName = "mt_"+ className; // your is "sol.~.classname"

        C** udata = (C**)lua_newuserdata(m_state, sizeof(C*));
        *udata = value;

        luaL_getmetatable(m_state, metaName.c_str());
        lua_setmetatable(m_state, -2);
    }

I'm not sure I get what you mean. Is this about inheritance and/or passing objects by value?

At the moment the way to pass objects by parameter is to have something like this:

#include <sol.hpp>
#include <iostream>

struct image {
    int width, height;
    image(int w, int h): width(w), height(h) {}
};

struct widget {
    void set_image(sol::object t) {
        if(t.is<sol::userdata<image>>()) {
            auto&& img = t.as<sol::userdata<image>>();
            std::cout << img.width << ", " << img.height;
        }
    }
};

int main() {
    sol::state lua;
    lua.new_userdata<image, int, int>("Image");
    lua.new_userdata<widget>("Widget", "setImage", &widget::set_image);
    lua.script("image = Image.new(100, 20)\n"
               "widget = Widget.new()\n"
               "widget:setImage(image)\n");
}

This is what i need, but with this method i have to add secondary function:
void set_image(Image image)

And! struct image is not scalar, i am not sure if this is problem. Tomorrow i will check this.

Fixed in this pull request in a more elegant manner. Note that you will have to take the "widget" or "image" classes by reference to prevent C++ from making copies and operating on copies of the data (as per usual C++ semantics).

Like i say, this is not working for scalars.
Working code:

        ClassA popClass()
        {
            std::cout << "address: " << this << std::endl;
            return *this;
        }

Crashed code:

        ClassA* popClass()
        {
            std::cout << "address: " << this << std::endl;
            return this;
        }

        bool test(int a)
        {
            std::cout << "ClassA->test() : " << m_str << std::endl;
            return true;
        }
local C = A:popClass()
C:test(6)

@edit:
And this difference:
void popClass(ClassA x) // working
void popClass(ClassA* x) // not working

Fixed.