What should the API be to get from a Struct to a Pointer and vice versa?
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:
. notationCon:
StructPointer<S> addressOf<S extends Struct>(S s) {
// ...
}
Pro:
StructCon:
. notationabstract 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:
Struct. notationCon:
extension MyExtension on S extends Struct {
Pointer<S> addressOf() {
// ...
}
}
@lrhn @leafpetersen is this supported by our design for extension methods?
Pro:
Struct. notationThis has the least amount of cons, if this would be supported.
Similar argument for getting from a Pointer to a Struct by using load.
extension MyExtension on Pointer<S extends Struct> {
S load();
}
Pro:
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:
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?
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
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.
.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.
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.
Most helpful comment
Yes.