diff --git a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart index 2e4c50d9b7..a086b56ef8 100644 --- a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart @@ -24,6 +24,7 @@ class CppMethod extends AstNode with HasLocalScope { final bool isConstant; final bool isStatic; final CppMethodKind kind; + final CppClass? originatingClass; CppMethod({ required this.name, @@ -33,10 +34,33 @@ class CppMethod extends AstNode with HasLocalScope { required this.isConstant, this.isStatic = false, this.kind = CppMethodKind.method, + this.originatingClass, }); bool get isConstructor => kind == .constructor; + CppMethod cloneForClass(CppClass targetClass, CppClass baseClass) { + return CppMethod( + name: Symbol( + '${targetClass.originalName}_$originalName', + SymbolKind.method, + ), + originalName: originalName, + returnType: returnType, + parameters: parameters.map((p) => p.clone()).toList(), + isConstant: isConstant, + isStatic: isStatic, + kind: kind, + originatingClass: baseClass, + ); + } + + String signatureKey() { + final paramTypes = parameters.map((p) => p.type.cacheKey()).join(','); + final constSuffix = isConstant ? ' const' : ''; + return '$originalName($paramTypes)$constSuffix'; + } + @override void visit(Visitation visitation) => visitation.visitCppMethod(this); @@ -46,6 +70,7 @@ class CppMethod extends AstNode with HasLocalScope { visitor.visit(name); visitor.visit(returnType); visitor.visitAll(parameters); + visitor.visit(originatingClass); } } @@ -71,6 +96,12 @@ class CppClass extends BindingType with HasLocalScope { final List methods; final List fields; + /// The public C++ base classes for this class, in declaration order. + /// + /// Only public inheritance is represented here. Protected and private bases + /// are silently ignored by the parser. + final List bases; + CppClass({ super.usr, super.originalName, @@ -79,6 +110,7 @@ class CppClass extends BindingType with HasLocalScope { required this.context, required this.methods, required this.fields, + this.bases = const [], }); @override @@ -96,6 +128,11 @@ class CppClass extends BindingType with HasLocalScope { required LocalVariables localVariables, }) => '$value._ptr'; + void copyMethod(CppMethod method, CppClass originatingBase) { + final cloned = method.cloneForClass(this, originatingBase); + methods.add(cloned); + } + @override BindingString toBindingString(Writer w) { final s = StringBuffer(); @@ -115,8 +152,13 @@ class CppClass extends BindingType with HasLocalScope { final deleteGlue = '_$deleteSymbol'; s.write(makeDartDoc(dartDoc)); + // Build the implements clause: ffi.Finalizable + public base classes. + final implementsClause = [ + '$ffiPrefix.Finalizable', + ...bases.map((b) => b.name), + ].join(', '); s.write(''' -class $name implements $ffiPrefix.Finalizable { +class $name implements $implementsClause { $ptrVoid _ptr; '''); @@ -287,7 +329,7 @@ class $name implements $ffiPrefix.Finalizable { } } s.write(''' - void dispose() { + ${bases.isNotEmpty ? '@override\n ' : ''}void dispose() { if (_ptr == $ffiPrefix.nullptr) { throw StateError('This object has already been disposed.'); } @@ -302,6 +344,7 @@ class $name implements $ffiPrefix.Finalizable { _ptr = $ffiPrefix.nullptr; } '''); + s.write('}\n'); // Writes a @Native annotation + external declaration for a glue function. @@ -403,17 +446,15 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { final otherParams = method.parameters.map(paramDecl); if (method.isStatic) { + final targetType = + method.originatingClass?.originalName ?? originalName; params = otherParams.join(', '); body = - '$returnPrefix$originalName::' + '$returnPrefix$targetType::' '${method.originalName}($callArgs);'; } else { - final String selfType; - if (method.isConstant) { - selfType = 'const $originalName'; - } else { - selfType = originalName; - } + final constPrefix = method.isConstant ? 'const ' : ''; + final selfType = '$constPrefix$originalName'; params = ['$selfType* self', ...otherParams].join(', '); final methodName = method.originalName; final suffix = method.returnType is CppUniquePtrType @@ -456,6 +497,7 @@ FFIGEN_EXPORT $returnTypeString $symbol($params) { super.visitChildren(visitor); visitor.visitAll(methods); visitor.visitAll(fields); + visitor.visitAll(bases); visitor.visit(ffiImport); } } diff --git a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart index 27bf8c496b..3e35be29f6 100644 --- a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart +++ b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart @@ -750,6 +750,22 @@ class Clang { late final _clang_getCString = _clang_getCStringPtr .asFunction Function(CXString)>(); + /// Returns the access control level for the referenced object. + /// + /// If the cursor refers to a C++ declaration, its access control level within its + /// parent scope is returned. Otherwise, if the cursor refers to a base specifier or + /// access specifier, the specifier itself is returned. + int clang_getCXXAccessSpecifier(CXCursor arg0) { + return _clang_getCXXAccessSpecifier(arg0); + } + + late final _clang_getCXXAccessSpecifierPtr = + _lookup>( + 'clang_getCXXAccessSpecifier', + ); + late final _clang_getCXXAccessSpecifier = _clang_getCXXAccessSpecifierPtr + .asFunction(); + /// Return the canonical type for a CXType. /// /// Clang's type system explicitly models typedefs and all the ways @@ -3042,6 +3058,15 @@ sealed class CXVisitorResult { static const CXVisit_Continue = 1; } +/// Represents the C++ access control level to a base class for a +/// cursor with kind CX_CXXBaseSpecifier. +sealed class CX_CXXAccessSpecifier { + static const CX_CXXInvalidAccessSpecifier = 0; + static const CX_CXXPublic = 1; + static const CX_CXXProtected = 2; + static const CX_CXXPrivate = 3; +} + /// Represents the storage classes as declared in the source. CX_SC_Invalid /// was added for the case that the passed cursor in not a declaration. sealed class CX_StorageClass { diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart index bbf538ee49..5418c2d6b2 100644 --- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/classdecl_parser.dart @@ -64,6 +64,9 @@ CppClass? parseClassDeclaration(Context context, clang_types.CXCursor cursor) { } }); + // Parse public base classes (only public specifiers; non-public are ignored). + final bases = _parsePublicBases(context, cursor); + final cppClass = CppClass( usr: usr, dartDoc: getCursorDocComment( @@ -76,6 +79,7 @@ CppClass? parseClassDeclaration(Context context, clang_types.CXCursor cursor) { context: context, methods: methods, fields: [], + bases: bases, ); context.bindingsIndex.addCppClassToSeen(usr, cppClass); @@ -83,6 +87,33 @@ CppClass? parseClassDeclaration(Context context, clang_types.CXCursor cursor) { return cppClass; } +/// Parses the direct public base classes of [cursor]. +List _parsePublicBases(Context context, clang_types.CXCursor cursor) { + final bases = []; + + cursor.visitChildren((child) { + final kind = clang.clang_getCursorKind(child); + if (kind != clang_types.CXCursorKind.CXCursor_CXXBaseSpecifier) return; + + final access = clang.clang_getCXXAccessSpecifier(child); + if (access != clang_types.CX_CXXAccessSpecifier.CX_CXXPublic) return; + + final baseType = clang.clang_getCursorType(child); + final baseDeclCursor = clang.clang_getTypeDeclaration(baseType); + final baseUsr = baseDeclCursor.usr(); + + final baseClass = context.bindingsIndex.getSeenCppClass(baseUsr); + if (baseClass == null) { + final parsed = parseClassDeclaration(context, baseDeclCursor); + if (parsed != null) bases.add(parsed); + } else { + bases.add(baseClass); + } + }); + + return bases; +} + void _parseAnyMethod( Context context, clang_types.CXCursor cursor, diff --git a/pkgs/ffigen/lib/src/visitor/copy_methods_from_super_type.dart b/pkgs/ffigen/lib/src/visitor/copy_methods_from_super_type.dart index 4f4f82ce09..110bc44552 100644 --- a/pkgs/ffigen/lib/src/visitor/copy_methods_from_super_type.dart +++ b/pkgs/ffigen/lib/src/visitor/copy_methods_from_super_type.dart @@ -40,6 +40,35 @@ const _excludedNSObjectMethods = { }; class CopyMethodsFromSuperTypesVisitation extends Visitation { + @override + void visitCppClass(CppClass node) { + node.visitChildren(visitor); + + final existingSignatures = node.methods + .map((m) => m.signatureKey()) + .toSet(); + + _copyCppMethodsFromBase(node, node, existingSignatures); + } + + void _copyCppMethodsFromBase( + CppClass target, + CppClass current, + Set existingSignatures, + ) { + for (final method in current.methods) { + if (method.kind == CppMethodKind.constructor) continue; + + if (existingSignatures.add(method.signatureKey())) { + target.copyMethod(method, current); + } + } + + for (final base in current.bases) { + _copyCppMethodsFromBase(target, base, existingSignatures); + } + } + @override void visitObjCInterface(ObjCInterface node) { node.visitChildren(visitor, typeGraphOnly: true); diff --git a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.cpp b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.cpp new file mode 100644 index 0000000000..ef4d685b3f --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "cpp_inheritance_test.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +Shape::Shape(double x, double y) : x_(x), y_(y) {} +Shape::~Shape() {} +double Shape::getX() const { return x_; } +double Shape::getY() const { return y_; } + +Drawable::Drawable() : drawCount_(0) {} +Drawable::~Drawable() {} +int Drawable::draw() const { return 42; } + +Circle::Circle(double x, double y, double radius) + : Shape(x, y), radius_(radius) {} + +double Circle::area() const { + return M_PI * radius_ * radius_; +} + +ColoredCircle::ColoredCircle(double x, double y, double radius, int color) + : Circle(x, y, radius), Drawable(), color_(color) {} + +int ColoredCircle::getColor() const { return color_; } + +Square::Square(double x, double y, double side) + : Shape(x, y), side_(side) {} + +double Square::getX() const { return Shape::getX() + side_; } + +double Square::area() const { return side_ * side_; } + +int AccessBase::value() const { return v_; } +PublicDerived::PublicDerived() {} +ProtectedDerived::ProtectedDerived() {} +PrivateDerived::PrivateDerived() {} + +OverloadBase::OverloadBase() {} +OverloadBase::~OverloadBase() {} +int OverloadBase::getValue(int x) { return x * 2; } +double OverloadBase::getValueDouble(double x) { return x * 3.0; } + +OverloadDerived::OverloadDerived() {} +int OverloadDerived::getValue(int x) { return x * 10; } + +DiamondBase::DiamondBase() {} +DiamondBase::~DiamondBase() {} +int DiamondBase::baseVal() const { return 42; } +int DiamondBase::virtVal() const { return 100; } + +DiamondLeft::DiamondLeft() {} +int DiamondLeft::virtVal() const { return 200; } +DiamondRight::DiamondRight() {} +DiamondDerived::DiamondDerived() {} diff --git a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.dart b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.dart new file mode 100644 index 0000000000..cde64b07cb --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.dart @@ -0,0 +1,132 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:test/test.dart'; +import 'cpp_inheritance_test_bindings.dart'; + +void main() { + group('C++ Inheritance', () { + test('Single inheritance - Circle implements Shape', () { + final circle = Circle(10.0, 20.0, 5.0); + + // Polymorphic interface conformance in Dart + expect(circle, isA()); + + // Own methods + expect(circle.area(), closeTo(78.5398, 0.001)); + + // Inherited methods from Shape + expect(circle.getX(), 10.0); + expect(circle.getY(), 20.0); + + circle.dispose(); + }); + + test('Multiple inheritance - ColoredCircle', () { + final coloredCircle = ColoredCircle(1.0, 2.0, 3.0, 0xFF00FF); + + // Polymorphic interface conformance + expect(coloredCircle, isA()); + expect(coloredCircle, isA()); + expect(coloredCircle, isA()); + + // Own method + expect(coloredCircle.getColor(), 0xFF00FF); + + // Inherited from Circle + expect(coloredCircle.area(), closeTo(28.2743, 0.001)); + + // Inherited from Shape (transitive) + expect(coloredCircle.getX(), 1.0); + expect(coloredCircle.getY(), 2.0); + + // Inherited from Drawable (multiple inheritance branch) + expect(coloredCircle.draw(), 42); + + coloredCircle.dispose(); + }); + + test('Virtual method override - Square overrides getX', () { + final square = Square(5.0, 10.0, 4.0); + + expect(square, isA()); + expect(square.area(), 16.0); + expect(square.getY(), 10.0); + + // Overridden getX() returns x + side = 5.0 + 4.0 = 9.0 + expect(square.getX(), 9.0); + + square.dispose(); + }); + + test('Polymorphic delegation via interface types', () { + final shapes = [Circle(0.0, 0.0, 1.0), Square(10.0, 20.0, 5.0)]; + + expect(shapes[0].getX(), 0.0); + expect(shapes[1].getX(), 15.0); // 10 + 5 via virtual override + + for (final shape in shapes) { + shape.dispose(); + } + }); + + test('Access specifier filtering - Public vs Private/Protected base', () { + // PublicDerived inherits public AccessBase -> implements AccessBase + final publicDerived = PublicDerived(); + expect(publicDerived, isA()); + expect(publicDerived.value(), 99); + publicDerived.dispose(); + + // ProtectedDerived and PrivateDerived do NOT implement AccessBase + final protectedDerived = ProtectedDerived(); + expect(protectedDerived, isNot(isA())); + protectedDerived.dispose(); + + final privateDerived = PrivateDerived(); + expect(privateDerived, isNot(isA())); + privateDerived.dispose(); + }); + + test('Throw StateError on inherited methods after dispose', () { + final circle = Circle(1.0, 2.0, 3.0); + circle.dispose(); + + expect(circle.getX, throwsStateError); + expect(circle.getY, throwsStateError); + expect(circle.area, throwsStateError); + }); + + test('Overload handling - OverloadDerived overrides & inherits', () { + final overload = OverloadDerived(); + expect(overload, isA()); + + // Overridden getValue(int): 5 * 10 = 50 + expect(overload.getValue(5), 50); + + // Inherited getValueDouble(double): 5.0 * 3.0 = 15.0 + expect(overload.getValueDouble(5.0), closeTo(15.0, 0.001)); + + overload.dispose(); + }); + + test('Diamond inheritance - DiamondDerived inherits from both sides', () { + final d = DiamondDerived(); + + // DiamondDerived implements both DiamondLeft and DiamondRight + expect(d, isA()); + expect(d, isA()); + // And transitively implements DiamondBase through each branch + expect(d, isA()); + + // baseVal() is delegated through DiamondLeft (first direct base), + // which itself dispatches through C++ — no ambiguity. + expect(d.baseVal(), 42); + + // virtVal() is overridden by DiamondLeft to return 200 + expect(d.virtVal(), 200); + + d.dispose(); + }); + }); +} diff --git a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h new file mode 100644 index 0000000000..2a40f171e7 --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h @@ -0,0 +1,102 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class Shape { +public: + Shape(double x, double y); + virtual ~Shape(); + virtual double getX() const; + double getY() const; + +private: + double x_; + double y_; +}; + +class Drawable { +public: + Drawable(); + virtual ~Drawable(); + virtual int draw() const; + +private: + int drawCount_; +}; + +class Circle : public Shape { +public: + Circle(double x, double y, double radius); + double area() const; + +private: + double radius_; +}; + +class ColoredCircle : public Circle, public Drawable { +public: + ColoredCircle(double x, double y, double radius, int color); + int getColor() const; + +private: + int color_; +}; + +class Square : public Shape { +public: + Square(double x, double y, double side); + double getX() const override; // override of Shape::getX + double area() const; + +private: + double side_; +}; + +class AccessBase { +public: + int value() const; +private: + int v_ = 99; +}; + +class PublicDerived : public AccessBase { public: PublicDerived(); }; +class ProtectedDerived : protected AccessBase { public: ProtectedDerived(); }; +class PrivateDerived : private AccessBase { public: PrivateDerived(); }; + +class OverloadBase { +public: + OverloadBase(); + virtual ~OverloadBase(); + int getValue(int x); + double getValueDouble(double x); +}; + +class OverloadDerived : public OverloadBase { +public: + OverloadDerived(); + int getValue(int x); +}; + +class DiamondBase { +public: + DiamondBase(); + virtual ~DiamondBase(); + int baseVal() const; + virtual int virtVal() const; +}; + +class DiamondLeft : virtual public DiamondBase { +public: + DiamondLeft(); + int virtVal() const override; +}; + +class DiamondRight : virtual public DiamondBase { +public: + DiamondRight(); +}; + +class DiamondDerived : public DiamondLeft, public DiamondRight { +public: + DiamondDerived(); +}; diff --git a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart new file mode 100644 index 0000000000..8d465f547d --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart @@ -0,0 +1,2255 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint, unused_import, unused_element, deprecated_member_use_from_same_package +@ffi.DefaultAsset('package:ffigen/cpp_test') +library; + +import 'dart:ffi' as ffi; + +class AccessBase implements ffi.Finalizable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_AccessBase_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + AccessBase.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_AccessBase_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_AccessBase_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + int value() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _AccessBase_value(_ptr); + } + + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native)>(symbol: 'AccessBase_value') +external int _AccessBase_value(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'AccessBase_delete', +) +external void _AccessBase_delete(ffi.Pointer self); + +class Circle implements ffi.Finalizable, Shape { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Circle_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + Circle.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Circle_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Circle_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory Circle(double x, double y, double radius) { + return Circle.fromPointer(_Circle_new(x, y, radius), takeOwnership: true); + } + double area() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Circle_area(_ptr); + } + + double getX() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Circle_getX(_ptr); + } + + double getY() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Circle_getY(_ptr); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function(ffi.Double, ffi.Double, ffi.Double)>( + symbol: 'Circle_new', +) +external ffi.Pointer _Circle_new(double x, double y, double radius); + +@ffi.Native)>(symbol: 'Circle_area') +external double _Circle_area(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Circle_getX') +external double _Circle_getX(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Circle_getY') +external double _Circle_getY(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Circle_delete') +external void _Circle_delete(ffi.Pointer self); + +class ColoredCircle implements ffi.Finalizable, Circle, Drawable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_ColoredCircle_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + ColoredCircle.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_ColoredCircle_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_ColoredCircle_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory ColoredCircle(double x, double y, double radius, int color) { + return ColoredCircle.fromPointer( + _ColoredCircle_new(x, y, radius, color), + takeOwnership: true, + ); + } + int getColor() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_getColor(_ptr); + } + + double area() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_area(_ptr); + } + + double getX() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_getX(_ptr); + } + + double getY() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_getY(_ptr); + } + + int draw() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_draw(_ptr); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native< + ffi.Pointer Function(ffi.Double, ffi.Double, ffi.Double, ffi.Int) +>(symbol: 'ColoredCircle_new') +external ffi.Pointer _ColoredCircle_new( + double x, + double y, + double radius, + int color, +); + +@ffi.Native)>( + symbol: 'ColoredCircle_getColor', +) +external int _ColoredCircle_getColor(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_area', +) +external double _ColoredCircle_area(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_getX', +) +external double _ColoredCircle_getX(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_getY', +) +external double _ColoredCircle_getY(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_draw', +) +external int _ColoredCircle_draw(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_delete', +) +external void _ColoredCircle_delete(ffi.Pointer self); + +class DiamondBase implements ffi.Finalizable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondBase_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + DiamondBase.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondBase_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondBase_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory DiamondBase() { + return DiamondBase.fromPointer(_DiamondBase_new(), takeOwnership: true); + } + int baseVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondBase_baseVal(_ptr); + } + + int virtVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondBase_virtVal(_ptr); + } + + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'DiamondBase_new') +external ffi.Pointer _DiamondBase_new(); + +@ffi.Native)>( + symbol: 'DiamondBase_baseVal', +) +external int _DiamondBase_baseVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondBase_virtVal', +) +external int _DiamondBase_virtVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondBase_delete', +) +external void _DiamondBase_delete(ffi.Pointer self); + +class DiamondDerived implements ffi.Finalizable, DiamondLeft, DiamondRight { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondDerived_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + DiamondDerived.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondDerived_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondDerived_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory DiamondDerived() { + return DiamondDerived.fromPointer( + _DiamondDerived_new(), + takeOwnership: true, + ); + } + int virtVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondDerived_virtVal(_ptr); + } + + int baseVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondDerived_baseVal(_ptr); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'DiamondDerived_new') +external ffi.Pointer _DiamondDerived_new(); + +@ffi.Native)>( + symbol: 'DiamondDerived_virtVal', +) +external int _DiamondDerived_virtVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondDerived_baseVal', +) +external int _DiamondDerived_baseVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondDerived_delete', +) +external void _DiamondDerived_delete(ffi.Pointer self); + +class DiamondLeft implements ffi.Finalizable, DiamondBase { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondLeft_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + DiamondLeft.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondLeft_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondLeft_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory DiamondLeft() { + return DiamondLeft.fromPointer(_DiamondLeft_new(), takeOwnership: true); + } + int virtVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondLeft_virtVal(_ptr); + } + + int baseVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondLeft_baseVal(_ptr); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'DiamondLeft_new') +external ffi.Pointer _DiamondLeft_new(); + +@ffi.Native)>( + symbol: 'DiamondLeft_virtVal', +) +external int _DiamondLeft_virtVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondLeft_baseVal', +) +external int _DiamondLeft_baseVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondLeft_delete', +) +external void _DiamondLeft_delete(ffi.Pointer self); + +class DiamondRight implements ffi.Finalizable, DiamondBase { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondRight_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + DiamondRight.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondRight_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_DiamondRight_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory DiamondRight() { + return DiamondRight.fromPointer(_DiamondRight_new(), takeOwnership: true); + } + int baseVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondRight_baseVal(_ptr); + } + + int virtVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondRight_virtVal(_ptr); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'DiamondRight_new') +external ffi.Pointer _DiamondRight_new(); + +@ffi.Native)>( + symbol: 'DiamondRight_baseVal', +) +external int _DiamondRight_baseVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondRight_virtVal', +) +external int _DiamondRight_virtVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondRight_delete', +) +external void _DiamondRight_delete(ffi.Pointer self); + +class Drawable implements ffi.Finalizable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Drawable_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + Drawable.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Drawable_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Drawable_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory Drawable() { + return Drawable.fromPointer(_Drawable_new(), takeOwnership: true); + } + int draw() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Drawable_draw(_ptr); + } + + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'Drawable_new') +external ffi.Pointer _Drawable_new(); + +@ffi.Native)>(symbol: 'Drawable_draw') +external int _Drawable_draw(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Drawable_delete') +external void _Drawable_delete(ffi.Pointer self); + +class OverloadBase implements ffi.Finalizable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_OverloadBase_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + OverloadBase.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_OverloadBase_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_OverloadBase_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory OverloadBase() { + return OverloadBase.fromPointer(_OverloadBase_new(), takeOwnership: true); + } + int getValue(int x) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _OverloadBase_getValue(_ptr, x); + } + + double getValueDouble(double x) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _OverloadBase_getValueDouble(_ptr, x); + } + + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'OverloadBase_new') +external ffi.Pointer _OverloadBase_new(); + +@ffi.Native, ffi.Int)>( + symbol: 'OverloadBase_getValue', +) +external int _OverloadBase_getValue(ffi.Pointer self, int x); + +@ffi.Native, ffi.Double)>( + symbol: 'OverloadBase_getValueDouble', +) +external double _OverloadBase_getValueDouble( + ffi.Pointer self, + double x, +); + +@ffi.Native)>( + symbol: 'OverloadBase_delete', +) +external void _OverloadBase_delete(ffi.Pointer self); + +class OverloadDerived implements ffi.Finalizable, OverloadBase { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_OverloadDerived_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + OverloadDerived.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_OverloadDerived_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_OverloadDerived_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory OverloadDerived() { + return OverloadDerived.fromPointer( + _OverloadDerived_new(), + takeOwnership: true, + ); + } + int getValue(int x) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _OverloadDerived_getValue(_ptr, x); + } + + double getValueDouble(double x) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _OverloadDerived_getValueDouble(_ptr, x); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'OverloadDerived_new') +external ffi.Pointer _OverloadDerived_new(); + +@ffi.Native, ffi.Int)>( + symbol: 'OverloadDerived_getValue', +) +external int _OverloadDerived_getValue(ffi.Pointer self, int x); + +@ffi.Native, ffi.Double)>( + symbol: 'OverloadDerived_getValueDouble', +) +external double _OverloadDerived_getValueDouble( + ffi.Pointer self, + double x, +); + +@ffi.Native)>( + symbol: 'OverloadDerived_delete', +) +external void _OverloadDerived_delete(ffi.Pointer self); + +class PrivateDerived implements ffi.Finalizable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_PrivateDerived_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + PrivateDerived.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_PrivateDerived_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_PrivateDerived_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory PrivateDerived() { + return PrivateDerived.fromPointer( + _PrivateDerived_new(), + takeOwnership: true, + ); + } + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'PrivateDerived_new') +external ffi.Pointer _PrivateDerived_new(); + +@ffi.Native)>( + symbol: 'PrivateDerived_delete', +) +external void _PrivateDerived_delete(ffi.Pointer self); + +class ProtectedDerived implements ffi.Finalizable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_ProtectedDerived_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + ProtectedDerived.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_ProtectedDerived_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_ProtectedDerived_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory ProtectedDerived() { + return ProtectedDerived.fromPointer( + _ProtectedDerived_new(), + takeOwnership: true, + ); + } + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'ProtectedDerived_new') +external ffi.Pointer _ProtectedDerived_new(); + +@ffi.Native)>( + symbol: 'ProtectedDerived_delete', +) +external void _ProtectedDerived_delete(ffi.Pointer self); + +class PublicDerived implements ffi.Finalizable, AccessBase { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_PublicDerived_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + PublicDerived.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_PublicDerived_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_PublicDerived_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory PublicDerived() { + return PublicDerived.fromPointer(_PublicDerived_new(), takeOwnership: true); + } + int value() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _PublicDerived_value(_ptr); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function()>(symbol: 'PublicDerived_new') +external ffi.Pointer _PublicDerived_new(); + +@ffi.Native)>( + symbol: 'PublicDerived_value', +) +external int _PublicDerived_value(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'PublicDerived_delete', +) +external void _PublicDerived_delete(ffi.Pointer self); + +class Shape implements ffi.Finalizable { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Shape_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + Shape.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Shape_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Shape_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory Shape(double x, double y) { + return Shape.fromPointer(_Shape_new(x, y), takeOwnership: true); + } + double getX() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Shape_getX(_ptr); + } + + double getY() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Shape_getY(_ptr); + } + + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function(ffi.Double, ffi.Double)>( + symbol: 'Shape_new', +) +external ffi.Pointer _Shape_new(double x, double y); + +@ffi.Native)>(symbol: 'Shape_getX') +external double _Shape_getX(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Shape_getY') +external double _Shape_getY(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Shape_delete') +external void _Shape_delete(ffi.Pointer self); + +class Square implements ffi.Finalizable, Shape { + ffi.Pointer _ptr; + static final _defaultFinalizer = ffi.NativeFinalizer( + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Square_delete), + ); + + /// The finalizer currently attached for this instance, or [null] if this + /// object does not own its pointer. + ffi.NativeFinalizer? _activeFinalizer; + + /// The native function pointer used by [_activeFinalizer], stored so that + /// [dispose] can call the correct destructor directly. + ffi.Pointer)>>? + _activeFinalizerFn; + + Square.fromPointer(this._ptr, {bool takeOwnership = false}) { + if (takeOwnership) { + _defaultFinalizer.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = _defaultFinalizer; + _activeFinalizerFn = + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Square_delete); + } + } + + /// Attaches a finalizer so this object takes ownership of the underlying + /// C++ pointer. If [customFinalizer] is provided it is used instead of the + /// default `delete` finalizer, which is useful when the object was not + /// allocated with `new` (e.g. `malloc` or a custom allocator). + /// + /// Both [customFinalizer] and [customFinalizerFn] must be provided together. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object already owns the pointer. + void retainOwnership([ + ffi.NativeFinalizer? customFinalizer, + ffi.Pointer)>>? + customFinalizerFn, + ]) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer != null) { + throw StateError('This object already owns its pointer.'); + } + if ((customFinalizer == null) != (customFinalizerFn == null)) { + throw ArgumentError( + 'Both customFinalizer and customFinalizerFn must be provided together.', + ); + } + final fin = customFinalizer ?? _defaultFinalizer; + final fnPtr = + customFinalizerFn ?? + ffi.Native.addressOf< + ffi.NativeFunction)> + >(_Square_delete); + fin.attach(this, _ptr.cast(), detach: this); + _activeFinalizer = fin; + _activeFinalizerFn = fnPtr; + } + + /// Detaches the finalizer so this object releases ownership of the + /// underlying C++ pointer. The caller becomes responsible for freeing + /// the memory. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + void releaseOwnership() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError('This object does not own its pointer.'); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn = null; + } + + /// Detaches the finalizer and invalidates this object, returning the + /// underlying C++ pointer. + /// + /// Throws a [StateError] if the object has already been disposed, or if + /// this object does not own the pointer. + ffi.Pointer detachPointer() { + final rawPtr = _ptr; + releaseOwnership(); + _ptr = ffi.nullptr; + return rawPtr; + } + + factory Square(double x, double y, double side) { + return Square.fromPointer(_Square_new(x, y, side), takeOwnership: true); + } + double getX() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Square_getX(_ptr); + } + + double area() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Square_area(_ptr); + } + + double getY() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Square_getY(_ptr); + } + + @override + void dispose() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + if (_activeFinalizer == null) { + throw StateError( + 'Cannot dispose a non-owning wrapper. ' + 'Call retainOwnership() first to take ownership.', + ); + } + _activeFinalizer!.detach(this); + _activeFinalizer = null; + _activeFinalizerFn?.asFunction)>()( + _ptr, + ); + _activeFinalizerFn = null; + _ptr = ffi.nullptr; + } +} + +@ffi.Native Function(ffi.Double, ffi.Double, ffi.Double)>( + symbol: 'Square_new', +) +external ffi.Pointer _Square_new(double x, double y, double side); + +@ffi.Native)>(symbol: 'Square_getX') +external double _Square_getX(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Square_area') +external double _Square_area(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Square_getY') +external double _Square_getY(ffi.Pointer self); + +@ffi.Native)>(symbol: 'Square_delete') +external void _Square_delete(ffi.Pointer self); diff --git a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart.cpp b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart.cpp new file mode 100644 index 0000000000..6280090e40 --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart.cpp @@ -0,0 +1,240 @@ +#include +#include "cpp_inheritance_test.h" + +#if defined(_WIN32) +#define FFIGEN_EXPORT __declspec(dllexport) +#else +#define FFIGEN_EXPORT +#endif + +extern "C" { + +FFIGEN_EXPORT int AccessBase_value(const AccessBase* self) { + return self->value(); +} + +FFIGEN_EXPORT void AccessBase_delete(AccessBase* self) { + delete self; +} + +FFIGEN_EXPORT Circle* Circle_new(double x, double y, double radius) { + return new Circle(x, y, radius); +} + +FFIGEN_EXPORT double Circle_area(const Circle* self) { + return self->area(); +} + +FFIGEN_EXPORT double Circle_getX(const Circle* self) { + return self->getX(); +} + +FFIGEN_EXPORT double Circle_getY(const Circle* self) { + return self->getY(); +} + +FFIGEN_EXPORT void Circle_delete(Circle* self) { + delete self; +} + +FFIGEN_EXPORT ColoredCircle* ColoredCircle_new(double x, double y, double radius, int color) { + return new ColoredCircle(x, y, radius, color); +} + +FFIGEN_EXPORT int ColoredCircle_getColor(const ColoredCircle* self) { + return self->getColor(); +} + +FFIGEN_EXPORT double ColoredCircle_area(const ColoredCircle* self) { + return self->area(); +} + +FFIGEN_EXPORT double ColoredCircle_getX(const ColoredCircle* self) { + return self->getX(); +} + +FFIGEN_EXPORT double ColoredCircle_getY(const ColoredCircle* self) { + return self->getY(); +} + +FFIGEN_EXPORT int ColoredCircle_draw(const ColoredCircle* self) { + return self->draw(); +} + +FFIGEN_EXPORT void ColoredCircle_delete(ColoredCircle* self) { + delete self; +} + +FFIGEN_EXPORT DiamondBase* DiamondBase_new() { + return new DiamondBase(); +} + +FFIGEN_EXPORT int DiamondBase_baseVal(const DiamondBase* self) { + return self->baseVal(); +} + +FFIGEN_EXPORT int DiamondBase_virtVal(const DiamondBase* self) { + return self->virtVal(); +} + +FFIGEN_EXPORT void DiamondBase_delete(DiamondBase* self) { + delete self; +} + +FFIGEN_EXPORT DiamondDerived* DiamondDerived_new() { + return new DiamondDerived(); +} + +FFIGEN_EXPORT int DiamondDerived_virtVal(const DiamondDerived* self) { + return self->virtVal(); +} + +FFIGEN_EXPORT int DiamondDerived_baseVal(const DiamondDerived* self) { + return self->baseVal(); +} + +FFIGEN_EXPORT void DiamondDerived_delete(DiamondDerived* self) { + delete self; +} + +FFIGEN_EXPORT DiamondLeft* DiamondLeft_new() { + return new DiamondLeft(); +} + +FFIGEN_EXPORT int DiamondLeft_virtVal(const DiamondLeft* self) { + return self->virtVal(); +} + +FFIGEN_EXPORT int DiamondLeft_baseVal(const DiamondLeft* self) { + return self->baseVal(); +} + +FFIGEN_EXPORT void DiamondLeft_delete(DiamondLeft* self) { + delete self; +} + +FFIGEN_EXPORT DiamondRight* DiamondRight_new() { + return new DiamondRight(); +} + +FFIGEN_EXPORT int DiamondRight_baseVal(const DiamondRight* self) { + return self->baseVal(); +} + +FFIGEN_EXPORT int DiamondRight_virtVal(const DiamondRight* self) { + return self->virtVal(); +} + +FFIGEN_EXPORT void DiamondRight_delete(DiamondRight* self) { + delete self; +} + +FFIGEN_EXPORT Drawable* Drawable_new() { + return new Drawable(); +} + +FFIGEN_EXPORT int Drawable_draw(const Drawable* self) { + return self->draw(); +} + +FFIGEN_EXPORT void Drawable_delete(Drawable* self) { + delete self; +} + +FFIGEN_EXPORT OverloadBase* OverloadBase_new() { + return new OverloadBase(); +} + +FFIGEN_EXPORT int OverloadBase_getValue(OverloadBase* self, int x) { + return self->getValue(x); +} + +FFIGEN_EXPORT double OverloadBase_getValueDouble(OverloadBase* self, double x) { + return self->getValueDouble(x); +} + +FFIGEN_EXPORT void OverloadBase_delete(OverloadBase* self) { + delete self; +} + +FFIGEN_EXPORT OverloadDerived* OverloadDerived_new() { + return new OverloadDerived(); +} + +FFIGEN_EXPORT int OverloadDerived_getValue(OverloadDerived* self, int x) { + return self->getValue(x); +} + +FFIGEN_EXPORT double OverloadDerived_getValueDouble(OverloadDerived* self, double x) { + return self->getValueDouble(x); +} + +FFIGEN_EXPORT void OverloadDerived_delete(OverloadDerived* self) { + delete self; +} + +FFIGEN_EXPORT PrivateDerived* PrivateDerived_new() { + return new PrivateDerived(); +} + +FFIGEN_EXPORT void PrivateDerived_delete(PrivateDerived* self) { + delete self; +} + +FFIGEN_EXPORT ProtectedDerived* ProtectedDerived_new() { + return new ProtectedDerived(); +} + +FFIGEN_EXPORT void ProtectedDerived_delete(ProtectedDerived* self) { + delete self; +} + +FFIGEN_EXPORT PublicDerived* PublicDerived_new() { + return new PublicDerived(); +} + +FFIGEN_EXPORT int PublicDerived_value(const PublicDerived* self) { + return self->value(); +} + +FFIGEN_EXPORT void PublicDerived_delete(PublicDerived* self) { + delete self; +} + +FFIGEN_EXPORT Shape* Shape_new(double x, double y) { + return new Shape(x, y); +} + +FFIGEN_EXPORT double Shape_getX(const Shape* self) { + return self->getX(); +} + +FFIGEN_EXPORT double Shape_getY(const Shape* self) { + return self->getY(); +} + +FFIGEN_EXPORT void Shape_delete(Shape* self) { + delete self; +} + +FFIGEN_EXPORT Square* Square_new(double x, double y, double side) { + return new Square(x, y, side); +} + +FFIGEN_EXPORT double Square_getX(const Square* self) { + return self->getX(); +} + +FFIGEN_EXPORT double Square_area(const Square* self) { + return self->area(); +} + +FFIGEN_EXPORT double Square_getY(const Square* self) { + return self->getY(); +} + +FFIGEN_EXPORT void Square_delete(Square* self) { + delete self; +} + +} diff --git a/pkgs/ffigen/test/native_cpp_test/verify_bindings_test.dart b/pkgs/ffigen/test/native_cpp_test/verify_bindings_test.dart index 2382316f08..eca55d0207 100644 --- a/pkgs/ffigen/test/native_cpp_test/verify_bindings_test.dart +++ b/pkgs/ffigen/test/native_cpp_test/verify_bindings_test.dart @@ -76,6 +76,39 @@ void main() { }), ), ), + 'cpp_inheritance': FfiGenerator( + output: Output( + dartFile: Uri.file('cpp_inheritance_test_bindings.dart'), + style: const NativeExternalBindings( + assetId: 'package:ffigen/cpp_test', + ), + ), + input: Input( + entryPoints: [ + Uri.file(path.join(testDir.path, 'cpp_inheritance_test.h')), + ], + compilerOptions: defaultCppCompilerOptions, + ), + cpp: Cpp( + classes: CppClasses.includeSet({ + 'Shape', + 'Drawable', + 'Circle', + 'ColoredCircle', + 'Square', + 'AccessBase', + 'PublicDerived', + 'ProtectedDerived', + 'PrivateDerived', + 'OverloadBase', + 'OverloadDerived', + 'DiamondBase', + 'DiamondLeft', + 'DiamondRight', + 'DiamondDerived', + }), + ), + ), }; for (final testFile in testFiles) { diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml index b0dbf20aae..021b4874d5 100644 --- a/pkgs/ffigen/tool/libclang_config.yaml +++ b/pkgs/ffigen/tool/libclang_config.yaml @@ -41,6 +41,7 @@ enums: - CXObjCPropertyAttrKind - CXTypeNullabilityKind - CXTypeLayoutError + - CX_CXXAccessSpecifier as-int: include: - .* @@ -125,6 +126,7 @@ functions: - clang_isCursorDefinition - clang_CXXMethod_isConst - clang_CXXMethod_isStatic + - clang_getCXXAccessSpecifier - clang_getCursorAvailability - clang_getCursorPlatformAvailability - clang_disposeCXPlatformAvailability