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

Potential issue with message definitions with fields with type char and signedness across platforms #115

Open
molysgaard opened this issue Dec 16, 2024 · 1 comment

Comments

@molysgaard
Copy link

molysgaard commented Dec 16, 2024

char is a very strange C-type. In the C-spec (and C++), it does not have a defined signedness.

This leads to some challenges when one is working with code on different platforms.
As an example, the char type has different signedness on x86 and aarch64:

  • On x86, a char is a signed 8-bit integer [-128..127]
  • On aarch64, a char is an unsigned 8-bit integer [0..255]

I have some code that runs on both x86 and aarch64, and a Yocto cross-compilation system to cross-compile from our x86 build machines onto our aarch64 robot machines.

We specify our messages using OMG IDL instead of the normal ROS2 .msg or .srv files.
R2R does a great job of parsing the generated C-headers and libraries and generating correct Rust code to interface with this, but I think I might have found an inconsistency in how r2r handles the OMG IDL char type.

On my x86 machine, an OMG IDL message with a char type gets type std::ffi::c_char in the generated Rust.
From the OMG IDL spec, I understand it so that char always is an unsigned 8-bit integer in the range [0..255]. Ref: https://www.omg.org/spec/IDL/4.2/PDF Section 7.2.6.2.1

I have also checked the C-headers generated by colcon and it generates unsigned char for my OMG IDL char field.

I tried to look through the r2r_msg_gen crate and found:

    pub fn to_rust_type(&self) -> proc_macro2::TokenStream {
        match self {
            MemberType::Bool => quote! { bool },
            MemberType::I8 => quote! { i8 },
            MemberType::I16 => quote! { i16 },
            MemberType::I32 => quote! { i32 },
            MemberType::I64 => quote! { i64 },
            MemberType::U8 => quote! { u8 },
            MemberType::U16 => quote! { u16 },
            MemberType::U32 => quote! { u32 },
            MemberType::U64 => quote! { u64 },
            MemberType::U128 => quote! { u128 },
            MemberType::F32 => quote! { f32 },
            MemberType::F64 => quote! { f64 },
            MemberType::Char => quote! { std::ffi::c_char },
            MemberType::WChar => quote! { u16 },
            MemberType::String => quote! { std::string::String },
            MemberType::WString => quote! { std::string::String },
            MemberType::Message => quote! { message },
        }
    }

I realized that I do not know enough about how r2r is implemented to know if this is the only place that needs to be changed to fix this. I am not even 100% sure that r2r does something wrong. I would love it if someone with a deeper understanding of r2r could have a look at this.

@m-dahl
Copy link
Collaborator

m-dahl commented Dec 21, 2024

Hi, thanks for reaching out!

I just tried this on two machines, one being arm64 osx + jazzy and the other x86_64 + iron, and in both cases this IDL section

    struct MyMessage {
      char char_value;
    }

generates this ros header

typedef struct test_package_msgs__msg__MyMessage
{
  signed char char_value;
}

Since you get unsigned and I get signed, this leads me to believe that ros actually uses the char signedness of the host system rather than following the IDL spec. In this case I think r2r does the right thing.

If you want to experiment with this it should be enough to change that one line. If I change to MemberType::Char => quote! { u8 }, on my system, I get, as one can expect:

674 |             msg.char_value = self.char_value;
    |             --------------   ^^^^^^^^^^^^^^^ expected `i8`, found `u8`
    |             |
    |             expected due to the type of this binding

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

No branches or pull requests

2 participants