Dushistov / flapigen-rs

Tool for connecting programs or libraries written in Rust with other languages
BSD 3-Clause "New" or "Revised" License
775 stars 59 forks source link

Lack of support of ENUM for C #419

Open stephan57160 opened 2 years ago

stephan57160 commented 2 years ago

I figured out that .hpp is generated to expose constants for enums, but this file is not usable for pure C code.

I worked a bit on that, this afternoon. I come to a c_<enum_name>.h that I'm able to include in a simple C code. But, its also a bit redundant with the already generated <enum_name>.hpp.

I'd like to discuss this, before raising a PR.

Dushistov commented 2 years ago

This issue is mainly due to the fact that size of "C enum" is an implementation defined according to the C standard. So generated "C API" get and return integers instead of enum, and C++ convert it from/into enum. So while flapigen can generate "C enum" it is useless because of C functions should get/return fixed size integers anyway.

Dushistov commented 2 years ago

It is obviously possible to generate C inline function that wrap C functions and take care of conversions enum<->integer, but this looks like overkill.

stephan57160 commented 2 years ago

Agreed for the enum returned by C. The current C++ implemantation is rather similar to what could be done for C :

enum FOO {
   FOO_xxx = 0,
   FOO_yyy = 1,
   ...
};

It's also convenient, as RUST can convert ENUM to any INT. It's not true from INT to ENUM, though.

stephan57160 commented 2 years ago

At least, this solves the situation where a RUST function returns an Error :

fn MyRustFunction() -> Error {...}

and the C code performs a switch ... case on returned value :

ret = MyRustFunction();
switch (ret) {
  case xxx : 
  ...
}
stephan57160 commented 2 years ago

Hmm ... I may need some guidance, to write some test case(s), if you don't mind, for a PR.

Dushistov commented 2 years ago

The creation of expectation test would be rather simple.

  1. Create file in "macroslib/tests/expectations", for example "macroslib/tests/expectations/cenum.rs".
  2. Add this file into "macroslib/tests/expectations/tests.list"
  3. Add code that would process flapigen into cenum.rs, for example foreign_enum!(enum Boo { A = Boo::A, B = Boo::B });
  4. Add cenum.cpp file near cenum.rs, with C code that you expect to flapigen generate, in form of string literals separated by ";"
  5. Run cargo test --release, "release" because of debug build to slow.

In spite of name fenum.cpp may contains C and C++

stephan57160 commented 2 years ago

OK. Thx. I'll have a look this afternoon.

stephan57160 commented 2 years ago

From glue.rs.in code below :

foreign_enum!(
    enum SomeEnum {
        Val1                   = SomeEnum::Val1,
        Val2                   = SomeEnum::Val2,
        Val3                   = SomeEnum::Val3,
    }
);

foreign_class!(class Foo {
    fn return_is_enum() -> SomeEnum;
    fn param_is_enum(e: SomeEnum);
    fn param_and_return_are_enum(e: SomeEnum) -> SomeEnum;
});

I'm able to generate something like :

enum SomeEnum {
    SomeEnum_Val1 = 0,
    SomeEnum_Val2 = 1,
    SomeEnum_Val3 = 2,
};

typedef enum SomeEnum SomeEnumOpaque;

This part could be considered as complet :-)

Now, I wanted the C functions to use this new type. So, I wandered in flapigen code and ...

OK. I suppose that the following generated code

    uint32_t Foo_return_is_enum();
    void Foo_param_is_enum(uint32_t e);
    uint32_t Foo_param_and_return_are_enum(uint32_t e);

is generated by RUSTC itself, based on cpp_glue.rs:

#[allow(non_snake_case, unused_variables, unused_mut, unused_unsafe)]
#[no_mangle]
pub extern "C" fn Foo_return_is_enum() -> u32 {
    let mut ret: SomeEnum = return_is_enum();
    let mut ret: u32 = <u32>::swig_from(ret);
    ret
}
...

Now, I understand your statment It is obviously possible to generate C inline function that wrap C functions and take care of conversions enum<->integer, but this looks like overkill....

Then ... I guess that there is no way to have something like

    SomeEnumOpaque Foo_return_is_enum();
    void Foo_param_is_enum(SomeEnumOpaque e);
    SomeEnumOpaque Foo_param_and_return_are_enum(SomeEnumOpaque e);

Moreover, I found that the C code is also used by the C++ one, to complicate a bit :-) OK. I'll leave the function part there.

Then, I also had a look to tests/expectations. It's rather simple to use. I was able to get some test with it.