[Mono-devel-list] Overcoming PInvoke limitations in e.g. Mono.Posix
Alan Jenkins
sourcejedi at phonecoop.coop
Fri Oct 8 16:53:58 EDT 2004
Ddo yourself a favour and skip to the bottom. Read my replies to the quoted
stuff afterwards or not at all. I have far too much time on my hands, and I
don't want to distract you from actual coding with banter.
> > > 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,
>
> This *shouldn't* be true. Binary compatibility is why there's the iBCS
> (Intel Binary Compatibility Standard) for x86 Unix systems, which
> specified the way system calls should be performed, the values for
> symbolic constants, structure layout, etc.
>
> However, (1) such binary compatibility rules are only for a single
> platform (x86 will differ from Sparc, for example), and (2) Linux
> ignored iBCS when it was developed, and thus has a different (more
> efficient) system-call mechanism and different values for constants. :-)
>
> Between different versions of the same OS, though, there normally
> shouldn't be binary compatibility issues. Linux 1.0 programs continue
> to work on Linux 2.6, and Solaris has been stable since at least 2.6
> (currently about to ship v10).
I was also thinking of libraries not defined by a standard, particularly open
source ones.
> <snip/>
>
> > > 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.
>
> Yuck. *Requiring* Reflection to use your structures is broken.
> Especially if we want to implement CAS (Code Access Security) at some
> point and allow access to Mono.Posix. Reflection requires a CAS
> permission to be used.
>
> <snip what="lots"/>
The second choice I included for completeness; it's just plain ugly. Try a
mix of choices one and three, depending on the attributes present. Mark some
fields as optional, replace optional fields with properties which throw
"NotImplemented" or somesuch if they are not available, and provide methods
for checking the presence of individual optional fields and groups of fields
(e.g. dirent.HasLinuxFields()).
> I'd try to quote something appropriate, but that would lead to lots of
> noise.
I kick myself for continuing to quote and comment when you've implied its too
noisy, but without resolving to do anything. Sorry. Skip this and get to
the code snippets, its the only important bit :).
> The problem with your approach, *as I understand your approach*, is
> primarily with structures. For a portable program, you *can't* know:
>
> 1. Member sizes. C# typedefs partially solve this.
> 2. Member offsets.
> 3. Member ordering.
>
> You'd think (2) is dependent on (1), but it isn't -- padding can be
> inserted into the structure, both explicitly and by the compiler, so a
> structure member offset is NOT just the sum of the sizes of the previous
> members.
>
> And even if (2) wasn't a problem, (3) certainly is.
I believe I can deal with these problems, hopefully the included snippets of
code should explain how.
> For example, Foo's dirent could be:
>
> struct dirent {
> ino_t d_ino;
> char d_name[NAME_MAX];
> };
>
> Bar's dirent could be:
>
> struct dirent {
> char d_name[NAME_MAX];
> char d_padding[2];
> ino_t d_ino;
> };
>
> Baz's dirent could be:
>
> struct dirent {
> struct some_nested_struct d_extension;
> off_t d_len;
> ino_t d_ino;
> char d_name[NAME_MAX];
> };
>
> There is a lot of potential for variation between platforms, and I don't
> see how you can actually solve this *only* from C# (because you'd have
> to parse the system header files to determine structure layout, and if
> you're actually going to do *that*, you might as well use C).
Yes, C is necessary. The best parser for header files is a C compiler. That
doesn't mean you have to call C code from .NET - a C program can provide all
the information you need at (platform dependant) build time.
> If we accept that a pure C# solution isn't possible (in the general
> case), then we can either: (1) not solve the general case, but create a
> solution that's "good enough" for most cases. I don't see this
> happening. (2) Use a mixed C/C# solution.
Yup.
> My principal thought is that, as soon as you're using C as part of the
> solution, you might as well run with it (like I did with Mono.Posix) and
> create cross-platform C# code and platform-specific C code. C is much
> better suited for the platform-specific code (since our target platforms
> export C structure declarations, not C#), and is easily used from C#.
I still don't like native glue code. On the other hand it would solve the
problem that C# doesn't support value-type fixed size arrays. The only way I
can think of to handle these in C# is with an automatically generated struct
with fields item_0 etc and a switch statement for the indexer, which sucks.
> > 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'm quite convinced that native glue *can't* be avoided. I'd love to be
> proven wrong, though. :-)
You're wrong; I'll just have to prove it :-). I think its possible to gather
enough information using a C compiler to move the glue to C#. The issue is
whether the advantages of doing away with native glue is outweighed by the
additional complexity (esp. with 64 bit alternative functions) and the
desirability of platform-dependant C# code.
> > 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!
>
> I look forward to seeing your code.
>
> - Jon
Example snippets follow.
--- dirent.in.cs ---
// The following struct is a C integer of undetermined size + sign
[CInt]
public struct ino_t {}
// The following struct is a C struct containing fields of undetermined offset
[CStruct]
public struct dirent {
ino_t f_off;
....
}
--- dirent.auto.c which is automatically generated from dirent.in.dll by a C#
program ---
// get type ino_t
ino_t i = -1;
int is_signed = f.baz < 0;
int size = sizeof(i);
// output "typedef" struct
...
// get struct dirent
struct dirent d;
int offset;
// get field d_ino
offset = (void*) &d.ino - (void*) &ino;
// output field
...
--- dirent.cs is the product of running dirent (the C program) ---
// Make sure there isn't any head/tail padding
[StructLayout(LayoutKind.Direct)]
public struct ino_t {
// only the type of this value changes
int value;
// the method declarations below remain the same
// although the bodies may change
public operator explicit int (ino_t value) {
return (int) ino_t.value;
}
...
}
[StructLayout(LayoutKind.Explicit)]
public struct dirent {
[FieldOffset(42)]
public ino_t d_ino;
}
-------
You would also have predifined typedefs for C types e.g. Int for "int". I've
left enums because they appear to be the easy part, and you already have a
method to deal with them.
Can I see your typedef stuff (including the perl)? Is Mono.Posix OEE publicly
available somewhere?
Yours verbosely,
Alan
More information about the Mono-devel-list
mailing list