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

Empty Opaque structs generated due to forward declaration. #397

Closed
mannprerak2 opened this issue Mar 5, 2021 · 3 comments · Fixed by dart-archive/ffigen#180 or dart-archive/ffigen#474
Assignees

Comments

@mannprerak2
Copy link
Contributor

In C, you can make forward declarations like this -

struct A;

struct A
{
    int a;
};

ffigen currently generates struct A as Opaque (without any fields). (This probably also affects enum declarations).

Note: This will only happen when parsing this declaration sequentially (i.e if the declaration was included).

@dcharkes
Copy link
Collaborator

We have another occurrence of this.

@dcharkes, @mannprerak2, I have a scenario here where base.h forward declares CBS as:

// from base.h
typedef struct cbs_st CBS;

But in bytestring.h we have the full cbs_st declaration:

// from bytestring.h
struct cbs_st {
  const uint8_t *data;
  size_t len;
};

If base.h is read first by ffigen, then I get:

class cbs_st extends ffi.Opaque {}

But if bytestring.h is read first, then I get:

class cbs_st extends ffi.Struct {
  external ffi.Pointer<ffi.Uint8> data;

  @ffi.Size()
  external int len;
}

(which is what I need in order to be able to allocate and instance of the struct)

All the other headers include base.h (or at-least many of them do). So my options here are:

  • Put bytestring.h at the top of the list.
  • Create my own amalgamation.h which includes all the headers I need.

I'm wondering if this is working as intended, or if it's a bug? I suspect this pattern is common in C-libraries.

google/webcrypto.dart#41 (review)

@mannprerak2
Copy link
Contributor Author

mannprerak2 commented Sep 27, 2022

I'm not sure if we can solve this without user input about the correct entry-points.

Since each entry-point is parsed separately. If an entry-point contains only the forward declaration and is parsed first, ffigen will only generate an empty definition of that struct.

However, this works -

struct A; // declaration
int sum(struct A a, int b);
struct A{ // definition
    int a;
};

since libclang resolves the definition and declaration for us.

A possible solution would be to create a header file containing all other header files but if they don't have include-guards the parsing may fail unexpectedly.

@dcharkes
Copy link
Collaborator

Since each entry-point is parsed separately. If an entry-point contains only the forward declaration and is parsed first, ffigen will only generate an empty definition of that struct.
[...]
since libclang resolves the definition and declaration for us.

Could we retroactively resolve it ourselves instead of relying on libclang? Or do we have too little information? In other words could we know if its the same struct or don't we know anything and do we risk making 2 different structs one and the same? (That's why we have the automatic renaming in the first place.)

For example, if something is opaque, we can always upgrade it to have a definition. However, do we know for sure that that earlier occurrence is not opaque by design but an actual forward declaration?

A possible solution would be to create a header file containing all other header files but if they don't have include-guards the parsing may fail unexpectedly.

It probably depends on the code base if there are include guards. (The Dart codebase has them for example.) So, if we add it, it should be a config option.

I think if we can't solve it within our code base, this might be a nice solution. Just a one config line fix for projects that have include guards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants