Skip to content

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

Description

@molysgaard

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.

Activity

  1. m-dahl commented on Dec 21, 2024

    @m-dahl
    Collaborator

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions