5 ms·
The issue was in the beginning they didn’t think interop with C/C++ was value add. People complained and they added “Managed C++” which unfortunately influenced
by reactordev 1y ago
The issue was in the beginning they didn’t think interop with C/C++ was value add. People complained and they added “Managed C++” which unfortunately influenced C++03’s design a lot. It wasn’t until C++11 that Microsoft gave up. You couldn’t effectively interop with C++ without writing a managed C++ wrapper, which only worked on windows. They added support for P/Invoke to aid in Win32 calls (shell.dll, user32.dll) as a simple fix and we went nuts for it. Wrote wrappers using P/Invoke and a config map nightmare of which dll’s, or dylib’s, or so’s, you needed to get the form to show.
Fast forward to today…
Rust can interop with C natively. Go can as well, though you’re bringing your luggage with you with CGO. .Net hasn’t ever really had that kind of focus. For one, IL, two, Microsoft saw the platform as a cash cow, three, ecosystem lock in allowed a thriving “MVP” contractor community.
- lowleveldesign 1y agoInterestingly, the Rust windows crate is generated from an MSIL assembly. And same metadata might be used to generate C# bindings thanks to cswin32 [1] project. The meta-assembly generation (Win32 metadata project) is based on clangsharp and it's fairly straightforward to generate interop code for native Windows libraries. Some time ago I described this process on my blog for the detours library [2] [1] https://github.com/microsoft/CsWin32 https://github.com/microsoft/CsWin32 [2] https://lowleveldesign.org/2023/11/23/generating-c-bindings-for-native-windows-libraries/ https://lowleveldesign.org/2023/11/23/generating-c-bindings-...
- reactordev 1y agoThe point is why is another language generating anything for C#?
- contextfree 1y agoYes, and this approach is an extension of the one taken with WinRT metadata which is also ".NET assembly" based.
- pjmlp 1y agoYou are jumping over a few facts there. P/Invoke was born as J/Direct on J++, it became P/Invoke after the lawsuit, and Cool project turned into C#. Managed C++ Extensions in .NET 1.0 got replaced by C++/CLI on .NET 2.0, it was a .NET Core 3.1 milestone to support it, and has recently been updated up to C++20, minus modules. Still heavily used among .NET community on Windows. Meanwhile the native C++/CX and C++/WinRT, both failed their adoption efforts.
- malkia 1y agoI only wish if C++/CLI worked on other platforms...
- pjmlp 1y agoSame here, for those of us comfortable on C++ land, it is much easier way to do integration with native libraries, than getting P/Invoke right.
- reactordev 1y agoMy memory is fragmented in my older age.
- jaydcarlson 1y agoYou'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).
- reactordev 1y agoThe 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.