rust_box2d icon indicating copy to clipboard operation
rust_box2d copied to clipboard

Functions returning structs do not work

Open meerik opened this issue 8 years ago • 12 comments

Structs defined in c++, such as b2Vec2 seem to cause many problems when they are passed over the FFI by-value. I think the reason is different calling conventions for C vs C++. It seems like functions returning structs as-value or taking structs as input argument as-value cause problems. Take Body::world_point() as example, it does not work and return undefined data or some times crash. (All other demos from the testbed work because they never use any such function).

It is implemented as follows:

pub fn world_point(&self, local: &Vec2) -> Vec2 {
        unsafe { ffi::Body_get_world_point(self.ptr(), local) }
    }
b2Vec2 Body_get_world_point(const b2Body* self, const b2Vec2* local) {
    return self->GetWorldPoint(*local);
}

Here the argument "local" will point to garbage no matter how you call the funcition and the program crashes. This is because the return value is a b2Vec2 as-value and b2Vec2 is compiled as struct/class by c++ code while the function Body_get_world_point is treated as C-code.

meerik avatar Jan 16 '17 16:01 meerik

@meerik Thanks for the detailed issue, convention issues between C and C++ would surprise me since I'm using extern "C" so I would at least expect a warning if the result is not C compatible. Although I don't know the details and there may be no checks at all, so it is very possible, are you confident in your explanation ?

This code works fine for me, I guess it doesn't work for you ?

use wrapped2d::b2;
use wrapped2d::user_data::NoUserData;

#[test]
fn get_world_point() {
    let gravity = b2::Vec2 { x: 0., y: -10. };
    let mut world = b2::World::<NoUserData>::new(&gravity);

    let mut ground_body_def = b2::BodyDef::new();
    ground_body_def.position = b2::Vec2 { x: 0., y: -10. };
    let ground_handle = world.create_body(&ground_body_def);
    {
        let mut ground = world.body_mut(ground_handle);

        let ground_box = b2::PolygonShape::new_box(50., 10.);
        ground.create_fast_fixture(&ground_box, 0.);
        assert_eq!(ground.world_point(&b2::Vec2 { x: 5., y: 0. }),
                   b2::Vec2 { x: 5., y: -10. });
    }
}
> rustc --print cfg
debug_assertions
target_arch="x86_64"
target_endian="little"
target_env="gnu"
target_family="unix"
target_feature="sse"
target_feature="sse2"
target_has_atomic="16"
target_has_atomic="32"
target_has_atomic="64"
target_has_atomic="8"
target_has_atomic="ptr"
target_os="linux"
target_pointer_width="64"
target_thread_local
target_vendor="unknown"
unix

Bastacyclop avatar Jan 16 '17 18:01 Bastacyclop

Thank you for the quick reply. Your test works on Ubuntu with GCC but not on Windows with MSVC.


---- tests::get_world_point stdout ----
	thread 'tests::get_world_point' panicked at 'assertion failed: `(left == right)` (left: `Vec2 { x: -0.00000000000000029138696, y: 0.000000000000000000000000000000000000000000912 }`, right: `Vec2 { x: 5, y: -10 }`)', src\lib.rs:22
rustc --print cfg
target_os="windows"
target_family="windows"
target_arch="x86_64"
target_endian="little"
target_pointer_width="64"
target_env="msvc"
windows
debug_assertions

meerik avatar Jan 16 '17 19:01 meerik

Maybe the problem comes from the use of gcc in build_frontend ?

Bastacyclop avatar Jan 16 '17 19:01 Bastacyclop

This may be a case of https://github.com/rust-lang/rfcs/issues/1342, especially since it specifically does not work on Windows.

retep998 avatar Jan 17 '17 00:01 retep998

@retep998 Thanks for the info!

Bastacyclop avatar Jan 17 '17 09:01 Bastacyclop

I've seen this too in VS 2015, msvc toolchain.

Quite a few other related issues too. 1) stdc++ cannot be linked in MSVC 2) no luck with 64 bit build, link.exe laments a 32/64 bit mismatch with one of the deps. I'll investigate and post separate issues.

norru avatar Jan 29 '17 22:01 norru

stdc++ cannot be linked in MSVC

Statically linking together MSVC and MinGW code is not supported at all whatsoever and will almost always break.

retep998 avatar Jan 29 '17 22:01 retep998

Yep, I am building with the MSVC toolchain, but rust_box2d causes the linker to attempt to include stdc++ anyway.

norru avatar Jan 29 '17 22:01 norru

Hmmm, it uses the gcc crate to build things, and that should know how to handle MSVC just fine. I'll investigate more later perhaps.

retep998 avatar Jan 29 '17 22:01 retep998

This may have something to do with it (in lib.rs)

#[link(name = "stdc++")] extern "C" {}

EDIT: removing the line above from lib.rs actually fixes the problem in MSVC - I haven't tried other toolchains/platforms though. That may require a #[cfg(...)] annotation for other platforms.

I'll move to another thread though, I've hijacked this enough :)

norru avatar Jan 29 '17 23:01 norru

@norru Since this uses the gcc crate to handle building and linking the C++, that automatically handles linking the C++ standard library. https://github.com/alexcrichton/gcc-rs/blob/master/src/lib.rs#L950-L963 So definitely removing that #[link(name = "stdc++")] is the right thing to do.

retep998 avatar Jan 30 '17 03:01 retep998

For information, the latter has been done.

Bastacyclop avatar Feb 12 '17 12:02 Bastacyclop