简体   繁体   中英

ANSI C as core of a C# project? Is this possible?

I'm writing a NON-GUI app which I want to be cross platform between OS X and Windows. I'm looking at the following architecture, but I don't know if it will work on the windows side:

(Platform specific entry point) -> ANSI C main loop => ANSI C model code doing data processing / logic => (Platform specific helpers)

So the core stuff I'm planning to write in regular ANSI C, because A) it should be platform independent, B) I'm extremely comfortable with C, C) It can do the job and do it well

(Platform specific entry point) can be written in whatever necessary to get the job done, this is a small amount of code, doesn't matter to me.

(Platform specific helpers) is the sticky thing. This is stuff like parsing XML, accessing databases, graphics toolkit stuff, whatever. Things that aren't easy in C. Things that modern languages/frameworks will give for free. On OS X this code will be written in Objective-C interfacing with Cocoa. On Windows I'm thinking my best bet is to use C#

So on Windows my architecture (simplified) looks like

(C# or C?) -> ANSI C -> C#

Is this possible? Some thoughts/suggestions so far..

1) Compile my C core as a .dll -- this is fine, but seems there's no way to call my C# helpers unless I can somehow get function pointers and pass them to my core, but that seems unlikely

2) Compile a C .exe and a C# .exe and have them talk via shared memory or some kind of IPC. I'm not entirely opposed to this but it obviously introduces a lot of complexity so it doesn't seem ideal

3) Instead of C# use C++, it gets me some nice data management stuff and nice helper code. And I can mix it pretty easily. And the work I do could probably easily port to Linux. But I really don't like C++, and I don't want this to turn in to a 3rd-party-library-fest. Not that it's a huge deal, but it's 2010.. anything for basic data management should be built in. And targetting Linux is really not a priority.

Note that no "total" alternatives are OK as suggested in other similar questions on SO I've seen; java, RealBasic, mono.. this is an extremely performance intensive application doing soft realtime for game/simulation purposes, I need C & friends here to do it right (maybe you don't, but I do)

Short answer: Yes. You can access non-managed code quite easily from .NET, but you'll have to marshal your data.

Long answer: Don't do it. You know about the mono project ? It's a x-platform implementation of .NET. They're a bit behind Microsoft, but they still offer a solution that works for many. I'd highly suggest keeping in managed code if at all possible, you're defeating the purpose of .NET if you're in-and-outing to C code. This will also help reduce your project complexity tenfold.

If you require C ANSI for low-level access, I suggest you expose a small & well-tested C ANSI api to your .NET core to do any low-level stuff.

C# Core <==> Small C ANSI Helper Library

Why would you not do this all in c++? You can hit all your platforms and get all the stuff that you say C does not have. C# is only for dot net stuff, c/c++ still works on windows, just get the api call you need for whatever you are trying to do.

I like Aren's answer (look at Mono), but if you really want to use C + C#, you can use SWIG to produce wrappers of C code somewhat automatically. It's got a learning curve, but if the number of C functions you want to call from C# is large enough, it's worth the effort. SWIG doesn't support Objective C out-of-box, but there is a branch with not-quite-finished support for it.

Update: oh, you want to primarily call C# from C? Sorry, SWIG isn't really designed for that. C# does allow it, however. You can use either a C# or C/C++ entry point (a C# entry point is probably easier), and you can pass pointers to C# functions (delegates) into C code.

Let's say you want to pass a void(string) function from C# to C. First off, I don't know how C code can directly get pointers to C# functions (it may be possible, I just don't know how.) Instead, I would start the program in C# code and have the C# code pass itself to the C code.

Something like this:

// Visual C code:
// (.NET functions use the __stdcall calling convention by default.)
typedef void (__stdcall *Callback)(PCWSTR);
void __declspec(dllexport) Foo(Callback c)
{
    c(L"Hello world");
}

// C# code:
// (A delegate declaration can include marshaling commands that
// control how argument types are converted.)
public delegate void Callback([MarshalAs(UnmanagedType.LPWStr)] string message);
void PrintOut(string message) { Console.WriteLine(message); }

Here we have a C function "Foo" that can receive a pointer to the C# function "PrintOut" (or a C function for that matter), plus a Callback typedef. We use __declspec(dllexport) so that C# code can call it.

On the C# side we have a delegate declaration, which is roughly analogous to the typedef in C, and a function "PrintOut" that we want to pass to C.

Assuming you compile your C code into a DLL called Foo.dll, you'll need a C# P/Invoke declaration, and code that actually calls Foo:

[DllImport("Foo")]
public static extern void Foo(Callback c);

public void CallFoo()
{
    Foo(PrintOut);
}

The above will probably work at first, but there is one "gotcha": the above code wraps PrintOut in a delegate, and the garbage collector will free the delegate eventually unless you keep a reference to it. So if you want the C code to have a permanent reference to the C# method, we must change the above C# code to keep a reference to the delegate:

[DllImport("Foo")]
public static extern void Foo(Callback c);

static Callback PrintOutRef; // to prevent garbage collection of the delegate

public void CallFoo()
{
    Foo(PrintOutRef = PrintOut);
}

Hope that helps!

First of all, to answer one specific worry: you can marshal delegates as function pointers to your native C code - in fact, it "just works", and P/Invoke will take care of all the wrapping:

// C#
class ManagedEntryPoint {

    [DllImport("core", CallingConvention=CallingConvention.Cdecl)]
    static extern void NativeEntryPoint(Func<int, int, float> helper);

    static float Helper(int, int) { ... }

    static void Main() {
        NativeEntryPoint(Helper);
    }
}

// C
void NativeEntryPoint(float (*helper)(int, int)) {
    float x = helper(1, 2);
    ...
}

However, I don't see much point in this - it's easier to use C++/CLI compiler. Note that this doesn't mean that you actually have to use C++ - you can stick to the subset of C which is C++-compatible, which is 95% of it (I'd expect about the only thing you'll need to do differently in practice is explicitly casting the return of malloc ). You compile your native C functions as a native .lib, and then link that to an executable compiled with /clr . C++/CLI will take care of all the marshaling itself, and you won't need to write P/Invoke declarations and such.

There's no reason at all to use ANSI C over C++. In addition, if you have C++ code, writing a C++/CLI wrapper for use in C# is very trivial (as far as I know) and C++ won't add two floats any slower than C will (many other operations are actually faster, like sorting). In addition to that, C++ is pretty flexible as to what you can do with it.

C++ <--> C++/CLI <--> C# is probably the easiest way to go, as C++/CLI interop can easily work both ways.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM