-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[vm/ffi] Allow subclassing Pointer? #35782
Comments
Do you plan to write the This model looks suspicious to me. A The Is there any possible way this could be written as: @ffi.struct
class Coordinate {
@ffi.Double() double x;
@ffi.Double() double y;
@ffi.Pointer() Coordinate next;
extends static in sizeOf(); // why not getter?
external Pointer<Coordinate> allocate({int count});
external factory Coordinate.fromAddress(Pointer<Void>);
/// Write your own factories and custom methods here.
} which is then automatically compiled as @ffi.struct
class Coordinate {
Pointer<Void> get _$address => this as Pointer<Void>; // my own address.
double get x => _$address.offsetBy(0).cast<ffi.Double>.load();
void set x(double value) { _$address.offsetBy(0).cast<ffi.Double>.save(value); }
...
Coordinate get next => _$address.offsetBy(16).cast<ffi.Pointer<Coordinate>>.load();
void set next(Pointer<Coordinate> value) { _$address.offsetBy(16).cast<ffi.Pointer<Coordinate>>.write(value._$address); }
...
static int sizeOf() => 24; // Or whatever size we compute from the fields.
Pointer<Coordinate> allocate({int count}) {
ffi.allocate<ffi.Uint8>(count: sizeOf() * count).cast<Pointer<Coordinate>>;
}
factory Coordinate.fromAddress(Pointer<Void> address) => address as Coordinate;
// user code.
} Then we treat the It's still a little iffy, because Pointer<Coordinate> p = ...;
var p2 = p.offsetBy(2); // p.cast<Void>.offsetBy(2 * Coordinate.sizeOf()).cast<Coordinate>
Coordinate o = p2.load(); // actually = p2.cast<Void>() as Coordinate It should be possible to statically detect void forEach<T>(int count, Pointer<T> o, void action(T value)) {
for (int i = 0; i < count; i++) {
action(o.offsetBy(i).load());
}
} There is no way that will work for things where the type is not known dynamically. The VM might need to be tricky and recognize, at run-time, that a type argument like (I guess this all boils down to how user friendly you want the API to be. If it's a low-level API where you are expected to write all conveniences yourself, then you might not care that the API is idiosyncratic and inconsistent, as long as it allows user code to be simple and efficient. If you want users to use the structs directly in their normal code, they should work as close as possible to a Dart object with the same signature, and should be designed to look like idiomatic classes). |
class Statement extends Pointer<Void> {
// ...
}
Statement prepare(String query); |
Quite often the Dart api does not coincide with Pointer-wrapper-classes class Result extends IterableBase<Row> implements ClosableIterable<Row> {
final StatementPointer _statement;
// ... https://github.com/dart-lang/sdk/blob/master/samples/ffi/sqlite/lib/src/database.dart#L122-L125 This would then have to be: class StatementPointer {
Pointer<Void> pointer;
} |
No we will not allow subtyping, the API will be: class Statement extends NativeType { }
Pointer<Statement> p; |
Will |
I think we're planning to only allow extending |
When this is implemented, we should ensure that the following code doesn't crash: import 'dart:ffi';
import 'dart:async';
class DbOptionsPtr extends Pointer<Void> {}
main(args) async {
DbOptionsPtr opts = fromAddress(0);
await Isolate.spawn(print, opts);
} |
@sjindel-google Any thoughts on what we do with See our use of CString in the SQLite sample: samples/ffi/sqlite/lib/src/bindings/signatures.dart and samples/ffi/sqlite/lib/src/bindings/bindings.dart. Conceptually I would say: CString = char* = Pointer<Uint8>. With generalized typedefs and extension methods we could write these things as extension methods: typedef CString = Pointer<Uint8>
extension CStringMethods on CString {
// ..
} You've proposed the following in the CL disallowing subclassing Pointer: class CString extends Struct {}
Pointer<CString> = Pointer<Uint8> This seems to equate Alternatively we could require manual wrapping, but that does not enable us to write CString in function signatures. class CString {
final Pointer<Uint8> chars;
} |
My feeling is that typedef/extension method is "obviously" the right solution. What I proposed (intended to be a temporary workaround) is actually: class CString extends Struct<CString> {
Uint8 char;
}
Pointer<CString> = Pointer<Uint8> Which captures the intuitive connection between memory behind a |
I'm not an expert on the commonalities between inline C arrays and char pointers, but the sqlite3 api just mentions If we want to keep the mental load of writing signatures and bindings low, we should maybe opt for Pointer<Char> myString;
myString.load<Char>().someMethod(); |
If we add Pointer<Utf8> myString;
myString.ref.toString(); |
dart:ffi
provides two ways to instantiatePointer
objects:allocate
andfromAddress
.Structs in
dart:ffi
are modeled as subtypes ofPointer<Void>
. For this reason the signature offromAddress
is:allocate
does not support allocating structs with malloc, as we do not have a way (yet) to know what the size of a struct is. Because of thisallocate
always returns aPointer
, not a subtype:With these signatures,
allocate
can be a factory, static method or top level function, whilefromAddress
can only be a static method or top level function (as the static return type can be a sub type of Pointer).We might be able to support
allocate
of structs in the future, if we have a predefined way to calculate/define the size of structs.For reference, the current boilerplate of structs:
Do we prefer a factory constructor for
allocate
?Or do we want to keep
allocate
andfromAddress
as similar as possible and change the signature ofallocate
to support allocating structs as well?And if so, a top level function or a static method for both?
The text was updated successfully, but these errors were encountered: