Skip to content

[ffigen] Functions using pointers to a C++ class are skipped unless cpp: class support is configured #3592

Closed

Description

@jakobkordez

Summary

When a header is parsed as C++ (-x c++ in compilerOptions) but cpp: class support is not configured, any function whose signature mentions a pointer to a class is dropped from the bindings:

[WARNING] Skipped Function 'my_handle_create', function has unsupported return type or parameter type.

A pointer to a class is exactly as expressible as a pointer to a struct — it should be emitted as an Opaque, which is what already happens for struct OtherHandle;. Only the keyword differs.

Reproduction

src/handle.h:

class MyHandle;          // forward-declared, used only through pointers
struct OtherHandle;      // same thing, spelled `struct`

class DefinedHandle {    // defined in the TU, still only used through pointers
 public:
  virtual ~DefinedHandle() = default;
  virtual int value() const = 0;
 private:
  int state_;
};

MyHandle* my_handle_create(void);
void my_handle_destroy(MyHandle* h);
OtherHandle* other_handle_create(void);
DefinedHandle* defined_handle_create(void);

(In a real header these would sit in an extern "C" block; that is left out here because extern "C" is currently ignored entirely, which is a separate issue)

tool/ffigen.dart:

import 'dart:io';

import 'package:ffigen/ffigen.dart';

Future<void> main() async {
  final root = Platform.script.resolve('../');
  await FfiGenerator(
    output: Output(dart: DartOutput(path: root.resolve('lib/bindings.dart'))),
    input: Input(
      entryPoints: [root.resolve('src/handle.h')],
      compilerOptions: ['-x', 'c++', '-std=c++17'],
    ),
    // No `cpp:` configured.
    visitors: [
      Visitor(
        func: (node) => node.isIncluded = {
          'my_handle_create',
          'my_handle_destroy',
          'other_handle_create',
          'defined_handle_create',
        }.contains(node.originalName),
      ),
    ],
  ).generate();
}

Actual (main @ f914da8):

[WARNING] Skipped Function 'my_handle_create', function has unsupported return type or parameter type.
[WARNING] Skipped Function 'my_handle_destroy', function has unsupported return type or parameter type.
[WARNING] Skipped Function 'defined_handle_create', function has unsupported return type or parameter type.
@ffi.Native<ffi.Pointer<OtherHandle> Function()>()
external ffi.Pointer<OtherHandle> other_handle_create();

final class OtherHandle extends ffi.Opaque {}

Expected: all four functions are generated, with MyHandle and DefinedHandle emitted as ffi.Opaque just like OtherHandle.

Cause

In _extractfromRecord (pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart), a CXCursor_ClassDecl is only handled by the C++ class parser, which is a no-op when config.cpp == null (and also returns null for anonymous or unavailable classes). Whatever it declines falls through to the UnimplementedType default instead of the StructDecl/UnionDecl fallback, so the enclosing function is removed by parseFunctionDeclaration.

Treating CXCursor_ClassDecl the same as CXCursor_StructDecl in that fallback fixes it: the class goes through importType / parseStructDeclaration and, since it is only ever referenced through pointers, ends up as an Opaque.

Activity

  1. added
    lang-cppExperimental C++ support
    and removed
    needs-triageNeeds triage from the team. Remove this label once triaged.
    on Aug 27, 2026
  2. liamappelbe commented on Aug 27, 2026

    @liamappelbe
    Contributor

    Yeah the class vs struct keyword is irrelevant. We discussed deciding whether something was a struct or a class based on clang_isPODType instead (then we can do the config.cpp check only for non-pod types). Not sure if that check was implemented or not.

    cc @Hassnaa9

  3. jakobkordez commented on Sep 1, 2026

    @jakobkordez
    ContributorAuthor

    Seems reasonable. I checked the code and the check is not implemented yet.
    Let me know if you want me to open a PR for either the POD check or the opaque fix. I have them both on my repo.
    Not sure if GSoC is over or not...

  4. liamappelbe commented on Sep 1, 2026

    @liamappelbe
    Contributor

    GSoC is officially over, but Hassnaa is continuing to contribute. I don't think she's planning to work on this bug any time soon though, so feel free to send a PR.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions