From a95321d6e66db8a67c39ec6fbb4b467270e53124 Mon Sep 17 00:00:00 2001 From: Hassnaa Mohamed Date: Wed, 12 Aug 2026 23:53:54 +0300 Subject: [PATCH 1/5] [ffigen] Add C++ public inheritance support (single, multiple, diamond) --- .../lib/src/code_generator/cpp_class.dart | 149 +- .../clang_bindings/clang_bindings.dart | 25 + .../sub_parsers/classdecl_parser.dart | 74 + .../native_cpp_test/cpp_inheritance_test.cpp | 58 + .../native_cpp_test/cpp_inheritance_test.dart | 129 + .../native_cpp_test/cpp_inheritance_test.h | 100 + .../cpp_inheritance_test_bindings.dart | 2221 +++++++++++++++++ .../cpp_inheritance_test_bindings.dart.cpp | 220 ++ .../native_cpp_test/verify_bindings_test.dart | 33 + pkgs/ffigen/tool/libclang_config.yaml | 2 + 10 files changed, 3003 insertions(+), 8 deletions(-) create mode 100644 pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.cpp create mode 100644 pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.dart create mode 100644 pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h create mode 100644 pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart create mode 100644 pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart.cpp diff --git a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart index 2e4c50d9b7..0f081045bb 100644 --- a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart @@ -5,6 +5,8 @@ import '../code_generator.dart'; import '../config_provider/public_ast.dart' as public_ast; import '../context.dart'; +import '../header_parser/sub_parsers/classdecl_parser.dart' + show InheritedMethod, collectInheritedMethods, methodSignatureKey; import '../visitor/ast.dart'; import 'binding_string.dart'; @@ -71,6 +73,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 +87,7 @@ class CppClass extends BindingType with HasLocalScope { required this.context, required this.methods, required this.fields, + this.bases = const [], }); @override @@ -96,6 +105,20 @@ class CppClass extends BindingType with HasLocalScope { required LocalVariables localVariables, }) => '$value._ptr'; + /// Returns the list of inherited methods from base classes that are not + /// overridden by this class. + List getInheritedMethodsToDelegate(Context ctx) { + if (bases.isEmpty) return const []; + final ownSignatures = methods + .map((m) => methodSignatureKey(m, ctx)) + .toSet(); + return collectInheritedMethods(this) + .where( + (im) => !ownSignatures.contains(methodSignatureKey(im.method, ctx)), + ) + .toList(); + } + @override BindingString toBindingString(Writer w) { final s = StringBuffer(); @@ -115,8 +138,13 @@ class CppClass extends BindingType with HasLocalScope { final deleteGlue = '_$deleteSymbol'; s.write(makeDartDoc(dartDoc)); + // Build the implements clause: ffi.Finalizable + public base classes. + final baseNames = bases.map((b) => b.name).join(', '); + final implementsClause = bases.isEmpty + ? '$ffiPrefix.Finalizable' + : '$ffiPrefix.Finalizable, $baseNames'; s.write(''' -class $name implements $ffiPrefix.Finalizable { +class $name implements $implementsClause { $ptrVoid _ptr; '''); @@ -287,7 +315,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 +330,49 @@ class $name implements $ffiPrefix.Finalizable { _ptr = $ffiPrefix.nullptr; } '''); + + // Inherited method delegation (Dart side) + final inheritedToDelegate = getInheritedMethodsToDelegate(ctx); + for (final im in inheritedToDelegate) { + final method = im.method; + final base = im.baseClass; + final delegateSymbol = '${name}_${base.name}_${method.originalName}'; + final delegateGlue = '_$delegateSymbol'; + final dartReturn = method.returnType.getDartType(ctx); + final dartParams = dartParamList(method.parameters); + final localVars = LocalVariables(method.localScope); + final callArgs = [ + '_ptr', + ...method.parameters.map( + (p) => p.type.convertDartTypeToFfiDartType( + ctx, + p.name, + objCRetain: false, + objCAutorelease: false, + localVariables: localVars, + ), + ), + ].join(', '); + final decls = localVars.generateDeclarations(); + final returnExpr = method.returnType.convertFfiDartTypeToDartType( + ctx, + '$delegateGlue($callArgs)', + objCRetain: false, + ); + final hasReturn = method.returnType != voidType; + final callLine = hasReturn ? 'return $returnExpr;' : '$returnExpr;'; + s.write('''\ + @override + $dartReturn ${method.originalName}($dartParams) { + if (_ptr == $ffiPrefix.nullptr) { + throw StateError('This object has already been disposed.'); + } + $decls + $callLine + } +'''); + } + s.write('}\n'); // Writes a @Native annotation + external declaration for a glue function. @@ -362,6 +433,33 @@ class $name implements $ffiPrefix.Finalizable { ffiParams: '$ptrVoid self', ); + // @Native declarations for inherited-method delegation glue + for (final im in inheritedToDelegate) { + final method = im.method; + final base = im.baseClass; + final delegateSymbol = '${name}_${base.name}_${method.originalName}'; + final delegateGlue = '_$delegateSymbol'; + final cReturn = method.returnType.getCType(ctx); + final ffiReturn = method.returnType.getFfiDartType(ctx); + final cParams = [ + ptrVoid, // self (typed as derived) + ...method.parameters.map((p) => p.type.getCType(ctx)), + ].join(', '); + final ffiParams = [ + '$ptrVoid self', + ...method.parameters.map( + (p) => '${p.type.getFfiDartType(ctx)} ${p.name}', + ), + ].join(', '); + writeNativeDecl( + symbol: delegateSymbol, + glue: delegateGlue, + cType: '$cReturn Function($cParams)', + ffiReturn: ffiReturn, + ffiParams: ffiParams, + ); + } + return BindingString( type: BindingStringType.cppClass, string: s.toString(), @@ -408,12 +506,8 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { '$returnPrefix$originalName::' '${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 @@ -430,6 +524,44 @@ FFIGEN_EXPORT $returnTypeString $symbol($params) { }) .join('\n\n'); + // Delegation stubs for inherited methods (C++ side) + final inheritedBindings = StringBuffer(); + final inheritedToDelegate = getInheritedMethodsToDelegate(context); + for (final im in inheritedToDelegate) { + final method = im.method; + final base = im.baseClass; + final delegateSymbol = + '${name}_${base.originalName}_${method.originalName}'; + final callArgs = method.parameters.map(_cppCallArg).join(', '); + + final nativeType = method.returnType.getNativeType(context); + final returnTypeString = nativeType.trim(); + final needsReturn = method.returnType != voidType; + final returnPrefix = needsReturn ? 'return ' : ''; + final suffix = method.returnType is CppUniquePtrType ? '.release()' : ''; + + final constPrefix = method.isConstant ? 'const ' : ''; + final selfType = '$constPrefix$originalName'; + final otherParams = method.parameters.map(paramDecl); + final params = ['$selfType* self', ...otherParams].join(', '); + + // static_cast adjusts the this-pointer offset for the base sub-object. + final castTarget = 'static_cast<$constPrefix${base.originalName}*>(self)'; + final body = + '$returnPrefix$castTarget' + '->${method.originalName}($callArgs)$suffix;'; + + inheritedBindings.write(''' + +FFIGEN_EXPORT $returnTypeString $delegateSymbol($params) { + $body +}'''); + } + + if (inheritedBindings.isNotEmpty) { + return '$methodBindings\n\n$deleteWrapper\n' + '${inheritedBindings.toString()}\n\n'; + } return '$methodBindings\n\n$deleteWrapper\n\n'; } @@ -456,6 +588,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 c9d931d0a6..360db4049d 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 @@ -732,6 +732,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 @@ -3024,6 +3040,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..f9fc2e40be 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,76 @@ 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; +} + +String methodSignatureKey(CppMethod method, Context context) { + final paramTypes = method.parameters + .map((p) => p.type.getNativeType(context)) + .join(','); + final constSuffix = method.isConstant ? ' const' : ''; + return '${method.originalName}($paramTypes)$constSuffix'; +} + +List collectInheritedMethods(CppClass cls) { + final seen = {}; + final result = []; + for (final directBase in cls.bases) { + _collectFromBase(directBase, directBase, seen, result, cls.context); + } + return result; +} + +void _collectFromBase( + CppClass current, + CppClass directBase, + Set seen, + List result, + Context context, +) { + for (final base in current.bases) { + _collectFromBase(base, directBase, seen, result, context); + } + for (final method in current.methods) { + if (method.kind == CppMethodKind.constructor) continue; + final key = methodSignatureKey(method, context); + if (seen.add(key)) { + result.add(InheritedMethod(method: method, baseClass: directBase)); + } + } +} + +class InheritedMethod { + final CppMethod method; + final CppClass baseClass; + + const InheritedMethod({required this.method, required this.baseClass}); +} + void _parseAnyMethod( Context context, clang_types.CXCursor cursor, 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..a7e26296a4 --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.cpp @@ -0,0 +1,58 @@ +// 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; } + +DiamondLeft::DiamondLeft() {} +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..5d002d01f2 --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.dart @@ -0,0 +1,129 @@ +// 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); + + 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..05f5424acb --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h @@ -0,0 +1,100 @@ +// 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; +}; + +class DiamondLeft : public DiamondBase { +public: + DiamondLeft(); +}; + +class DiamondRight : 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..10d37ab595 --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart @@ -0,0 +1,2221 @@ +// 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); + } + + @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; + } + + @override + double getX() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Circle_Shape_getX(_ptr); + } + + @override + double getY() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Circle_Shape_getY(_ptr); + } +} + +@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_delete') +external void _Circle_delete(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'Circle_Shape_getX', +) +external double _Circle_Shape_getX(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'Circle_Shape_getY', +) +external double _Circle_Shape_getY(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); + } + + @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; + } + + @override + double getX() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_Circle_getX(_ptr); + } + + @override + double getY() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_Circle_getY(_ptr); + } + + @override + double area() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_Circle_area(_ptr); + } + + @override + int draw() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _ColoredCircle_Drawable_draw(_ptr); + } +} + +@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_delete', +) +external void _ColoredCircle_delete(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_Circle_getX', +) +external double _ColoredCircle_Circle_getX(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_Circle_getY', +) +external double _ColoredCircle_Circle_getY(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_Circle_area', +) +external double _ColoredCircle_Circle_area(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_Drawable_draw', +) +external int _ColoredCircle_Drawable_draw(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); + } + + 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_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, + ); + } + @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; + } + + @override + int baseVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondDerived_DiamondLeft_baseVal(_ptr); + } +} + +@ffi.Native Function()>(symbol: 'DiamondDerived_new') +external ffi.Pointer _DiamondDerived_new(); + +@ffi.Native)>( + symbol: 'DiamondDerived_delete', +) +external void _DiamondDerived_delete(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondDerived_DiamondLeft_baseVal', +) +external int _DiamondDerived_DiamondLeft_baseVal(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); + } + @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; + } + + @override + int baseVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondLeft_DiamondBase_baseVal(_ptr); + } +} + +@ffi.Native Function()>(symbol: 'DiamondLeft_new') +external ffi.Pointer _DiamondLeft_new(); + +@ffi.Native)>( + symbol: 'DiamondLeft_delete', +) +external void _DiamondLeft_delete(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondLeft_DiamondBase_baseVal', +) +external int _DiamondLeft_DiamondBase_baseVal(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); + } + @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; + } + + @override + int baseVal() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _DiamondRight_DiamondBase_baseVal(_ptr); + } +} + +@ffi.Native Function()>(symbol: 'DiamondRight_new') +external ffi.Pointer _DiamondRight_new(); + +@ffi.Native)>( + symbol: 'DiamondRight_delete', +) +external void _DiamondRight_delete(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondRight_DiamondBase_baseVal', +) +external int _DiamondRight_DiamondBase_baseVal(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); + } + + @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; + } + + @override + double getValueDouble(double x) { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _OverloadDerived_OverloadBase_getValueDouble(_ptr, x); + } +} + +@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)>( + symbol: 'OverloadDerived_delete', +) +external void _OverloadDerived_delete(ffi.Pointer self); + +@ffi.Native, ffi.Double)>( + symbol: 'OverloadDerived_OverloadBase_getValueDouble', +) +external double _OverloadDerived_OverloadBase_getValueDouble( + ffi.Pointer self, + double x, +); + +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); + } + @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; + } + + @override + int value() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _PublicDerived_AccessBase_value(_ptr); + } +} + +@ffi.Native Function()>(symbol: 'PublicDerived_new') +external ffi.Pointer _PublicDerived_new(); + +@ffi.Native)>( + symbol: 'PublicDerived_delete', +) +external void _PublicDerived_delete(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'PublicDerived_AccessBase_value', +) +external int _PublicDerived_AccessBase_value(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); + } + + @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; + } + + @override + double getY() { + if (_ptr == ffi.nullptr) { + throw StateError('This object has already been disposed.'); + } + + return _Square_Shape_getY(_ptr); + } +} + +@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_delete') +external void _Square_delete(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'Square_Shape_getY', +) +external double _Square_Shape_getY(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..174303386f --- /dev/null +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart.cpp @@ -0,0 +1,220 @@ +#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 void Circle_delete(Circle* self) { + delete self; +} + +FFIGEN_EXPORT double Circle_Shape_getX(const Circle* self) { + return static_cast(self)->getX(); +} +FFIGEN_EXPORT double Circle_Shape_getY(const Circle* self) { + return static_cast(self)->getY(); +} + +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 void ColoredCircle_delete(ColoredCircle* self) { + delete self; +} + +FFIGEN_EXPORT double ColoredCircle_Circle_getX(const ColoredCircle* self) { + return static_cast(self)->getX(); +} +FFIGEN_EXPORT double ColoredCircle_Circle_getY(const ColoredCircle* self) { + return static_cast(self)->getY(); +} +FFIGEN_EXPORT double ColoredCircle_Circle_area(const ColoredCircle* self) { + return static_cast(self)->area(); +} +FFIGEN_EXPORT int ColoredCircle_Drawable_draw(const ColoredCircle* self) { + return static_cast(self)->draw(); +} + +FFIGEN_EXPORT DiamondBase* DiamondBase_new() { + return new DiamondBase(); +} + +FFIGEN_EXPORT int DiamondBase_baseVal(const DiamondBase* self) { + return self->baseVal(); +} + +FFIGEN_EXPORT void DiamondBase_delete(DiamondBase* self) { + delete self; +} + +FFIGEN_EXPORT DiamondDerived* DiamondDerived_new() { + return new DiamondDerived(); +} + +FFIGEN_EXPORT void DiamondDerived_delete(DiamondDerived* self) { + delete self; +} + +FFIGEN_EXPORT int DiamondDerived_DiamondLeft_baseVal(const DiamondDerived* self) { + return static_cast(self)->baseVal(); +} + +FFIGEN_EXPORT DiamondLeft* DiamondLeft_new() { + return new DiamondLeft(); +} + +FFIGEN_EXPORT void DiamondLeft_delete(DiamondLeft* self) { + delete self; +} + +FFIGEN_EXPORT int DiamondLeft_DiamondBase_baseVal(const DiamondLeft* self) { + return static_cast(self)->baseVal(); +} + +FFIGEN_EXPORT DiamondRight* DiamondRight_new() { + return new DiamondRight(); +} + +FFIGEN_EXPORT void DiamondRight_delete(DiamondRight* self) { + delete self; +} + +FFIGEN_EXPORT int DiamondRight_DiamondBase_baseVal(const DiamondRight* self) { + return static_cast(self)->baseVal(); +} + +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 void OverloadDerived_delete(OverloadDerived* self) { + delete self; +} + +FFIGEN_EXPORT double OverloadDerived_OverloadBase_getValueDouble(OverloadDerived* self, double x) { + return static_cast(self)->getValueDouble(x); +} + +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 void PublicDerived_delete(PublicDerived* self) { + delete self; +} + +FFIGEN_EXPORT int PublicDerived_AccessBase_value(const PublicDerived* self) { + return static_cast(self)->value(); +} + +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 void Square_delete(Square* self) { + delete self; +} + +FFIGEN_EXPORT double Square_Shape_getY(const Square* self) { + return static_cast(self)->getY(); +} + +} 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 5dd1509bc8..1ec54bcd4c 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: - .* @@ -124,6 +125,7 @@ functions: - clang_isCursorDefinition - clang_CXXMethod_isConst - clang_CXXMethod_isStatic + - clang_getCXXAccessSpecifier - clang_getCursorAvailability - clang_getCursorPlatformAvailability - clang_disposeCXPlatformAvailability From 4e79ab9a7c880ed1ee3f5b5df02a0cc188608c2e Mon Sep 17 00:00:00 2001 From: Hassnaa Mohamed Date: Thu, 13 Aug 2026 07:40:18 +0300 Subject: [PATCH 2/5] Copy inherited C++ methods in visitor instead of delegating at codegen. --- .../lib/src/code_generator/cpp_class.dart | 164 ++------- .../sub_parsers/classdecl_parser.dart | 35 -- .../visitor/copy_methods_from_super_type.dart | 35 ++ .../native_cpp_test/cpp_inheritance_test.cpp | 2 + .../native_cpp_test/cpp_inheritance_test.dart | 3 + .../native_cpp_test/cpp_inheritance_test.h | 2 + .../cpp_inheritance_test_bindings.dart | 338 ++++++++++-------- .../cpp_inheritance_test_bindings.dart.cpp | 92 +++-- 8 files changed, 321 insertions(+), 350 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart index 0f081045bb..0e54cfd0ca 100644 --- a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart @@ -5,8 +5,6 @@ import '../code_generator.dart'; import '../config_provider/public_ast.dart' as public_ast; import '../context.dart'; -import '../header_parser/sub_parsers/classdecl_parser.dart' - show InheritedMethod, collectInheritedMethods, methodSignatureKey; import '../visitor/ast.dart'; import 'binding_string.dart'; @@ -26,6 +24,7 @@ class CppMethod extends AstNode with HasLocalScope { final bool isConstant; final bool isStatic; final CppMethodKind kind; + final String? originatingClass; CppMethod({ required this.name, @@ -35,10 +34,27 @@ 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.originalName, + ); + } + @override void visit(Visitation visitation) => visitation.visitCppMethod(this); @@ -105,18 +121,9 @@ class CppClass extends BindingType with HasLocalScope { required LocalVariables localVariables, }) => '$value._ptr'; - /// Returns the list of inherited methods from base classes that are not - /// overridden by this class. - List getInheritedMethodsToDelegate(Context ctx) { - if (bases.isEmpty) return const []; - final ownSignatures = methods - .map((m) => methodSignatureKey(m, ctx)) - .toSet(); - return collectInheritedMethods(this) - .where( - (im) => !ownSignatures.contains(methodSignatureKey(im.method, ctx)), - ) - .toList(); + void copyMethod(CppMethod method, CppClass originatingBase) { + final cloned = method.cloneForClass(this, originatingBase); + methods.add(cloned); } @override @@ -139,10 +146,10 @@ class CppClass extends BindingType with HasLocalScope { s.write(makeDartDoc(dartDoc)); // Build the implements clause: ffi.Finalizable + public base classes. - final baseNames = bases.map((b) => b.name).join(', '); - final implementsClause = bases.isEmpty - ? '$ffiPrefix.Finalizable' - : '$ffiPrefix.Finalizable, $baseNames'; + final implementsClause = [ + '$ffiPrefix.Finalizable', + ...bases.map((b) => b.name), + ].join(', '); s.write(''' class $name implements $implementsClause { $ptrVoid _ptr; @@ -331,48 +338,6 @@ class $name implements $implementsClause { } '''); - // Inherited method delegation (Dart side) - final inheritedToDelegate = getInheritedMethodsToDelegate(ctx); - for (final im in inheritedToDelegate) { - final method = im.method; - final base = im.baseClass; - final delegateSymbol = '${name}_${base.name}_${method.originalName}'; - final delegateGlue = '_$delegateSymbol'; - final dartReturn = method.returnType.getDartType(ctx); - final dartParams = dartParamList(method.parameters); - final localVars = LocalVariables(method.localScope); - final callArgs = [ - '_ptr', - ...method.parameters.map( - (p) => p.type.convertDartTypeToFfiDartType( - ctx, - p.name, - objCRetain: false, - objCAutorelease: false, - localVariables: localVars, - ), - ), - ].join(', '); - final decls = localVars.generateDeclarations(); - final returnExpr = method.returnType.convertFfiDartTypeToDartType( - ctx, - '$delegateGlue($callArgs)', - objCRetain: false, - ); - final hasReturn = method.returnType != voidType; - final callLine = hasReturn ? 'return $returnExpr;' : '$returnExpr;'; - s.write('''\ - @override - $dartReturn ${method.originalName}($dartParams) { - if (_ptr == $ffiPrefix.nullptr) { - throw StateError('This object has already been disposed.'); - } - $decls - $callLine - } -'''); - } - s.write('}\n'); // Writes a @Native annotation + external declaration for a glue function. @@ -433,33 +398,6 @@ class $name implements $implementsClause { ffiParams: '$ptrVoid self', ); - // @Native declarations for inherited-method delegation glue - for (final im in inheritedToDelegate) { - final method = im.method; - final base = im.baseClass; - final delegateSymbol = '${name}_${base.name}_${method.originalName}'; - final delegateGlue = '_$delegateSymbol'; - final cReturn = method.returnType.getCType(ctx); - final ffiReturn = method.returnType.getFfiDartType(ctx); - final cParams = [ - ptrVoid, // self (typed as derived) - ...method.parameters.map((p) => p.type.getCType(ctx)), - ].join(', '); - final ffiParams = [ - '$ptrVoid self', - ...method.parameters.map( - (p) => '${p.type.getFfiDartType(ctx)} ${p.name}', - ), - ].join(', '); - writeNativeDecl( - symbol: delegateSymbol, - glue: delegateGlue, - cType: '$cReturn Function($cParams)', - ffiReturn: ffiReturn, - ffiParams: ffiParams, - ); - } - return BindingString( type: BindingStringType.cppClass, string: s.toString(), @@ -501,9 +439,10 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { final otherParams = method.parameters.map(paramDecl); if (method.isStatic) { + final targetType = method.originatingClass ?? originalName; params = otherParams.join(', '); body = - '$returnPrefix$originalName::' + '$returnPrefix$targetType::' '${method.originalName}($callArgs);'; } else { final constPrefix = method.isConstant ? 'const ' : ''; @@ -513,7 +452,16 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { final suffix = method.returnType is CppUniquePtrType ? '.release()' : ''; - body = '${returnPrefix}self->$methodName($callArgs)$suffix;'; + if (method.originatingClass != null) { + final origClass = method.originatingClass; + final castTarget = + 'static_cast<$constPrefix$origClass*>(self)'; + body = + '$returnPrefix$castTarget' + '->$methodName($callArgs)$suffix;'; + } else { + body = '${returnPrefix}self->$methodName($callArgs)$suffix;'; + } } } @@ -524,44 +472,6 @@ FFIGEN_EXPORT $returnTypeString $symbol($params) { }) .join('\n\n'); - // Delegation stubs for inherited methods (C++ side) - final inheritedBindings = StringBuffer(); - final inheritedToDelegate = getInheritedMethodsToDelegate(context); - for (final im in inheritedToDelegate) { - final method = im.method; - final base = im.baseClass; - final delegateSymbol = - '${name}_${base.originalName}_${method.originalName}'; - final callArgs = method.parameters.map(_cppCallArg).join(', '); - - final nativeType = method.returnType.getNativeType(context); - final returnTypeString = nativeType.trim(); - final needsReturn = method.returnType != voidType; - final returnPrefix = needsReturn ? 'return ' : ''; - final suffix = method.returnType is CppUniquePtrType ? '.release()' : ''; - - final constPrefix = method.isConstant ? 'const ' : ''; - final selfType = '$constPrefix$originalName'; - final otherParams = method.parameters.map(paramDecl); - final params = ['$selfType* self', ...otherParams].join(', '); - - // static_cast adjusts the this-pointer offset for the base sub-object. - final castTarget = 'static_cast<$constPrefix${base.originalName}*>(self)'; - final body = - '$returnPrefix$castTarget' - '->${method.originalName}($callArgs)$suffix;'; - - inheritedBindings.write(''' - -FFIGEN_EXPORT $returnTypeString $delegateSymbol($params) { - $body -}'''); - } - - if (inheritedBindings.isNotEmpty) { - return '$methodBindings\n\n$deleteWrapper\n' - '${inheritedBindings.toString()}\n\n'; - } return '$methodBindings\n\n$deleteWrapper\n\n'; } 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 f9fc2e40be..7c7788f07a 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 @@ -122,41 +122,6 @@ String methodSignatureKey(CppMethod method, Context context) { return '${method.originalName}($paramTypes)$constSuffix'; } -List collectInheritedMethods(CppClass cls) { - final seen = {}; - final result = []; - for (final directBase in cls.bases) { - _collectFromBase(directBase, directBase, seen, result, cls.context); - } - return result; -} - -void _collectFromBase( - CppClass current, - CppClass directBase, - Set seen, - List result, - Context context, -) { - for (final base in current.bases) { - _collectFromBase(base, directBase, seen, result, context); - } - for (final method in current.methods) { - if (method.kind == CppMethodKind.constructor) continue; - final key = methodSignatureKey(method, context); - if (seen.add(key)) { - result.add(InheritedMethod(method: method, baseClass: directBase)); - } - } -} - -class InheritedMethod { - final CppMethod method; - final CppClass baseClass; - - const InheritedMethod({required this.method, required this.baseClass}); -} - 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..3eee628ae8 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 @@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. import '../code_generator.dart'; +import '../header_parser/sub_parsers/classdecl_parser.dart' + show methodSignatureKey; import 'ast.dart'; @@ -40,6 +42,39 @@ const _excludedNSObjectMethods = { }; class CopyMethodsFromSuperTypesVisitation extends Visitation { + @override + void visitCppClass(CppClass node) { + node.visitChildren(visitor); + + if (node.bases.isEmpty) return; + + final existingSignatures = node.methods + .map((m) => methodSignatureKey(m, node.context)) + .toSet(); + + for (final base in node.bases) { + _copyCppMethodsFromBase(node, base, existingSignatures); + } + } + + void _copyCppMethodsFromBase( + CppClass target, + CppClass base, + Set existingSignatures, + ) { + for (final method in base.methods) { + if (method.kind == CppMethodKind.constructor) continue; + final sigKey = methodSignatureKey(method, target.context); + if (existingSignatures.add(sigKey)) { + target.copyMethod(method, base); + } + } + + for (final grandBase in base.bases) { + _copyCppMethodsFromBase(target, grandBase, 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 index a7e26296a4..ef4d685b3f 100644 --- a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.cpp +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.cpp @@ -52,7 +52,9 @@ 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 index 5d002d01f2..cde64b07cb 100644 --- a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.dart +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.dart @@ -123,6 +123,9 @@ void main() { // 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 index 05f5424acb..2038999bf1 100644 --- a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h @@ -82,11 +82,13 @@ class DiamondBase { DiamondBase(); virtual ~DiamondBase(); int baseVal() const; + virtual int virtVal() const; }; class DiamondLeft : public DiamondBase { public: DiamondLeft(); + int virtVal() const override; }; class DiamondRight : public DiamondBase { 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 index 10d37ab595..8d465f547d 100644 --- a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test_bindings.dart @@ -242,6 +242,22 @@ class Circle implements ffi.Finalizable, Shape { 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) { @@ -261,24 +277,6 @@ class Circle implements ffi.Finalizable, Shape { _activeFinalizerFn = null; _ptr = ffi.nullptr; } - - @override - double getX() { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _Circle_Shape_getX(_ptr); - } - - @override - double getY() { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _Circle_Shape_getY(_ptr); - } } @ffi.Native Function(ffi.Double, ffi.Double, ffi.Double)>( @@ -289,18 +287,14 @@ 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_delete') -external void _Circle_delete(ffi.Pointer self); +@ffi.Native)>(symbol: 'Circle_getX') +external double _Circle_getX(ffi.Pointer self); -@ffi.Native)>( - symbol: 'Circle_Shape_getX', -) -external double _Circle_Shape_getX(ffi.Pointer self); +@ffi.Native)>(symbol: 'Circle_getY') +external double _Circle_getY(ffi.Pointer self); -@ffi.Native)>( - symbol: 'Circle_Shape_getY', -) -external double _Circle_Shape_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; @@ -410,60 +404,56 @@ class ColoredCircle implements ffi.Finalizable, Circle, Drawable { return _ColoredCircle_getColor(_ptr); } - @override - void dispose() { + double area() { 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; + + return _ColoredCircle_area(_ptr); } - @override double getX() { if (_ptr == ffi.nullptr) { throw StateError('This object has already been disposed.'); } - return _ColoredCircle_Circle_getX(_ptr); + return _ColoredCircle_getX(_ptr); } - @override double getY() { if (_ptr == ffi.nullptr) { throw StateError('This object has already been disposed.'); } - return _ColoredCircle_Circle_getY(_ptr); + return _ColoredCircle_getY(_ptr); } - @override - double area() { + int draw() { if (_ptr == ffi.nullptr) { throw StateError('This object has already been disposed.'); } - return _ColoredCircle_Circle_area(_ptr); + return _ColoredCircle_draw(_ptr); } @override - int draw() { + void dispose() { if (_ptr == ffi.nullptr) { throw StateError('This object has already been disposed.'); } - - return _ColoredCircle_Drawable_draw(_ptr); + 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; } } @@ -482,30 +472,30 @@ external ffi.Pointer _ColoredCircle_new( ) external int _ColoredCircle_getColor(ffi.Pointer self); -@ffi.Native)>( - symbol: 'ColoredCircle_delete', -) -external void _ColoredCircle_delete(ffi.Pointer self); - @ffi.Native)>( - symbol: 'ColoredCircle_Circle_getX', + symbol: 'ColoredCircle_area', ) -external double _ColoredCircle_Circle_getX(ffi.Pointer self); +external double _ColoredCircle_area(ffi.Pointer self); @ffi.Native)>( - symbol: 'ColoredCircle_Circle_getY', + symbol: 'ColoredCircle_getX', ) -external double _ColoredCircle_Circle_getY(ffi.Pointer self); +external double _ColoredCircle_getX(ffi.Pointer self); @ffi.Native)>( - symbol: 'ColoredCircle_Circle_area', + symbol: 'ColoredCircle_getY', ) -external double _ColoredCircle_Circle_area(ffi.Pointer self); +external double _ColoredCircle_getY(ffi.Pointer self); @ffi.Native)>( - symbol: 'ColoredCircle_Drawable_draw', + symbol: 'ColoredCircle_draw', +) +external int _ColoredCircle_draw(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'ColoredCircle_delete', ) -external int _ColoredCircle_Drawable_draw(ffi.Pointer self); +external void _ColoredCircle_delete(ffi.Pointer self); class DiamondBase implements ffi.Finalizable { ffi.Pointer _ptr; @@ -612,6 +602,14 @@ class DiamondBase implements ffi.Finalizable { 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.'); @@ -640,6 +638,11 @@ external ffi.Pointer _DiamondBase_new(); ) external int _DiamondBase_baseVal(ffi.Pointer self); +@ffi.Native)>( + symbol: 'DiamondBase_virtVal', +) +external int _DiamondBase_virtVal(ffi.Pointer self); + @ffi.Native)>( symbol: 'DiamondBase_delete', ) @@ -745,6 +748,22 @@ class DiamondDerived implements ffi.Finalizable, DiamondLeft, DiamondRight { 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) { @@ -764,29 +783,25 @@ class DiamondDerived implements ffi.Finalizable, DiamondLeft, DiamondRight { _activeFinalizerFn = null; _ptr = ffi.nullptr; } - - @override - int baseVal() { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _DiamondDerived_DiamondLeft_baseVal(_ptr); - } } @ffi.Native Function()>(symbol: 'DiamondDerived_new') external ffi.Pointer _DiamondDerived_new(); -@ffi.Native)>( - symbol: 'DiamondDerived_delete', +@ffi.Native)>( + symbol: 'DiamondDerived_virtVal', ) -external void _DiamondDerived_delete(ffi.Pointer self); +external int _DiamondDerived_virtVal(ffi.Pointer self); @ffi.Native)>( - symbol: 'DiamondDerived_DiamondLeft_baseVal', + symbol: 'DiamondDerived_baseVal', ) -external int _DiamondDerived_DiamondLeft_baseVal(ffi.Pointer self); +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; @@ -885,6 +900,22 @@ class DiamondLeft implements ffi.Finalizable, DiamondBase { 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) { @@ -904,29 +935,25 @@ class DiamondLeft implements ffi.Finalizable, DiamondBase { _activeFinalizerFn = null; _ptr = ffi.nullptr; } - - @override - int baseVal() { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _DiamondLeft_DiamondBase_baseVal(_ptr); - } } @ffi.Native Function()>(symbol: 'DiamondLeft_new') external ffi.Pointer _DiamondLeft_new(); -@ffi.Native)>( - symbol: 'DiamondLeft_delete', +@ffi.Native)>( + symbol: 'DiamondLeft_virtVal', ) -external void _DiamondLeft_delete(ffi.Pointer self); +external int _DiamondLeft_virtVal(ffi.Pointer self); @ffi.Native)>( - symbol: 'DiamondLeft_DiamondBase_baseVal', + symbol: 'DiamondLeft_baseVal', +) +external int _DiamondLeft_baseVal(ffi.Pointer self); + +@ffi.Native)>( + symbol: 'DiamondLeft_delete', ) -external int _DiamondLeft_DiamondBase_baseVal(ffi.Pointer self); +external void _DiamondLeft_delete(ffi.Pointer self); class DiamondRight implements ffi.Finalizable, DiamondBase { ffi.Pointer _ptr; @@ -1025,6 +1052,22 @@ class DiamondRight implements ffi.Finalizable, DiamondBase { 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) { @@ -1044,29 +1087,25 @@ class DiamondRight implements ffi.Finalizable, DiamondBase { _activeFinalizerFn = null; _ptr = ffi.nullptr; } - - @override - int baseVal() { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _DiamondRight_DiamondBase_baseVal(_ptr); - } } @ffi.Native Function()>(symbol: 'DiamondRight_new') external ffi.Pointer _DiamondRight_new(); -@ffi.Native)>( - symbol: 'DiamondRight_delete', +@ffi.Native)>( + symbol: 'DiamondRight_baseVal', ) -external void _DiamondRight_delete(ffi.Pointer self); +external int _DiamondRight_baseVal(ffi.Pointer self); @ffi.Native)>( - symbol: 'DiamondRight_DiamondBase_baseVal', + symbol: 'DiamondRight_virtVal', ) -external int _DiamondRight_DiamondBase_baseVal(ffi.Pointer self); +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; @@ -1464,6 +1503,14 @@ class OverloadDerived implements ffi.Finalizable, OverloadBase { 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) { @@ -1483,15 +1530,6 @@ class OverloadDerived implements ffi.Finalizable, OverloadBase { _activeFinalizerFn = null; _ptr = ffi.nullptr; } - - @override - double getValueDouble(double x) { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _OverloadDerived_OverloadBase_getValueDouble(_ptr, x); - } } @ffi.Native Function()>(symbol: 'OverloadDerived_new') @@ -1502,19 +1540,19 @@ external ffi.Pointer _OverloadDerived_new(); ) external int _OverloadDerived_getValue(ffi.Pointer self, int x); -@ffi.Native)>( - symbol: 'OverloadDerived_delete', -) -external void _OverloadDerived_delete(ffi.Pointer self); - @ffi.Native, ffi.Double)>( - symbol: 'OverloadDerived_OverloadBase_getValueDouble', + symbol: 'OverloadDerived_getValueDouble', ) -external double _OverloadDerived_OverloadBase_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( @@ -1868,6 +1906,14 @@ class PublicDerived implements ffi.Finalizable, AccessBase { 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) { @@ -1887,30 +1933,21 @@ class PublicDerived implements ffi.Finalizable, AccessBase { _activeFinalizerFn = null; _ptr = ffi.nullptr; } - - @override - int value() { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _PublicDerived_AccessBase_value(_ptr); - } } @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); -@ffi.Native)>( - symbol: 'PublicDerived_AccessBase_value', -) -external int _PublicDerived_AccessBase_value(ffi.Pointer self); - class Shape implements ffi.Finalizable { ffi.Pointer _ptr; static final _defaultFinalizer = ffi.NativeFinalizer( @@ -2171,6 +2208,14 @@ class Square implements ffi.Finalizable, Shape { 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) { @@ -2190,15 +2235,6 @@ class Square implements ffi.Finalizable, Shape { _activeFinalizerFn = null; _ptr = ffi.nullptr; } - - @override - double getY() { - if (_ptr == ffi.nullptr) { - throw StateError('This object has already been disposed.'); - } - - return _Square_Shape_getY(_ptr); - } } @ffi.Native Function(ffi.Double, ffi.Double, ffi.Double)>( @@ -2212,10 +2248,8 @@ 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); - -@ffi.Native)>( - symbol: 'Square_Shape_getY', -) -external double _Square_Shape_getY(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 index 174303386f..4479bfd333 100644 --- 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 @@ -25,17 +25,18 @@ FFIGEN_EXPORT double Circle_area(const Circle* self) { return self->area(); } -FFIGEN_EXPORT void Circle_delete(Circle* self) { - delete self; -} - -FFIGEN_EXPORT double Circle_Shape_getX(const Circle* self) { +FFIGEN_EXPORT double Circle_getX(const Circle* self) { return static_cast(self)->getX(); } -FFIGEN_EXPORT double Circle_Shape_getY(const Circle* self) { + +FFIGEN_EXPORT double Circle_getY(const Circle* self) { return static_cast(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); } @@ -44,23 +45,26 @@ FFIGEN_EXPORT int ColoredCircle_getColor(const ColoredCircle* self) { return self->getColor(); } -FFIGEN_EXPORT void ColoredCircle_delete(ColoredCircle* self) { - delete self; +FFIGEN_EXPORT double ColoredCircle_area(const ColoredCircle* self) { + return static_cast(self)->area(); } -FFIGEN_EXPORT double ColoredCircle_Circle_getX(const ColoredCircle* self) { +FFIGEN_EXPORT double ColoredCircle_getX(const ColoredCircle* self) { return static_cast(self)->getX(); } -FFIGEN_EXPORT double ColoredCircle_Circle_getY(const ColoredCircle* self) { + +FFIGEN_EXPORT double ColoredCircle_getY(const ColoredCircle* self) { return static_cast(self)->getY(); } -FFIGEN_EXPORT double ColoredCircle_Circle_area(const ColoredCircle* self) { - return static_cast(self)->area(); -} -FFIGEN_EXPORT int ColoredCircle_Drawable_draw(const ColoredCircle* self) { + +FFIGEN_EXPORT int ColoredCircle_draw(const ColoredCircle* self) { return static_cast(self)->draw(); } +FFIGEN_EXPORT void ColoredCircle_delete(ColoredCircle* self) { + delete self; +} + FFIGEN_EXPORT DiamondBase* DiamondBase_new() { return new DiamondBase(); } @@ -69,6 +73,10 @@ 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; } @@ -77,36 +85,48 @@ FFIGEN_EXPORT DiamondDerived* DiamondDerived_new() { return new DiamondDerived(); } -FFIGEN_EXPORT void DiamondDerived_delete(DiamondDerived* self) { - delete self; +FFIGEN_EXPORT int DiamondDerived_virtVal(const DiamondDerived* self) { + return static_cast(self)->virtVal(); } -FFIGEN_EXPORT int DiamondDerived_DiamondLeft_baseVal(const DiamondDerived* self) { +FFIGEN_EXPORT int DiamondDerived_baseVal(const DiamondDerived* self) { return static_cast(self)->baseVal(); } +FFIGEN_EXPORT void DiamondDerived_delete(DiamondDerived* self) { + delete self; +} + FFIGEN_EXPORT DiamondLeft* DiamondLeft_new() { return new DiamondLeft(); } -FFIGEN_EXPORT void DiamondLeft_delete(DiamondLeft* self) { - delete self; +FFIGEN_EXPORT int DiamondLeft_virtVal(const DiamondLeft* self) { + return self->virtVal(); } -FFIGEN_EXPORT int DiamondLeft_DiamondBase_baseVal(const DiamondLeft* self) { +FFIGEN_EXPORT int DiamondLeft_baseVal(const DiamondLeft* self) { return static_cast(self)->baseVal(); } +FFIGEN_EXPORT void DiamondLeft_delete(DiamondLeft* self) { + delete self; +} + FFIGEN_EXPORT DiamondRight* DiamondRight_new() { return new DiamondRight(); } -FFIGEN_EXPORT void DiamondRight_delete(DiamondRight* self) { - delete self; +FFIGEN_EXPORT int DiamondRight_baseVal(const DiamondRight* self) { + return static_cast(self)->baseVal(); } -FFIGEN_EXPORT int DiamondRight_DiamondBase_baseVal(const DiamondRight* self) { - return static_cast(self)->baseVal(); +FFIGEN_EXPORT int DiamondRight_virtVal(const DiamondRight* self) { + return static_cast(self)->virtVal(); +} + +FFIGEN_EXPORT void DiamondRight_delete(DiamondRight* self) { + delete self; } FFIGEN_EXPORT Drawable* Drawable_new() { @@ -145,12 +165,12 @@ FFIGEN_EXPORT int OverloadDerived_getValue(OverloadDerived* self, int x) { return self->getValue(x); } -FFIGEN_EXPORT void OverloadDerived_delete(OverloadDerived* self) { - delete self; +FFIGEN_EXPORT double OverloadDerived_getValueDouble(OverloadDerived* self, double x) { + return static_cast(self)->getValueDouble(x); } -FFIGEN_EXPORT double OverloadDerived_OverloadBase_getValueDouble(OverloadDerived* self, double x) { - return static_cast(self)->getValueDouble(x); +FFIGEN_EXPORT void OverloadDerived_delete(OverloadDerived* self) { + delete self; } FFIGEN_EXPORT PrivateDerived* PrivateDerived_new() { @@ -173,12 +193,12 @@ FFIGEN_EXPORT PublicDerived* PublicDerived_new() { return new PublicDerived(); } -FFIGEN_EXPORT void PublicDerived_delete(PublicDerived* self) { - delete self; +FFIGEN_EXPORT int PublicDerived_value(const PublicDerived* self) { + return static_cast(self)->value(); } -FFIGEN_EXPORT int PublicDerived_AccessBase_value(const PublicDerived* self) { - return static_cast(self)->value(); +FFIGEN_EXPORT void PublicDerived_delete(PublicDerived* self) { + delete self; } FFIGEN_EXPORT Shape* Shape_new(double x, double y) { @@ -209,12 +229,12 @@ FFIGEN_EXPORT double Square_area(const Square* self) { return self->area(); } -FFIGEN_EXPORT void Square_delete(Square* self) { - delete self; +FFIGEN_EXPORT double Square_getY(const Square* self) { + return static_cast(self)->getY(); } -FFIGEN_EXPORT double Square_Shape_getY(const Square* self) { - return static_cast(self)->getY(); +FFIGEN_EXPORT void Square_delete(Square* self) { + delete self; } } From 7c3a32541c54fed26a0bd5262e15bfb865ec1a01 Mon Sep 17 00:00:00 2001 From: Hassnaa Mohamed Date: Mon, 17 Aug 2026 02:51:13 +0300 Subject: [PATCH 3/5] fix analyze issues --- pkgs/ffigen/lib/src/code_generator/cpp_class.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart index 0e54cfd0ca..9a4e9c0bfd 100644 --- a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart @@ -454,8 +454,7 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { : ''; if (method.originatingClass != null) { final origClass = method.originatingClass; - final castTarget = - 'static_cast<$constPrefix$origClass*>(self)'; + final castTarget = 'static_cast<$constPrefix$origClass*>(self)'; body = '$returnPrefix$castTarget' '->$methodName($callArgs)$suffix;'; From a600f415ba33666a84c31f4424a078d4f1f58764 Mon Sep 17 00:00:00 2001 From: Hassnaa Mohamed Date: Mon, 17 Aug 2026 03:38:17 +0300 Subject: [PATCH 4/5] Refactor CppMethod originatingClass reference and signatureKey lookup. --- .../lib/src/code_generator/cpp_class.dart | 16 +++++++++---- .../sub_parsers/classdecl_parser.dart | 8 ------- .../visitor/copy_methods_from_super_type.dart | 24 +++++++------------ 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart index 9a4e9c0bfd..58a7362fe6 100644 --- a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart @@ -24,7 +24,7 @@ class CppMethod extends AstNode with HasLocalScope { final bool isConstant; final bool isStatic; final CppMethodKind kind; - final String? originatingClass; + final CppClass? originatingClass; CppMethod({ required this.name, @@ -51,10 +51,16 @@ class CppMethod extends AstNode with HasLocalScope { isConstant: isConstant, isStatic: isStatic, kind: kind, - originatingClass: baseClass.originalName, + 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); @@ -64,6 +70,7 @@ class CppMethod extends AstNode with HasLocalScope { visitor.visit(name); visitor.visit(returnType); visitor.visitAll(parameters); + visitor.visit(originatingClass); } } @@ -439,7 +446,8 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { final otherParams = method.parameters.map(paramDecl); if (method.isStatic) { - final targetType = method.originatingClass ?? originalName; + final targetType = + method.originatingClass?.originalName ?? originalName; params = otherParams.join(', '); body = '$returnPrefix$targetType::' @@ -453,7 +461,7 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { ? '.release()' : ''; if (method.originatingClass != null) { - final origClass = method.originatingClass; + final origClass = method.originatingClass!.originalName; final castTarget = 'static_cast<$constPrefix$origClass*>(self)'; body = '$returnPrefix$castTarget' 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 7c7788f07a..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 @@ -114,14 +114,6 @@ List _parsePublicBases(Context context, clang_types.CXCursor cursor) { return bases; } -String methodSignatureKey(CppMethod method, Context context) { - final paramTypes = method.parameters - .map((p) => p.type.getNativeType(context)) - .join(','); - final constSuffix = method.isConstant ? ' const' : ''; - return '${method.originalName}($paramTypes)$constSuffix'; -} - 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 3eee628ae8..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 @@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import '../code_generator.dart'; -import '../header_parser/sub_parsers/classdecl_parser.dart' - show methodSignatureKey; import 'ast.dart'; @@ -46,32 +44,28 @@ class CopyMethodsFromSuperTypesVisitation extends Visitation { void visitCppClass(CppClass node) { node.visitChildren(visitor); - if (node.bases.isEmpty) return; - final existingSignatures = node.methods - .map((m) => methodSignatureKey(m, node.context)) + .map((m) => m.signatureKey()) .toSet(); - for (final base in node.bases) { - _copyCppMethodsFromBase(node, base, existingSignatures); - } + _copyCppMethodsFromBase(node, node, existingSignatures); } void _copyCppMethodsFromBase( CppClass target, - CppClass base, + CppClass current, Set existingSignatures, ) { - for (final method in base.methods) { + for (final method in current.methods) { if (method.kind == CppMethodKind.constructor) continue; - final sigKey = methodSignatureKey(method, target.context); - if (existingSignatures.add(sigKey)) { - target.copyMethod(method, base); + + if (existingSignatures.add(method.signatureKey())) { + target.copyMethod(method, current); } } - for (final grandBase in base.bases) { - _copyCppMethodsFromBase(target, grandBase, existingSignatures); + for (final base in current.bases) { + _copyCppMethodsFromBase(target, base, existingSignatures); } } From 06d61f00aa84e3c92f009a7ce179e8f50732a5d4 Mon Sep 17 00:00:00 2001 From: Hassnaa Mohamed Date: Mon, 17 Aug 2026 04:08:21 +0300 Subject: [PATCH 5/5] Remove static_cast from C++ glue code and use virtual inheritance in tests. --- .../lib/src/code_generator/cpp_class.dart | 10 +------ .../native_cpp_test/cpp_inheritance_test.h | 4 +-- .../cpp_inheritance_test_bindings.dart.cpp | 28 +++++++++---------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart index 58a7362fe6..a086b56ef8 100644 --- a/pkgs/ffigen/lib/src/code_generator/cpp_class.dart +++ b/pkgs/ffigen/lib/src/code_generator/cpp_class.dart @@ -460,15 +460,7 @@ FFIGEN_EXPORT void ${name}_delete($originalName* self) { final suffix = method.returnType is CppUniquePtrType ? '.release()' : ''; - if (method.originatingClass != null) { - final origClass = method.originatingClass!.originalName; - final castTarget = 'static_cast<$constPrefix$origClass*>(self)'; - body = - '$returnPrefix$castTarget' - '->$methodName($callArgs)$suffix;'; - } else { - body = '${returnPrefix}self->$methodName($callArgs)$suffix;'; - } + body = '${returnPrefix}self->$methodName($callArgs)$suffix;'; } } diff --git a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h index 2038999bf1..2a40f171e7 100644 --- a/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h +++ b/pkgs/ffigen/test/native_cpp_test/cpp_inheritance_test.h @@ -85,13 +85,13 @@ class DiamondBase { virtual int virtVal() const; }; -class DiamondLeft : public DiamondBase { +class DiamondLeft : virtual public DiamondBase { public: DiamondLeft(); int virtVal() const override; }; -class DiamondRight : public DiamondBase { +class DiamondRight : virtual public DiamondBase { public: DiamondRight(); }; 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 index 4479bfd333..6280090e40 100644 --- 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 @@ -26,11 +26,11 @@ FFIGEN_EXPORT double Circle_area(const Circle* self) { } FFIGEN_EXPORT double Circle_getX(const Circle* self) { - return static_cast(self)->getX(); + return self->getX(); } FFIGEN_EXPORT double Circle_getY(const Circle* self) { - return static_cast(self)->getY(); + return self->getY(); } FFIGEN_EXPORT void Circle_delete(Circle* self) { @@ -46,19 +46,19 @@ FFIGEN_EXPORT int ColoredCircle_getColor(const ColoredCircle* self) { } FFIGEN_EXPORT double ColoredCircle_area(const ColoredCircle* self) { - return static_cast(self)->area(); + return self->area(); } FFIGEN_EXPORT double ColoredCircle_getX(const ColoredCircle* self) { - return static_cast(self)->getX(); + return self->getX(); } FFIGEN_EXPORT double ColoredCircle_getY(const ColoredCircle* self) { - return static_cast(self)->getY(); + return self->getY(); } FFIGEN_EXPORT int ColoredCircle_draw(const ColoredCircle* self) { - return static_cast(self)->draw(); + return self->draw(); } FFIGEN_EXPORT void ColoredCircle_delete(ColoredCircle* self) { @@ -86,11 +86,11 @@ FFIGEN_EXPORT DiamondDerived* DiamondDerived_new() { } FFIGEN_EXPORT int DiamondDerived_virtVal(const DiamondDerived* self) { - return static_cast(self)->virtVal(); + return self->virtVal(); } FFIGEN_EXPORT int DiamondDerived_baseVal(const DiamondDerived* self) { - return static_cast(self)->baseVal(); + return self->baseVal(); } FFIGEN_EXPORT void DiamondDerived_delete(DiamondDerived* self) { @@ -106,7 +106,7 @@ FFIGEN_EXPORT int DiamondLeft_virtVal(const DiamondLeft* self) { } FFIGEN_EXPORT int DiamondLeft_baseVal(const DiamondLeft* self) { - return static_cast(self)->baseVal(); + return self->baseVal(); } FFIGEN_EXPORT void DiamondLeft_delete(DiamondLeft* self) { @@ -118,11 +118,11 @@ FFIGEN_EXPORT DiamondRight* DiamondRight_new() { } FFIGEN_EXPORT int DiamondRight_baseVal(const DiamondRight* self) { - return static_cast(self)->baseVal(); + return self->baseVal(); } FFIGEN_EXPORT int DiamondRight_virtVal(const DiamondRight* self) { - return static_cast(self)->virtVal(); + return self->virtVal(); } FFIGEN_EXPORT void DiamondRight_delete(DiamondRight* self) { @@ -166,7 +166,7 @@ FFIGEN_EXPORT int OverloadDerived_getValue(OverloadDerived* self, int x) { } FFIGEN_EXPORT double OverloadDerived_getValueDouble(OverloadDerived* self, double x) { - return static_cast(self)->getValueDouble(x); + return self->getValueDouble(x); } FFIGEN_EXPORT void OverloadDerived_delete(OverloadDerived* self) { @@ -194,7 +194,7 @@ FFIGEN_EXPORT PublicDerived* PublicDerived_new() { } FFIGEN_EXPORT int PublicDerived_value(const PublicDerived* self) { - return static_cast(self)->value(); + return self->value(); } FFIGEN_EXPORT void PublicDerived_delete(PublicDerived* self) { @@ -230,7 +230,7 @@ FFIGEN_EXPORT double Square_area(const Square* self) { } FFIGEN_EXPORT double Square_getY(const Square* self) { - return static_cast(self)->getY(); + return self->getY(); } FFIGEN_EXPORT void Square_delete(Square* self) {