dart-lang / native

Dart packages related to FFI and native assets bundling.
BSD 3-Clause "New" or "Revised" License
82 stars 27 forks source link

C header includes C++ Header #1089

Closed Matthew-w56 closed 2 days ago

Matthew-w56 commented 1 month ago

I am attempting to use the Dart FFIGen to generate a binding for a library written in C++, with C-compatible headers. When I run the FFIGen, I get the following errors:

`PS C:\Users\mbwil\Documents\Programming\Flutter\guido_integration> dart run ffigen -v fine

[INFO] : Running in Directory: 'C:\Users\mbwil\Documents\Programming\Flutter\guido_integration'

[FINE] : Adding header/file: C:\Users\mbwil\Desktop\guidolib\src\engine\include\GUIDOEngine.h

[FINE] : CompilerOpts used: []

[INFO] : Input Headers: [C:\Users\mbwil\Desktop\guidolib\src\engine\include\GUIDOEngine.h]

[FINE] : Creating TranslationUnit for header: C:\Users\mbwil\Desktop\guidolib\src\engine\include\GUIDOEngine.h

[SEVERE] : Header C:\Users\mbwil\Desktop\guidolib\src\engine\include\GUIDOEngine.h: Total errors/warnings: 20.

[SEVERE] : C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.39.33519\include\yvals_core.h:28:2: error: Error in C++ Standard Library usage [User-Defined Issue]

[SEVERE] : C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.39.33519\include\yvals.h:334:1: error: unknown type name 'namespace' [Semantic Issue]

[SEVERE] : C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.39.33519\include\yvals.h:334:1: error: expected ';' after top level declarator [Parse Issue]

[SEVERE] : C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.39.33519\include\cstdlib:22:62: error: expected function body after function declarator [Parse Issue]

[SEVERE] : C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\ucrt\corecrt_wstdio.h:34:14: error: unknown type name 'FILE' [Semantic Issue]`

The second error stands out to me as the reason for the cascade of issues. Specifically, the build tools in the visual studio folder are C++ headers, and use the namespace keyword. This causes the tool to fail because C doesn't support this. So what is there to be done to avoid this collision?

Is there something in the internals that needs to be able to deal with C++ headers being depended on within the build tools, or am I missing a way around this problem?

Extra Information:

Thanks for any help!

dcharkes commented 1 month ago

Just googling around:

https://github.com/microsoft/STL/blob/main/stl/inc/yvals.h#L334C1-L334C11

_STD_BEGIN // equal to namespace mynamespace {

https://stackoverflow.com/questions/41536696/how-where-c-namespace-std-is-defined-link-to-documentation-standard

C doesn't have namespaces.

So the headers are at least not transitively compatible with C. Maybe it will succeed if you set a filter on what bindings are generated to not generate for the MSVC and Windows includes.

headers:
  entry-points:
    - ...
  include-directives:
    - 'C:\Users\mbwil\Desktop\guidolib\src\engine\include\**'
Matthew-w56 commented 1 month ago

Is there a way to filter like that? This is my configuration and it seems to include what it needs to the in the project without explicitly including those c++ headers.

ffigen: output: 'lib/guido/generated_bindings.dart' name: 'GuidoWrapper' description: 'Guido C Library wrapper for Dart' ignore-source-errors: true functions: expose-typedefs: include:

dcharkes commented 1 month ago

I'm not familiar with the Windows headers here.

I'd expect the _STD_BEGIN macro to be defined as empty for C compatibility. If the _STD_BEGIN is unconditionally defined as a namespace, it is not C compatible.

Googling some more: https://github.com/microsoft/STL This is the C++ standard library. So it's unsurprising it's incompatible with C. Are you sure what's in your include-directives is C compatible only? Maybe try removing things from your include-directives to generate less and try to only generate for the single header file?

ignore-source-errors: true

You might want to remove this to triage errors.

Matthew-w56 commented 1 week ago

So I've been working on it and it is still giving me problems. I cut down to absolutely no includes, and just the entry point, and it still somehow thinks it has to compile the c++ headers that are indirectly linked to. Is there anything that can be done about this? I'm just not aware of what can be done here. Here is my section in the pubspec.yaml:

ffigen: output: 'lib/guido/generated_bindings.dart' name: 'GuidoWrapper' language: c description: 'Guido C Library wrapper for Dart' headers: entry-points:

Edit: Where my confusion comes in is that I used this same tool (dart ffigen) to generate bindings for a different c++ library with a C header, and it didn't give me problems. So what could be causing the tool to think it has to go and compile the non-C headers?

dcharkes commented 6 days ago

Where my confusion comes in is that I used this same tool (dart ffigen) to generate bindings for a different c++ library with a C header, and it didn't give me problems. So what could be causing the tool to think it has to go and compile the non-C headers?

This seems to be the right question to ask.

I cut down to absolutely no includes, and just the entry point, and it still somehow thinks it has to compile the c++ headers that are indirectly linked to.

I guess the next step is to make a copy of C:/Users/mbwil/Desktop/guidolib/src/engine/include/GUIDOEngine.h and start removing stuff from that until it works. If it's an empty file with a single function from your other project it should work.

I'm sorry I can't provide much help here. I'm curious what you'll find!

Matthew-w56 commented 2 days ago

Okay I got it resolved. For any who find this thread with the same or similar issue:

The FFIGen tool attempts to generate the bindings for everything needed for any entry points you give it. So, if your header file makes imports to the rest of your program, it will pull those in, too. This lead to a chain of imports for me, which was a problem because the library itself is written in C++, but all the headers themselves are C-compatible. So, for me, the easiest solution was to create a new header and .cpp file, and create simple driver methods for what I needed to access through the API. The .cpp file was able to import whatever it wanted, as long as the .h file didn't import anything from the rest of the project (or from most of the C++ standard library). Instead, structs and library-defined objects were referred to as void* in the header file, and casted to their appropriate types in the .cpp file.

This way, the FFIGen tool can create bindings for my custom header file (which I got built into my dll), and I can interact with the library through those defined methods.

Thanks all for the suggestions and feedback!