1 unstable release
new 0.0.0-2025-01-19 | Jan 19, 2025 |
---|
#78 in FFI
30KB
280 lines
abienum - underlying types for C enums
Attempts to define the implicit underlying types of C and C++ enums, when compiled via the cc
crate using default settings.
That is, enums that follow any of these styles:
enum Test { Hello = 1 };
typedef enum { Hello = 1 } Test;
typedef enum Test { Hello = 1 } Test;
typedef enum _Test { Hello = 1 } Test;
Would presumably be FFI-compatible with the following Rust type:
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)] pub struct Test(abienum::c_enum_u7);
impl Test { pub const Hello : Test = Test(1 as _); }
You are expected to use the c_enum_*
with the smallest compatible range — e.g. c_enum_u7
over c_enum_u8
or c_enum_i8
.
Caveats
The underlying type of enums is implementation specific, to the point of being potentially finicky in practice.
-
Compilers may disagree on enum size. For example, ARM delegates type selection to the platform ABI, which leads to Clang and GCC disagreeing on enum size for unknown/none, where no platform ABI is specified or agreed upon. If using the
cc
crate, prefer a globalCC=...
over.compiler("...")
. If linking against prebuilt libraries, specify the same compiler as was used for said libraries viaCC=...
-
Compilers provide flags controlling enum size, such as
-f[no-]short-enums
[clang, gcc]. If you need these, prefer a globalCFLAGS=...
over.flag("...")
. -
Compilers may provide extensions controlling enum size, such as
__attribute__((packed))
or#pragma
s. You're completely on your own for those. I recommend a lot ofstatic_assert
s on the C++ side andconst _ : () = assert!(...);
s on the Rust side. Good luck. -
Compilers may or may not actually respect the flags, attributes, and pragmas specified. E.g. LLVM seems to ignore them on Win32 despite parsing them (llvm/llvm-project #70607)
-
Type selection beyond the basics (e.g. involving potentially typed expressions that depend on previous enumerands) is enough of a potential mess that I haven't tackled it. See also these C23 proposals:
Alternatives
C++11 (and perhaps C23?) provides syntax to explicitly control the underlying type of enums, which — assuming you can sanely modify the C/C++ — I strongly recommend over resorting to this crate's nonsense:
enum Test : int { Hello = 1 };
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)] pub struct Test(core::ffi::c_int);
impl Test { pub const Hello : Test = Test(1 as _); }
If you're stuck in earlier versions of C or C++, the old "force dword" trick will at least get the size right (signedness may still vary):
typedef enum _D3DLIGHTTYPE {
D3DLIGHT_POINT = 1,
D3DLIGHT_SPOT = 2,
D3DLIGHT_DIRECTIONAL = 3,
D3DLIGHT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */
} D3DLIGHTTYPE;
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)] pub struct D3DLIGHTTYPE(i32);
pub const D3DLIGHT_POINT : D3DLIGHTTYPE = D3DLIGHTTYPE(1);
pub const D3DLIGHT_SPOT : D3DLIGHTTYPE = D3DLIGHTTYPE(2);
pub const D3DLIGHT_DIRECTIONAL : D3DLIGHTTYPE = D3DLIGHTTYPE(3);
// const D3DLIGHT_FORCE_DWORD : D3DLIGHTTYPE = D3DLIGHTTYPE(0x7fffffff); // impl detail
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
No runtime deps
~0–305KB