wrenpp icon indicating copy to clipboard operation
wrenpp copied to clipboard

Binding a free function as a non-static member function

Open OverShifted opened this issue 2 years ago • 5 comments

I'm trying to bind a free function, as a non-static member function of a foreign class. Here is my Wren code:

foreign class MyData {
	foreign value
	foreign value=(rhs)

	foreign twiceValue

	toString { "MyData(%(value))" }
}

// Some where else
class Player {
	// ...
	static puts(obj) {
		System.print("Putting: %(obj)")
		System.print("Putting twice: %(obj.twiceValue)")
	}
}

And here is my C++ code (module names are handled correctly):

class MyData
{
public:
	double value;
};

double twiceValue(MyData& self)
{
	return self.value * 2;
}

// *m_VM is a thin wrapper around `wrenpp::WrenVM` which provides `beginModule` as a wrapper around wrenpp`s version of it.
m_VM->beginModule("src/wren/lib/components")
	.bindClass<MyData>("MyData")
		.bindGetter<decltype(MyData::value), &MyData::value>("value")
		.bindSetter<decltype(MyData::value), &MyData::value>("value=(_)")
		.bindMethod<decltype(twiceValue), twiceValue>(false, "twiceValue")
	.endClass()
.endModule();

// Loading modules...

m_VM->GetWrenpp().method("src/wren/test", "Player", "puts(_)")(MyData{12});

When I run it, I get:

Putting: MyData(12)

And then it segfaults. Using a static function (binding twiceValue as static and passing instance as its first parameter) or a member function works fine.

OverShifted avatar Jan 03 '23 10:01 OverShifted

I'm not a contributor to this repository, but I'm fairly familiar with the codebase. Anyhow, here's my 2 cents.

Firstly, why do you have a free function that takes a reference to MyData? Don't you think it'd be easier to simply put it inside the class? Also, since you are placing it outside of the class, it really should be static.

Secondly, you are binding to a function that expects one parameter, but you provide none in the Wren version, so this won't work.

Here's my advice, put the method inside the class and you can remove the self-reference parameter, then the code would do what you expect it to and it will be cleaner too, hope this helps!

ConorDamery avatar Apr 29 '23 01:04 ConorDamery

@ConorDamery Thanks for the reply! I'm trying to keep my C++ API simple and avoid adding extra methods. Sometimes I can't even do that because of some "C-style" APIs. For example, I'm using a library that provides a vec3 class (Its C++), along with a length free function which computes the length of the vector. The vec3 class is bound to wren along with a constructor. I wish I had a .length property on the wren class which calls the C++ length function under the hood. In this situation, I can't add a new method to the C++ class.

OverShifted avatar May 01 '23 16:05 OverShifted

You can still do this. I'm going to assume the math library you are referring to is glm? Anyway, there's nothing preventing you from wrapping glm into your own vec3 class which mirrors the wren implementation. And if you really don't want to do that, then you can still mirror the wren implementation by providing a static method in your wren class which takes a const vec3 reference.

For example:

// wren
foreign class vec3 {
    // constructor, etc...
    foreign static length(v)
}
// cpp
.bindMethod<decltype(glm::length<glm::vec3>), &glm::length<glm::vec3>>(true, "length(_)")

ConorDamery avatar May 01 '23 23:05 ConorDamery

I dont want to wrap the glm type just for scripting purposes. And the second solution is exactly what I'm currently doing and trying to not do.

On Tue, May 2, 2023, 3:06 AM ConorDamery @.***> wrote:

You can still do this. I'm going to assume the math library you are referring to is glm? Anyway, there's nothing preventing you from wrapping glm into your own vec3 class which mirrors the wren implementation. And if you really don't want to do that, then you can still mirror the wren implementation by providing a static method in your wren class which does take a const vec3 reference (note the const reference is what really matters as I mentioned before). For example:

// wren foreign class vec3 { // constructor, etc... foreign static length(v) }

// cpp .bindMethod<decltype(glm::lengthglm::vec3), &glm::lengthglm::vec3>(true, "length(_)")

— Reply to this email directly, view it on GitHub https://github.com/Nelarius/wrenpp/issues/20#issuecomment-1530573115, or unsubscribe https://github.com/notifications/unsubscribe-auth/AMZGPZRQTRK4FDTUVDG7R3TXEBCIZANCNFSM6AAAAAATPQQ5QM . You are receiving this because you authored the thread.Message ID: @.***>

OverShifted avatar May 02 '23 12:05 OverShifted

Alright fair enough, well my only other approach is to try hacking it using wren semi-directly, which should work in theory (the code is not tested!).

.bindCFunction(false, "length", [](WrenVM* vm)
{
    const glm::vec3& v = WrenSlotAPI<const glm::vec3&>::get(vm, 0); // First get the object from slot 0
    float l = glm::length(v);
    WrenSlotAPI<float>::set(vm, 0, l); // Now we set the return value to slot 0 as usual
})

ConorDamery avatar May 03 '23 20:05 ConorDamery