[Mono-devel-list] Overcoming PInvoke limitations in e.g. Mono.Posix
Alan Jenkins
sourcejedi at phonecoop.coop
Fri Oct 8 03:24:06 EDT 2004
On Friday 08 October 2004 02:16, Jonathan Pryor wrote:
> On Thu, 2004-10-07 at 14:45, Alan Jenkins wrote:
> > PInvoke is convenient, but it relies on binary compatability.
>
> It should be noted that *any* interop layer will rely on binary
> compatibility, *unless* you distribute only as source code (and require
> that your users have a compiler available at install- or run-time for
> source compatibility).
Yup. The *nix way. Binary compatability is virtually nonexistant, even on
diferent versions of the same platform, so you write a powerful as possible
source level interop layer (like autoconf, which this would have to play well
with), and distribute binaries for given platforms. This is no different to
writing C interop layers by hand - they also need to be compiled on the same
platform as they run. I admit this doesn't sound much like "write once, run
anywhere", but in order for that to happen you have to have a "write many
times" somewhere. If its possible to write a program to do that, then why
not?
> > E.g. much
> > Posix specifications define symbolic constants for C programs which do
> > not have a fixed value across all platforms.
>
> Mono.Posix has a code generator to handle this. See make-map.cs. It
> generates C code which maps between the managed value and the unmanaged
> value.
>
> > The layout of structures also
> > varies between platforms. PInvoke is sufficient for the microsoft world,
> > but Mono really needs more. As far as I know the current Mono.Posix
> > implementation doesn't have a complete solution - although I don't know
> > anything about the SDL or GTK bindings.
>
> Define "complete solution." It has *a* solution, which offers a
> framework for future expansion. Granted, *all* of POSIX isn't wrapped
> yet, but it's fairly straightforward to add more support.
>
> > I propose a Mono.InteropServices namespace, with tools to write structs
> > and enums in C# from annotated (with attributes) C# code, with C code as
> > an intermediate step.
>
> Eh? So... You write C# code, which contains attributes, which you run
> through a code generator, which generates more C# code? Have I got that
> right?
>
> Actually, Mono.Posix OEE's make-map.cs does that, mostly because I was
> too lazy to keep the C# and C enum mapping declarations in sync. Then I
> broke this support by adding non-enum methods to PosixConvert (for
> DateTime conversions). Partial classes would be good for this, though.
>
> > The C code would be relatively portable, but the generated
> > C# code would be platform specific.
>
> BAD idea. Horribly bad. Platform-specific C# code requires compiling
> it for all target platforms, which implies that you need a C# compiler
> to be present on those platforms. This can make bootstrapping
> difficult.
Surely thats not a problem so long as the C# compiler doesn't require the code
in question? And if the code generation tools are only provided with Mono,
(as opposed to with all .NET runtime environments), then the compiler must
not depend on them in order to maintian portability anywhere?
I confess I have zero practical knowledge on bootstrapping difficulties.
> Worse is coping with standardization, or lack thereof. Consider the
> dirent structure (see readdir(3)). The man page says that only d_name
> can be assumed, and d_ino is an XSI extension. Meanwhile linux also has
> members d_off, d_reclen, and d_type. AIX has others.
Three solutions. One: generate only the fields which can be assumed to be
there (bail out if they aren't). Two: generate all fields, and have programs
check explicitly (through reflection) that they exist. Three: generate
properties for the fields which don't exist which throw exceptions, and
include programatic ways to check which fields are really available.
> Even worse, is that there's *actually* a dirent and a dirent64
> structure. The primary difference being the types of the d_ino and
> d_off members (32 vs 64 bits). Ditto for the stat and flock
> structures. I'm sure there are others (I just haven't hit them yet).
Um... use your "typedef" method for the fields. I thought this was always
necessary anyway - sizes of C integers are not fixed, just given minimums
(char=8, int=16, long=32, long long=64).
> What this *really* means is that, for platform-specific C# code, on some
> platforms your structure members will be of type `long', and on others
> it will be `int'. How you can have platform-independent code which uses
> this platform specific code is beyond me.
Again, this is something you have to cope with already e.g. "size_t" varies
with the platform. I think this can be made to work with private member
variables and public operators (casting and arithmetic)
> I'm sure I'm biased, but the Mono.Posix approach is much more sane.
Good. Bias is allowable, prejudgement not (to paraphrase Frank Herbert). If
I can present a solution which appeals to you, then other people in the same
situation are likely to find it useful.
> Keep the C# side fixed, and vary the C side to map arguments to the
> corresponding C# structures.
For simple situations, you can do without native glue code using my model.
> <snip/>
>
> > Stop me if this is already being worked on - I know its obvious, it's
> > just nobody seems to have got around to it yet! Unless I've missed
> > something.
>
> You're missing the details. :-)
>
> Which I've been hitting. I had been thinking that there has to be a
> better way with Mono.Posix, but I was convincing myself that it was a
> terribly difficult problem. Because it is.
Yup. Everything would be much easier if it wasn't for all those pesky
details :).
> However, your discussion has brought up some ideas for future
> implementation. Basically:
>
> - Keep all declarations in C#
> - Tag everything that needs to have mapping C code generated
> (similar to the [Map] attribute used by make-map.cs)
> - Tag methods that have 64-bit "overloads" as well.
>
> A possible example:
>
> [Map, Native ("stat"), Has64 ("stat64")]
> public struct Stat {
> [Native ("dev_t")] public ulong st_dev;
> [Native ("ino_t")] public ulong st_ino;
> [Native ("mode_t")] public uint st_mode;
> // ...
> }
>
> public static class Syscall {
> [Map, Has64 ("stat64"),
> DllImport ("HelperLib", SetLastError=true,
> EntryPoint="my_stat")]
> public static extern int stat (string file_name,
> out Stat buf);
> // ...
> }
>
> Your code generator could then pick out all the elements marked with
> [Map], generate appropriate C intermediate structures, and generate
> appropriate function definitions for the intermediate layers.
I was hoping I could get away without mangling the function calls, just the
data structures which they use.
> The Has64 attribute is needed so that the code generator knows what the
> appropriate 64-bit function to call, if necessary. Alas, you can't just
> append "64" to the function name, as readdir_r(P) is readdir64_r().
> Sigh.
But I guess its necessary in order to change the imported function
> Of course, that's a lot of attributes to keep track of and ensure is
> consistent. Better the code generator than me, I guess...
>
> It won't be easy. I wish you luck. :-)
>
> - Jon
>
Thanks for a reality check :). I think you may have misinterpreted me on some
points: I really think that native glue code can be avoided. I
underestimated the job; looks like function calls need to be handled as well
as data types, at least for Mono.Posix. I think I'm best off expressing my
ideas through code right now, so I'll try to make it as clear & well
commented as possible. Til then!
Alan
More information about the Mono-devel-list
mailing list