Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing Lua self object from a C++ function #1559

Open
akovachev opened this issue Dec 3, 2023 · 4 comments
Open

Accessing Lua self object from a C++ function #1559

akovachev opened this issue Dec 3, 2023 · 4 comments

Comments

@akovachev
Copy link

akovachev commented Dec 3, 2023

I need to call a Lua function attached to the object instance from C++ class member function.

In other words I need to pass the self object to the function when calling from C++. If I know the name of the object I can access it by state["objectName"] and pass it. But unless I cache it somewhere I can't really convert this pointer to self object. I'm looking for an elegant mechanism to do this as I have a ton of bound C++ classes that need to call custom functions.

Ideally I'd want to do this at construction time. Perhaps by getting access to the Lua table object as a constructor parameter? Would that even work, or is the table created after the object has been constructed?

class Cpp {
public:
...
void callLuaMemberFunction {
if (luaMemberFunc) {
luaMemberFunc(, params...);
}
}

void bindLuaFunction(const std::string& key, sol::function func) {
if (key == "memberFunc") {
luaMemberFunc = std::move(func);
}
// ... [Handle other overridable functions similarly]
}

private:
sol::function luaMemberFunc;
}

at binding time:

cpp.set(sol::meta_function::new_index, [](Cpp& _this, const std::string& key, sol::function func) {
_this.bindLuaFunction(key, func);
});

Lua:

cpp = Cpp()
cpp:memberFunc = function(...)
end

Currently I'm solving this by passing the name to the object at construction which then looks up the object by name every call. A typo could be an issue if we pass a different name than the object's assigned name. Something I'd like to avoid. I also want to avoid caching the object for each Lua member function (I'm assuming I'd have access to it at new_index call).

@Rochet2
Copy link

Rochet2 commented Dec 3, 2023

Would something like this work for you?

#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>

struct Cpp {
};

int main() {
    sol::state lua;
    lua.open_libraries(sol::lib::base, sol::lib::package);

    auto cpp_meta = lua.new_usertype<Cpp>("Cpp");

    lua.script(R"(
        Cpp.memberFunc = function(...)
            print(...)
        end

        cpp = Cpp.new()
        cpp:memberFunc("hello world!")
        -- prints sol.Cpp: 0x5b0038	hello world!
    )");
    return 0;
}

Its not what you ask, but it achieves what you set out to do in your code example from what I see.

@akovachev
Copy link
Author

akovachev commented Dec 3, 2023 via email

@akovachev
Copy link
Author

akovachev commented Dec 3, 2023

In short I need something like returns_self, but instead of returning it, for it to be passed in as sol::object or sol::reference to a bound member function.

Attaching one of the tests using returns_self from sol::policies:

TEST_CASE("policies/self", "ensure we return a direct reference to the lua userdata rather than creating a new one") {
	struct vec2 {
		float x = 20.f;
		float y = 20.f;

		vec2& normalize() {
			float len2 = x * x + y * y;
			if (len2 != 0) {
				float len = sqrtf(len2);
				x /= len;
				y /= len;
			}
			return *this;
		}

		~vec2() {
			x = std::numeric_limits<float>::lowest();
			y = std::numeric_limits<float>::lowest();
		}
	};

	sol::state lua;
	lua.open_libraries(sol::lib::base);

	lua.new_usertype<vec2>("vec2", "x", &vec2::x, "y", &vec2::y, "normalize", sol::policies(&vec2::normalize, sol::returns_self()));

	auto result1 = lua.safe_script(R"(
v1 = vec2.new()
print('v1:', v1.x, v1.y)
v2 = v1:normalize()
print('v1:', v1.x, v1.y)
print('v2:', v2.x, v2.y)
print(v1, v2)
assert(rawequal(v1, v2))
v1 = nil
collectgarbage()
print(v2) -- v2 points to same, is not destroyed
		)",
	     sol::script_pass_on_error);
	REQUIRE(result1.valid());
}

@akovachev
Copy link
Author

I think I found the solution, it's not super obvious, but there is a working example:

https://github.com/ThePhD/sol2/blob/develop/examples/source/self_from_lua.cpp

The key is to dig inside the state's stack using this_state (from the example above):

		thing(sol::this_state ts) {
			lua_State* L = ts;
			// references the object that called this function
			// in constructors:
			sol::stack_object selfobj(L, 1);

			// definitely the same
			thing& self = selfobj.as<thing>();
			SOL_ASSERT(&self == this);
		}

		void func(sol::this_state ts) const {
			lua_State* L = ts;
			// references the object that called this function
			// in regular member functions:
			sol::stack_object selfobj(L, 1);
			// "1" is the bottom of the Lua stack
			// 2 is one up, so on and so forth...
			thing& self = selfobj.as<thing>();

			// definitely the same
			SOL_ASSERT(&self == this);
		}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants