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

Issues with repr(C) trait #21

Closed
clegaard opened this issue Jun 6, 2020 · 4 comments
Closed

Issues with repr(C) trait #21

clegaard opened this issue Jun 6, 2020 · 4 comments

Comments

@clegaard
Copy link

clegaard commented Jun 6, 2020

I am writing some code that needs to be callable from C. To my understanding enums exposed to the c code should have the trait #[repr(C)], but this seems to cause an issue with the module:

#[repr(C)]
pub enum Okay {
    a,

#[derive(TryFromPrimitive)]
#[repr(C)]
pub enum NotOkay {
    a,
}

Produces the error:

error: repr(C) doesn't have a well defined size
   --> src\lib.rs:124:8
    |
124 | #[repr(C)]
    |        ^

Is there a workaround for this? I tried defining multiple types: #[repr(C,i32)] which appears to have been possible previously: rust-lang/rust#68585

Any help is much appreciated :)

@danielhenrymantilla
Copy link
Collaborator

danielhenrymantilla commented Jun 6, 2020

The issue is that a #[repr(C)] is very badly specified, so you could get into issues when calling it from C, if, for instance, Rust gives it the size of a c_int but C, on the other hand, decides that the enum could fit in a uint8_t and thus uses that instead.

The most sensible thing to do then is to give it instead a fixed size layout, such as #[repr(i32)], #[repr(u8)] or #[repr(i8)] (this gives the enum the layout of an integer, thus making it C compatible), and then use something like:

typedef int32_t MyEnum; enum {
    MY_ENUM_FOO,
    // etc.
};

(Or use what cbindgen generates)

@clegaard
Copy link
Author

clegaard commented Jun 6, 2020

Ah, I guess using enums for return values across a C API is somewhat problematic if the compiler is free to choose the datatype?

In my particular case, the enum is being used to communicate the status between an application, A that dynamically loads the library B (the part I am writing). So i guess that both A and B should use the same size for the enum?

@danielhenrymantilla
Copy link
Collaborator

Yes.

  • If, for instance, A is a Rust binary too, then indeed, as long as you use the same integer type for the #[repr()] anotation on the enum for both Rust crates (e.g., same common rlib dependency or same textual definition), then all will work fine.

B

#[derive(...)]
#[repr(i32)]
pub
enum Status {
    Ok = 0,
    OhNo,
}

#[no_mangle] pub extern "C"
fn status () -> Status
{
    // ...
}
  • crate-type = ["cdylib"]

A

If C binary

#include <stdint.h>

typedef int32_t Status; enum {
    STATUS_OK = 0,
    STATUS_OH_NO,
};

// If at link / load time:
Status get_status (void);

// If manually loaded at runtime
// (_e.g._, hot reloading)
#include <dl.h>
void some_func (...)
{
    ...
    Status (*get_status)(void) = dlsym(... "get_status" ...);
    ...
}

If Rust binary

#[derive(...)]
#[repr(i32)]
pub
enum Status {
    Ok = 0,
    OhNo,
}

fn some_func (...)
{
    ...
    let get_status: extern "C" fn() -> Status = unsafe { ... };
    ...
}

@clegaard
Copy link
Author

clegaard commented Jun 6, 2020

Thank you for taking the time to create an example, it really helped with the understanding.
It seems that the only robust solution would be to formalize the type of status code. Multiple "A" applications exist, written in different languages. I have looked into one of these (written in python) and it seems to be using something similar to Rust's std::os::raw to "guess" the correct types.

Fortunately, a new standard is being drafted defining the communication interface between A and B, so your feedback may prove useful in creating a more robust/portable interface.

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