Comment by jaydcarlson
6 days ago
You're clobbering together a bunch of different stuff and not making a ton of sense. C and C++ are very different languages, and that's especially true when doing interop with them from other languages.
For C-based libraries, P/invoking is trivial in C# and has been around forever. And it's cross-platform, working identically on Linux and macOS. I have no idea how you can say ".Net hasn’t ever really had that kind of focus" when it's been a core part of .NET from the start, and .NET relies on P/Invoke to do everything. Go look at all the DllImport() statements in the .NET reference source. Rust FFI is nearly identical in implementation to C#. Go has a slightly different implementation with the CGO module, but whatever, it's close enough. Just step back and remember that, in general, calling into C code is trivial in every language, since it has to be: all these languages will eventually have to hit libc / user32.dll / whatever.
C++ is a totally different story. You can't work with C++ libraries with P/Invoke, that's true... But you also can't work with C++ libraries using Rust or Go, either. Nor Python, Java, Javascript, or really any other popular cross-platform language.
C++ dynamic libraries are really challenging to call into for a variety of reasons. Virtual functions, multiple inheritance, RTTI, name-mangling, struct/class layout, vtable placement, ABI differences, runtimes, etc all make calling into precompiled C++ libraries a nightmare.
In fact, the only way I know of working with pre-compiled C++ libraries is with languages that target specific operating system / compiler collections. E.g., Objective-C++/Swift from Apple, and C++/CLI from Microsoft. These are obviously not cross-platform solutions, since they depend on knowing the exact compiler configuration used to build those libraries in the first place.
For every other language, you either need to manually build C shim libs that you can call into using the C-based approach above, or if you have access to the C++ source code, creating wrappers around it and building it into a module (for example, using pybind11 in Python).
The only reason is name mangling. You can disable this or export declspec it and keep the C-like signature. Painstakingly recreate the API in C#, using P/Invoke, and hope for the best. It wasn’t until late 2015 that we got codegen to “automate” this, or roll your own.
My perspective is from a first adopter, not an insider, 24 years ago, so I can’t speak to motive but as a customer, it felt exactly as I described. The documentation around P/Invoke was lax, you were shoved “Managed C++” down your throat by your rep, and any dream of going cross platform died in that meeting room until Miguel De Icaza did something about it.
It certainly is not, it is quite common to use templates directly or indirectly via the C++ standard library, and P/Invoke cannot handle those.
Also during .NET 1.0 days, Microsoft had a Website where you could paste any well known Win32 API or related SDK, and it would spit the annotations.
What was never as good in .NET as it was in VB 6 and still is in Delphi and C++ Builder to this day, was creating and consuming COM, which is kind of annonying given how much Windows team loves it.
At least it isn't as bad as the multiple reboots in C++, which I will keep asserting that from all of those, MFC still has the best tooling to handle COM.