Skip to content

[ffigen] Add C++ public inheritance support (single, multiple, diamond) - #3542

Open
Hassnaa9 wants to merge 3 commits into
dart-lang:mainfrom
Hassnaa9:cpp-inheritance
Open

[ffigen] Add C++ public inheritance support (single, multiple, diamond)#3542
Hassnaa9 wants to merge 3 commits into
dart-lang:mainfrom
Hassnaa9:cpp-inheritance

Conversation

@Hassnaa9

Copy link
Copy Markdown
Contributor

No description provided.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if you combine '$ffiPrefix.Finalizable' into a list literal with the iterable returned by bases.map, then you can just rely on the .join and don't need this conditional.

''');

// Inherited method delegation (Dart side)
final inheritedToDelegate = getInheritedMethodsToDelegate(ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than have this huge amount of duplicated code, just add the super type's methods to this class's methods. That way there's nothing special about these methods at all.

It probably makes sense to do that in lib/src/visitor/copy_methods_from_super_type.dart. When copying across the method, make an actual copy of it, like we do for ObjC methods.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Could you check if this is what you had in mind, or if there are any further improvements needed?

public:
DiamondBase();
virtual ~DiamondBase();
int baseVal() const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a virtual method test?

@Hassnaa9
Hassnaa9 requested a review from liamappelbe August 13, 2026 04:42

@liamappelbe liamappelbe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there's some formatting errors on CI. The bot is using the most recent stable Dart, which was just released (and makes some small changes to the formatter), so make sure you update to the latest version.

final bool isConstant;
final bool isStatic;
final CppMethodKind kind;
final String? originatingClass;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to store a reference to the CppClass object.

return bases;
}

String methodSignatureKey(CppMethod method, Context context) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a method on CppMethod.

CppMethod cloneForClass(CppClass targetClass, CppClass baseClass) {
return CppMethod(
name: Symbol(
'${targetClass.originalName}_$originalName',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was pretty confused about this name mangling until I realised that you're using originalName instead of name when generating the Dart method. That means that users won't be able to rename methods, and the name collision resolution logic won't work.

You don't need to fix that in this PR, but I filed a bug so we don't forget: #3552

if (method.originatingClass != null) {
final origClass = method.originatingClass;
final castTarget =
'static_cast<$constPrefix$origClass*>(self)';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the static_cast necessary?


String methodSignatureKey(CppMethod method, Context context) {
final paramTypes = method.parameters
.map((p) => p.type.getNativeType(context))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getNativeType is only supposed to be used at codegen time. In general, these getters that are used in codegen may refer to things that are filled in during the transformation stage (all those visitors in the visitor dir), such as a Symbol's name. That would cause an NPE or assertion failure.

It's the kind of bug that you only catch if you have very good test coverage. Eg, writing a C++ class with a method for every possible return type and arg type (primitives, structs, unions, classes, function pointers etc etc etc).

You should use cacheKey instead. Let me know if that method gives you problems. I have a bug I've been meaning to fix to improve it.

.map((m) => methodSignatureKey(m, node.context))
.toSet();

for (final base in node.bases) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dedupe this for loop with the for (final grandBase in base.bases) { loop below. You can refactor _copyCppMethodsFromBase so you only need one .bases loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants