[Mono-list] PInvoke Conventions

Fergus Henderson fjh@cs.mu.oz.au
Tue, 31 Jul 2001 09:37:58 +1000


On 30-Jul-2001, James Perry <jeperry@uwaterloo.ca> wrote:
> 
> I'm looking at the System.Runtime.InteropServices namespace, and in
> particular: StructLayoutAttribute, Marshall, MarshalAs and UnmanagedType.
> The obvious implementation of PInvoke (at least, to me) would be to
> implement it using Marshall and then define the actual values using
> [StructLayout] and MarshalAs - a platform dependent wrapper might be
> necessary, but at least using these _specified_ parts of the runtime and
> class library, PInvoke and all of its platform dependent wrappers can be
> written in C# without any needing any custom attributes (which would
> restrict such code to Mono only).

Having platform-dependent *source code* seems to me to be a worse sin than
having platform-dependent object code.  That's why I think it would be
better to implement the wrappers in unmanaged C/C++.

However, I could live with the platform-dependent source code *if*
it could be isolated to a single module or two.

> Looking at UnmanagedType and
> StructLayoutAttribute, it seems to me that most POSIX platforms could be
> swept up in a single implementation - it'd take a pretty broken
> implementation of POSIX to fool this.

I don't agree -- constant values, type sizes, and struct layouts differ
between different Posix platforms.  I don't see how you can do that
just using StructLayoutAttribute, MashalAs, or UnmanagedType and retain
source portability.  You'd have to write different source for different
platforms.

> The other suggested implementation, having external wrappers in unmanaged
> code is plagued with similar problems. From the mono-development side, it's
> again a lot of time doing a relatively simple, repetitive task (but not so
> simple or repetitive to make it very easily scriptable), this time would be
> usefull on other parts of the project. Also from the mono-development side,
> it makes porting mono to other platforms difficult since you need a platform
> dependend implementation of the library; this is mitigated by the fact it
> could probably be done once and then left to an autoconf script to
> fine-tune, but that is also a lot of work which ultimately means the runtime
> developers are now faced with a much larger base of code they have to worry
> about portability issues for which, for reason explained in the previous
> paragraph, I think is a bad idea.

It's definitely a pain, but this kind of work is easily parallelizable
amongst multiple developers.  You only need to write the C code once,
and no autoconf script is needed unless the interface that you're trying to
wrap is actually different at the C source level on different platforms.

> When you start considering 3rd party developers (you know, the people who
> would actually /use/ mono:) it's a major barrier for two reasons, one of
> which hasn't been touched on yet. The first one means that if you want to
> write something which talks to an external library you can either a) rewrite
> the library in Mono (bad) or b) become responsible for writing a wrapper for
> every platform mono supports (bad) or c) making a wrapper for a single
> platform (defeats the point of using a platform independent architecture, so
> bad). I can't see a good solution to this.

Write a single wrapper (in unmanaged C/C++) and compile it once for each
different platform.

Using native-code libraries will of course reduce the portability
of your code.  This should come as no surprise.  However, it doesn't
entirely defeat the point of using a platform independent architecture,
because although you need to distribute N copies of the wrappers, one
for each platform, once you've built the wrappers they are unlikely to
change much; whereas for your main application, which does change often,
you only need to distribute one copy.

I think you are exaggerating the drawbacks of this approach a bit.
But I agree that it does have significant drawbacks.

> Ultimately, even if the runtime interop stuff isn't sufficient for PInvoke,
> it could be implemented in a platform dependent way that mitigates a lot of
> the problems of the other two: have a shared library which describes the
> platform dependencies in an abstract way (sizes of integers, sizes of chars,
> etc...) and then have the logic to convert written in PInvoke based on this
> information.

The trouble is that the "etc..." is large; it includes "offset of tm_idst
in struct tm", "size of nlink_t", "value of O_CREAT", etc.

Still, it may be the best that we can do.
Doing it this way would probably make things easier for Mono users,
albeit at the expense of making things harder for Mono developers.

-- 
Fergus Henderson <fjh@cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.