4 ms·
> If your stacks are the size of C stacks, and if your language doesn't feature a pervasive runtime, then interoperability with C is nearly trivial in .NET sta
by Const-me 7y ago
> If your stacks are the size of C stacks, and if your language doesn't feature a pervasive runtime, then interoperability with C is nearly trivial
in .NET stacks are different, the runtime is relatively large, yet interoperability with C is nearly trivial. From my PoV at least, I never worked on compilers, only used them a lot.
- bryanlarsen 7y agoIt's easy to call C from .NET, but is it easy to call .NET from C? In a cross-platform manner?
- Const-me 7y agoYes, and yes. Declare a delegate in C#, apply [UnmanagedFunctionPointer] attribute specifying calling convention (on Linux you’ll likely want CallingConvention.Cdecl), pass to native code specifying [MarshalAs( UnmanagedType.FunctionPtr )] in the prototype of the DLL/SO function which accepts that pointer. This is even more convenient than doing that in C. In C, function pointers are stateless, typical design pattern is pass accompanying `void* context` for the state. Not needed in C#, that delegate can capture whatever it wants, the runtime will do it’s marshalling magic, generating a native function and somehow associating it with the captured state. The only caveat is lifetime. Function pointers can’t be retained by native code, they’re too simple and don’t have a ref.counter. You must ensure the .NET delegate is not garbage collected while it’s referenced by the native code, or the app will crash.
- kibwen 7y agoInteresting, though note that when I say "trivial" I'm not referring to the amount of effort it takes on behalf of the programmer using the feature, but the amount of work that needs to be done (and hence CPU time that needs to be spent) when crossing the boundary from one to another and back again (which is often invisible in the source and implicitly inserted by the compiler/runtime). In Rust there is no cost to traversing this boundary (the only hypothetical cost being that inlining is more difficult (though still technically possible, miraculously) across language boundaries).
- Const-me 7y ago> the amount of work that needs to be done (and hence CPU time that needs to be spent) when crossing the boundary from one to another and back again That amount is really small for .NET. See this doc https://docs.microsoft.com/en-us/cpp/dotnet/calling-native-functions-from-managed-code?view=vs-2019 https://docs.microsoft.com/en-us/cpp/dotnet/calling-native-f... it says “PInvoke has an overhead of between 10 and 30 x86 instructions per call.” That’s barely measurable. Close to the cost of a mispredicted branch. 4x cheaper than a single cache miss. That’s if you do it correctly, i.e. only marshal value types or arrays/pointers of them, don’t use custom marshallers, etc. Also it’s important to specify correct attributes. To call `int func(const sStruct&)` C++ function, you should specify `[In] ref sStruct` in C#.