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

Embind port #7239

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft

Embind port #7239

wants to merge 38 commits into from

Conversation

kripken
Copy link
Member

@kripken kripken commented Jan 23, 2025

For #6225

Basic stuff works, but I am having trouble with the return policy @brendandahl

I am doing

#define DELEGATE_FIELD_NAME_VECTOR(id, field)                                  \
  .property(#field, &id::field, return_value_policy::reference())

Reference is what I want for fields which are vectors of names - we don't want to copy them, and they'll be around while the object is. But I get

emscripten/cache/sysroot/include/emscripten/bind.h:794:20: error: object of type 'ArenaVector<wasm::Name>' cannot be assigned because its copy assignment operator is implicitly deleted
  794 |         ptr.*field = MemberBinding::fromWireType(value);
      |                    ^
emscripten/cache/sysroot/include/emscripten/bind.h:1870:69: note: in instantiation of function template specialization 'emscripten::internal::MemberAccess<wasm::Switch, ArenaVector<wasm::Name>>::setWire<wasm::Switch>' requested here
 1870 |         auto setter = &MemberAccess<ClassType, FieldType>::template setWire<ClassType>;
      |                                                                     ^
3-binaryen/src/wasm-delegations-fields.def:287:1: note: in instantiation of function template specialization 'emscripten::class_<wasm::Switch, emscripten::base<wasm::Expression>>::property<ArenaVector<wasm::Name>, emscripten::return_value_policy::reference, void>' requested here
  287 | DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(Switch, targets)
      | ^
3-binaryen/src/binaryen-embind.cpp:99:4: note: expanded from macro 'DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR'
   99 |   .property(#field, &id::field, return_value_policy::reference())
      |    ^
3-binaryen/src/mixed_arena.h:402:3: note: copy assignment operator is implicitly deleted because 'ArenaVector<wasm::Name>' has a user-declared move constructor
  402 |   ArenaVector(ArenaVector<T>&& other) : allocator(other.allocator) {
      |   ^
In file included from 3-binaryen/src/binaryen-embind.cpp:21:

That requirement for a move constructor or copy assignment operator confuse me, as the docs for a return value policy of reference say "Constructor: none". What am I doing wrong?

@brendandahl
Copy link
Collaborator

When using .property(#field, &id::field, ...) embind creates a getter/setter method for the field. In this case you can't assign to ArenaVector<wasm::Name> in a setter. You probably don't want a setter in this case, you can do a few things:

  1. Make the field const so embind only generates a getter.
  2. Add a getter method:
const ArenaVector<X>* getField() const {
    return &field;
}
...
property("field", &Foo::getField, return_value_policy::reference());

@brendandahl
Copy link
Collaborator

I played a bit more with your example and I am now running into some other issues using a getter. Since ArenaVector extends ArenaVectorBase<ArenaVector<int>, int> the bindings for all the methods in ArenaVectorBase<ArenaVector<int>, int> need to be put in their own class_ bindings. e.g.

  class_<ArenaVectorBase<ArenaVector<int>, int>>("Base")
    .function("insertAt", &ArenaVectorBase<ArenaVector<int>, int>::insertAt) 
    .function("getAt", &ArenaVectorBase<ArenaVector<int>, int>::operator[])
  ;
  class_<ArenaVector<int>, base<ArenaVectorBase<ArenaVector<int>, int>>>("Derived");

However, if I then try to do something like arena.insertAt(...) embind then fails with Cannot convert argument of type Derived const* to parameter type Base* . This is because the getter for the arena returns a const *, but then tries to convert the pointer to the base class as a non const pointer.

I'm still thinking about how we could better handle this, but a current alternative is to not use a property binding but instead use a function binding.

class_<Foo>("Foo").function("getField", +[](Foo& foo) { return &foo.field; }, return_value_policy::reference());

@brendandahl
Copy link
Collaborator

I thought about this a little more and the Cannot convert argument of type... does make sense in this case. We have a const pointer to ArenaVector so it makes sense we can't call non-const method e.g. insertAt. Calling operator[] works as expected with the const pointer since it's marked const. So if you don't need to write to the vector I guess a const pointer is fine otherwise you'll need to use a function binding like I mentioned above.

@kripken
Copy link
Member Author

kripken commented Jan 24, 2025

Thanks! I'll try a function here. I don't think we need to write to the vector, which is good.

@kripken
Copy link
Member Author

kripken commented Jan 24, 2025

That gets me a little further. Now I see that some of the automation I was hoping to get will not "just work" as I'd hoped. We can automate creation of builder.makeNop and several others, in just a single line, but it fails on overloads: builder.makeBlock has several overloads, so we'd need to manually write code to pick one. Maybe that isn't so bad but it does remove some benefit here.

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

Successfully merging this pull request may close these issues.

2 participants