Defining structs / enums based on .h file input

Hello All,

To preface... I've heard about the bindgen crate.
Just wondering if there's a way to auto-generate an .rs file by processing a C header file, to auto-define structs with the appropriate type definitions etc.
I understand that this itself will be a program's worth of code... is it worth it?
Or is there an easy way to create these definitions?

I'm not looking to do any calls to C from Rust.
Rather, it might be required that I create a very basic C program (with easy access to the header file) that works like a server task, listens on some interface, gets whatever args, and then calls Rust with that arguments to offload processing. Rust, in turn, offloads that to some child thread so the main task remains free to collect incoming work from C.

3 Likes

I've already mentioned that I know about this.
Can I use it to convert C headers into native Rust structs?

Yes, that's what bindgen is meant to do. If I remember correctly, it should work for any system where it has access to clang. You should also be able to generate once and use from then on, barring platform-specific codegen.

I don't want to call a C program, I just want to reduce the effort of having to build a Rust struct manually from C structs.
From what I see in the documentation & examples, this crate is to call into C programs...

It will export all definitions in the header, which includes enums, structs, and functions.

This crate is to generate Rust code which will be able to call into C programs, but you're not forced to use it this way. The only thing which is possibly unnecessary in this case is that every struct will be repr[C].

That's the primary use case, but it can be used for the other way around.

If the majority of your header files are functions that need to be implemented by Rust code, though, you're right that bindgen isn't exactly right. It can be made right, but you'd need to do more work.

Specifically, bindgen produces functions like:

extern "C" {
    pub fn func_name(pupper: *mut u32);
}

You would need to manually remove the extern block, and add function implementations:

pub fn func_name(pupper: *mut u32) {
    // (whatever actually implements this)
}

It's not hard work, but it is manual.


The other tool, cbindgen, is the current state-of-the-art package from writing Rust libraries with C interfaces.

It doesn't read .h files, but it will produce one from your Rust code. I think the assumption is that if you're primarily implementing Rust functions, then having a tool which goes from a .h header into a bunch of empty function declarations would be much less useful than a tool which reads .rs files and generates the corresponding .h.


That actually brings up a question. If you have a .h file, what exact rust code would you want auto-generated, and how should it output functions?

Most code generation tools are rerun periodically, every time the source is updated. The first run could just generate a .rs file with empty function implementations. But then I assume you'd want to implement those functions - to write code inside them.

I think dealing with functions on the second run would be a hard problem, and that might be why this tool doesn't exist. Ideally, you'd want it to be automatic - so it could "just work" on an existing .rs file. But then it'd have to A) read the rust file, B) identify all the functions whose declarations, but not bodies, need to be replaced with those from the .h file, and C) make those replacements, while still keeping variable names and bodies from the old code.

The fact that we need to edit the generated .rs file after it's generated makes this job a lot harder to get right, and I think that's why people choose to use the other option - generating a .h from the .rs.

In short, I want to parse data, whose structure is documented in a .h file, in structs, enums, etc.
Let's say

#include<whatever.h>
typedef struct {
  uint16_t LEN;
  uint16_t SEG;
  uint8_t  FLG;
  uint8_t  RTY;
  uint32_t TME;
  uint32_t DTE;
  char SID[4];
  uint16_t SBS;
  uint16_t SGN;
  uint8_t FL1;
  uint8_t VER;
  uint16_t STY;
  uint16_t TRN;
  uint32_t PRS;
  uint16_t PRL;
  uint16_t PRN;
  uint32_t GNS;
  uint16_t GNL;
  uint16_t GNN;
  uint32_t J1O;
  uint16_t J1L;
  uint16_t J1N;
} HDR;

I want to convert this into

struct header {
len: u32,
seg: u16,
}

Not worried about the functions as I'll be implementing them manually in Rust. Will only refer to the equivalent functions in C, if available, for reference/ideas.

So.. just wondering if there's a way to generate relatively close boilerplate structs from C header structs.
Rather than having to type it all out by hand.

:open_mouth: that's great! This is exactly what I was hoping I could do!
Any way I can turn off the pub annotation in the individual fields?

Maybe, but if you're just doing it once, it's probably easier to just search and replace them away.

We're talking about hundreds of auto-generated structs.
Even if it's going to be a once-in-a-few-months run of bindgen, that's a lot.

I couldn't find any options in bindgen for controlling visibility of the fields. They might add it though, if one of us makes a bug report?

Besides that, this isn't a good solution, but you could always do a search-and-replace on the output, and put it in a script for automation - something like:

#!/bin/sh
bindgen foo.h > src/gen/foo.rs
# only replace pub with indentation, so as only to replace field publicity
sed -i 's/^    pub /    pub(crate) /' src/gen/foo.rs

It's hacky, but it should work, and the only manual work it needs is checking the git diff after each run to ensure that the replacement hasn't messed up.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.