native icon indicating copy to clipboard operation
native copied to clipboard

ffigen still uses ints for struct members.

Open Levi-Lesches opened this issue 1 year ago • 3 comments
trafficstars

Consider the header below:

typedef enum A {
  enum1,
  enum2
} A;

typedef struct B {
  A a;
} B;

ffigen generates the following Dart code:

enum A {
  enum1(0),
  enum2(1);

  final int value;
  const A(this.value);

  static A fromValue(int value) => switch (value) {
        0 => enum1,
        1 => enum2,
        _ => throw ArgumentError("Unknown value for A: $value"),
      };
}

final class B extends ffi.Struct {
  @ffi.UnsignedInt()
  external int a;
}

I found that structs are generated here:

https://github.com/dart-lang/native/blob/2464aaa562f72eae9765a90573417096eccac9ee/pkgs/ffigen/lib/src/code_generator/compound.dart#L154-L159

I'm not sure exactly how the external part works here, so I'm not sure if this can be rewritten to use an enum directly or it has to stay an integer. If it's the latter, this should be sufficient:

final class B extends ffi.Struct {
  @ffi.UnsignedInt()
  external int _a;

  A get a => A.fromValue(_a);
}

That could be implemented by doing:

- s.write('${depth}external ${m.type.getFfiDartType(w)} ${m.name};\n\n');
+ final memberName = m is EnumClass ? '_${m.name}' : m.name;
+ s.write('${depth}external ${m.type.getFfiDartType(w)} $memberName;\n\n');

and then adding:

if (m is EnumClass) {
  final enumName = m.getDartType(w);
  final memberName = m.name;
  s.write('$enumName get $memberName => $enumName.fromValue(_$memberName);\n\n');
}

/cc @dcharkes Should I open a PR with the above or should we do something else? Also, this is technically a breaking change again...

Levi-Lesches avatar Aug 08 '24 18:08 Levi-Lesches

Thankfully, a good workaround in the meantime is using A.fromValue in code that uses B, like:

void main() {
  final b = getB();
  final result = switch (A.fromValue(b.a) { /* ... */ };
}

Levi-Lesches avatar Aug 08 '24 18:08 Levi-Lesches

final class B extends ffi.Struct {
  @ffi.UnsignedInt()
  external int _a;

  A get a => A.fromValue(_a);
}

That would be my first thought indeed. 👍

Also, this is technically a breaking change again...

We're already at 14.0.0-wip due to other breaking changes. So just add another entry to the changelog.

/cc @dcharkes Should I open a PR with the above or should we do something else?

Yes please. 😄 🙏

dcharkes avatar Aug 09 '24 08:08 dcharkes

@dcharkes PR at #1432. Workflows need approval

Levi-Lesches avatar Aug 15 '24 22:08 Levi-Lesches