Support shared struct with C++ source of truth
I want to be able to write the following or equivalent:
#[cxx::bridge]
fn ffi {
struct FVector {
pub X: f32,
pub Y: f32,
pub Z: f32,
}
extern "C++" {
include!("Vector.h");
type FVector;
}
}
and it have it expand to the the following or equivalent:
// Rust
#[repr(C)]
struct FVector {
pub X: f32,
pub Y: f32,
pub Z: f32,
}
// Cxx
#include "Vector.h"
#include <cstdint>
#include <cstddef>
#include <type_traits>
#include <utility>
static_assert(::std::is_class<FVector>::value, "expected class");
static_assert(sizeof(FVector) == 12, "incorrect size");
static_assert(offsetof(FVector, X) == 0, "disagrees with the value in #[cxx::bridge]");
static_assert(::std::is_same<decltype(::std::declval<FVector>().X), float>::value, "disagrees with the value in #[cxx::bridge]");
static_assert(offsetof(FVector, Y) == 4, "disagrees with the value in #[cxx::bridge]");
static_assert(::std::is_same<decltype(::std::declval<FVector>().Y), float>::value, "disagrees with the value in #[cxx::bridge]");
static_assert(offsetof(FVector, Z) == 8, "disagrees with the value in #[cxx::bridge]");
static_assert(::std::is_same<decltype(::std::declval<FVector>().Z), float>::value, "disagrees with the value in #[cxx::bridge]");
This uses only C++11 functionality as written. The only member types supported would be primitive types or (potentially) shared types defined in the same bridge module. This requires knowledge of the repr(C) layout algorithm to assert that it is correct; for this use case we can use the simple definition:
fn repr_c(field_layouts: Vec<Layout>) -> Result<StructLayout> {
let mut layout = Layout::new::<()>();
let mut fields = vec![];
for field in field_layouts {
let (extended, offset) = layout.extend(field_layout)?;
layout = extended;
offsets.push(FieldLayout { layout: field_layout, offset });
}
layout.pad_to_align();
StructLayout { layout, fields }
}
For extra paranoia we could also emit assertions that our understanding is correct on the Rust side as well.
Preprocessor macros to make doing so manually easier
#define PREPROCESSOR_TO_STRING(x) PREPROCESSOR_TO_STRING_INNER(x)
#define PREPROCESSOR_TO_STRING_INNER(x) #x
#define ASSERT_TYPE_SIZE(Type, Size) \
static_assert( \
sizeof(Type) == Size, \
PREPROCESSOR_TO_STRING(Type) " does not have expected size " PREPROCESSOR_TO_STRING(Size));
#define ASSERT_TYPE_MEMBER(Type, Offset, MemberType, Member, ...) \
static_assert( \
offsetof(Type, Member) == Offset, \
PREPROCESSOR_TO_STRING(Type) "::" PREPROCESSOR_TO_STRING(Member) " is not at expected offset " PREPROCESSOR_TO_STRING(Offset)); \
static_assert( \
std::is_same<decltype(std::declval<Type>().Member), MemberType>::value, \
PREPROCESSOR_TO_STRING(Type) "::" PREPROCESSOR_TO_STRING(Member) " is not expected type " PREPROCESSOR_TO_STRING(MemberType));
ASSERT_TYPE_SIZE(FVector, 12);
ASSERT_TYPE_MEMBER(FVector, 0, float, X);
ASSERT_TYPE_MEMBER(FVector, 4, float, Y);
ASSERT_TYPE_MEMBER(FVector, 8, float, Z);
If you're feeling bold, you can try autocxx and specifically generate_pod!, which should do what you want.
Unfortunately, the C++ code I'm trying to interface with is too complicated for autocxx/bindgen to process cleanly at the moment, so while I'd love to use autocxx, it's not possible quite yet.
It's worth noting that you can already do exactly this, just without the checks that your mapping is correct. It would be nice to extend the checks for extern shared enums to extern shared structs.
Unfortunately, the C++ code I'm trying to interface with is too complicated for autocxx/bindgen to process cleanly at the moment
If you get a chance, it'd be great if you could file an autocxx bug per these instructions. In particular if you can run the codegen with AUTOCXX_PREPROCESS=out.h then sent me out.h I should be able to minimize to get a small test case. Obviously you can only do that if your code is open source.
The general state of play is that it can cope with any codebase right now, so long as you are specific about bindings what you want to generate using generate! directives (as opposed to asking it to generate bindings for every C++ API it encounters). If there's a C++ codebase which is just plain dies on, it'd be great to get a bug report.
Reported https://github.com/google/autocxx/issues/479. Unfortunately the code isn't reproducible but it is freely obtainable, so perhaps someone able to track down where autocxx is panicking can reproduce the panic and minimize the issue.