Sdk: [vm/ffi] Struct.addressOf Pointer<Struct>.load() signatures

Created on 17 Jun 2019  路  9Comments  路  Source: dart-lang/sdk

What should the API be to get from a Struct to a Pointer and vice versa?

Struct to Pointer: Struct.addressOf

Field of struct

abstract class Struct<S extends NativeType> extends NativeType {
  // If the struct is backed by native memory, returns the address of the
  // reference. If the struct is backed by Dart-allocated memory, returns null.
  final Pointer<S> addressOf;
}

class TestStruct4 extends ffi.Struct<TestStruct4> {
  @ffi.Double()
  double z;
}

Pros:

  • concise access through . notation
  • no specification of type argument on invocation

Con:

  • a type argument for Struct

Top level function

Pointer<S> addressOf<S extends Struct>(S s) {
  // ...
}

Pro:

  • no type argument on Struct
  • specify type argument at every invocation

Con:

  • no . notation

Method

abstract class Struct extends NativeType {
  // If the struct is backed by native memory, returns the address of the
  // reference. If the struct is backed by Dart-allocated memory, returns null.
 Pointer<S> addressOf<S>();
}

Pro:

  • no type argument on Struct
  • . notation

Con:

  • specify type argument at every invocation

Extension method

extension MyExtension on S extends Struct {
  Pointer<S> addressOf() {
    // ...
  }
}

@lrhn @leafpetersen is this supported by our design for extension methods?

Pro:

  • no type argument on Struct
  • . notation
  • no type argument on invocation

This has the least amount of cons, if this would be supported.

Pointer to Struct: Pointer.load

Similar argument for getting from a Pointer to a Struct by using load.

Extension method

extension MyExtension on Pointer<S extends Struct> {
  S load();
}

Pro:

  • No type argument on invacation

Method

This is the current API.

Pointer<N extends NativeType> {
  // T = int when N = Int32 etc
  // T = N when N is a subtype of Struct
  T load<T>();
}

Con:

  • specifying type argument on invocation

Pre-extension methods

If the extension methods will be supported in the future, what should we decide for now - with the easiest path of migrating to extension methods later?

area-vm library-ffi type-design

Most helpful comment

@leafpetersen : Will we get extension getters and setters?

Yes.

All 9 comments

Honestly, I feel that extension methods on all pointer types would be the best, e.g.

// Say, NativeInt is a superclass to FFI's Int8, Int16, etc.
extension IntPointerExt on Pointer<T extends NativeInt> {
  int load();
  void store(int value);
}

extension DoublePointerExt on Pointer<T extends NativeDouble> {
  // ...
}

This removes the need for the type parameter on load, which seems redundant right now (I already have a Pointer, why do I need to specify that I'm loading an int?).

The only unfortunate bit is that extension methods are... NYI. But I think it would be a great change to do in the future (although breaking).

Yes, that should work fine with extension methods. It would look like this though:

dart extension MyExtension<S extends Struct> on S { Pointer<S> addressOf() { // ... } }

This would apply to any object with a type which extends Struct.

@ds84182 that is exactly what we are aiming for! Unfortunately we have to wait until extension methods get implemented in Dart. Until then we'll have to use T load<T>() and store(Object value).

Plan is for extension methods to be ready in Q3, so not too far out.

We could use this API today to have a clean API until extension methods, with a non-breaking migration path:

class Pointer<T extends NativeType> {
  // ...
  dynamic get val;  // FE ensure T !extends Struct
  void set val(dynamic val);  // FE ensure T !extends Struct
  T get ref;  // FE only allows when T extends Struct
  // ...
}

@leafpetersen : Will we get extension getters and setters?

We should change Pointer.address/fromAddress to Pointer.toInt/fromInt to avoid confusion with addressOf.

Some documentation for the reasoning behind the above suggestion.

Separation ref and val

.ref and .val are semantically different. Using .val on Pointer<T extends NativeInt> and Pointer<T extends NativeDouble> directly stores and loads data. Using .ref on Pointer<T extends Struct> gives a reference which allows one to load and store fields of the struct.

Use of dynamic

Using dynamic for .val and T for .ref (previously load<T>() and store(Object o)) allows us to omit type arguments on code at the use site now _and_ when we replace them with extension methods.

@leafpetersen : Will we get extension getters and setters?

Yes.

I think we've agreed on the design here -- the implementation remains in #37361.

Was this page helpful?
0 / 5 - 0 ratings