mystor / rust-cpp

Embed C++ directly inside your rust code!
Apache License 2.0
798 stars 44 forks source link

Introduce a rust! pseudo-macro that can be used within the cpp! macro #31

Closed ogoffart closed 6 years ago

ogoffart commented 6 years ago

Allows to call rust from the embedded C++ code. Usefull for callback or re-implementing virtual functions.

See the docs change and tests on how it is used.

This commit just adds it to the "top level" macro. Support wihin the closure macro is not yet done

ogoffart commented 6 years ago

@mystor ping

ogoffart commented 6 years ago

now the arguments are passed by pointers

ogoffart commented 6 years ago

please use ::std::ptr::write (with the preceding ::)

Done. Although I don't understand why this is needed. I thought that with the hygiene of macro, there could not be variable named std.

We probably want to panic::catch_unwind here, so that we can avoid unwinding across language boundaries.

I don't really know how to use that. What do I do with the resulting error? If i unwrap it, it will panic! which will make the whole catch pointless. I could try to forward it to the caller and throw a C++ exception. That'd work if we supported exception thrown from the C++, but currently we don't.

In my tests, panic'ing from the rust! macro seems to work as expected, unwinding the C++ stack (calling C++ destructors and catch(...) handler). (This might be because rust stack unwinding use the same ABI as gcc for stack unwinding. I guess this might not work with MSVC maybe)

However, throwing an exception from the C++ closure will cause an "unrelated" panic in std::panicking::default_hook, or will cause the test driver thread to dead-lock.

I think this is a tad unintuitive. Can we just dereference these pointers into references & ask code inside the lambda to work with &T rather than T?

What do you find unintuitive? The internal code within the macro, or the code the user has to write.

I think this is nicer

    int cpp_add(int x, int y)  {
        return rust!(rust_add [x : i32 as "int", y : i32 as "int"] -> i32 as "int" { 
                x + y
        });
    }

than

    int cpp_ass(int x, int y)  {
        return rust!(rust_add [x : i32 as "int", y : i32 as "int"] -> i32 as "int" { 
                *x  +  *y
        });
    }

Because you specify the types as i32 in the capture, not as &i32.

And I rather have some hidden internal "a tad unintuitive" rather than making user-code less intuitive.

But I admit that the way the arguments are passed can be improved. I was trying to do the copy on the C++ side rather than in the rust side, but it actually does not matter we need a copy anyway. Maybe we can try to support move-only type later.

The only use case I see this really helping with is reference types, which I think we can ask people to use pointers for.

Then we need to find a syntax to simplify the code because otherwise we'd have to explicitly declare pointers on the stack