autocxx icon indicating copy to clipboard operation
autocxx copied to clipboard

Support custom smart pointer types

Open kcking opened this issue 2 years ago • 3 comments

Hi there!

I was wondering if it's currently possible to generate bindings to a scoped_refptr reference-counted type, for example a webrtc MediaStream. If this isn't currently possible, is there a recommended way to implement this such as mimicing cxx::SharedPtr's impl?

Thank you for the great tool!

kcking avatar Nov 08 '21 05:11 kcking

Hi, good question. The short answer is no - this is some way off. Here's how the plan might look. First off, we will need https://github.com/dtolnay/cxx/issues/683 - that's the heavy lift. We then need to make that work nicely in autocxx - that's #349. So far this assumes that the contained T is owned by the outer structure. That wouldn't quite be the case here. We would want to give the opportunity for autocxx clients to craft idiomatic safe Rust representations of the ownership situation, much like this.

So. Yes. There are many steps down the road before we can support arbitrary smart pointer types, sorry. This won't be soon.

Meanwhile: cxx::SharedPtr is itself more limited than you might realize. As far as I know, there's no way to get a mutable reference to the owned object on the Rust side, and this is intentional due to Rust's aliasing requirements. I think the solution there is https://github.com/dtolnay/cxx/issues/537.

So: to cut a long story short, your only option right now is to create C++ code which manages the ownership of the object and provides references (both const and mutable) to Rust when necessary.

Totally off the top of my head, something like:

class MediaStreamHandle {
public:
  MediaStream& get_mut() { /* do whatever it takes to return a reference to 'stream' */ }
  const MediaStream& get() const  { /* do whatever it takes to return a reference to 'stream' */ };
  scoped_refptr<MediaStream> stream;
}

then ask autocxx to generate bindings for MediaStreamHandle, and use the get and get_mut methods from Rust each time you want to access the MediaStream.

Be very careful that you don't ever keep the resulting mutable reference on the Rust side, or you'll violate Rust's aliasing rules.

Could you let me know whether this approach works? I would love to add an example demonstrating this idea. A pull request adding an example would be even better.

adetaylor avatar Nov 08 '21 15:11 adetaylor

Thank you for such a thorough write-up!

It turns out the memory layout of the scoped_refptr impl puts the T* first, I tried directly transmuting it to a *const T and that "just worked."

Noted on your point about rust aliasing rules. It sounds like if there is C++ code storing any reference to T then it's not safe for rust to have a *mut T, so mutability from within rust has to be done extremely carefully.

I am going to experiment more and see if I come up with anything useful. Thanks again!

kcking avatar Nov 10 '21 05:11 kcking

if there is C++ code storing any reference to T then it's not safe for rust to have a *mut T, so mutability from within rust has to be done extremely carefully.

I don't think it's quite that bad. Assuming the single-threaded case, my belief is that it's OK for C++ to have references to T even if Rust has a Pin<&mut T>... so long as Rust only has one such reference and it remains !Unpin (so that Rust code can't try to move it around in memory). Also, *mut T is OK - there are no aliasing requirements there, it just becomes hard once you convert it to a &mut T and actually try to use the T.

But yeah, aliasing requirements are hard. Good luck!

adetaylor avatar Nov 10 '21 06:11 adetaylor