[Mono-list] Troubles with mint

Sean MacIsaac macisaac@ximian.com
24 Sep 2001 21:54:19 -0400


On Mon, 2001-09-24 at 21:25, Miguel de Icaza wrote:
> Sean,
> 
>    I am not sure that using interfaces is such a great idea to
> "abstract" the platform bits.  The reason is very simple, we are not
> going to have 3 or 4 platforms compiled in at the same time.
> 
> Ie, you wont have things like:
> 
> 	class UnixFile : IFile {
> 	}
> 
>    What we will have is just a class "MyFile" that will have a
> completely different implementation for Unix and another one for
> Windows.  You have to implement them in different ways for each
> architecture.
> 
> So you have:
> 
> UnixFile.cs
> -----
> class MyFile { [DllImport("libc") int fopen (char *name, char *mode) }
> 
> WinFile.cs
> -----
> class MyFile { [DllImport("kernel") int fopen (char *name, char *mode) }
> 
> User-of-File.cs:
> ----
> class X { MyFile x;  X () { x = new MyFile ();  x.fopen (...); }
> 
> The above is an example only.  But notice that for a "Windows" target
> we have to compile and link against WinFile.cs, while for a Unix
> target you compile and link against UnixFile.cs

The beauty of this is that you don't need to conditionally compile the
class libraries.  The interface contains all of the platform specific
items.  The actual classes used implement the interface and are choosen
by a class factory that talks to the runtime to find out what platform
we're on.  Here is an example:

internal interface IOperatingSystem
{
	IntPtr OpenFile (string path, FileAccess access);
	int WriteFile (IntPtr fd, byte[] data, int count);
}

internal class Linux : IOperatingSystem
{
	public IntPtr OpenFile (string path, FileAccess access)
	{
		do_something_linux_specfic_here...
	}

	public int WriteFile (IntPtr fd, byte[] data, int count)
	{
		do_something_linux_specific_here...
	}
}

internal class Windows : IOperatingSystem
{
	public IntPtr OpenFile (string path, FileAccess access)
	{
		do_something_windows_specfic_here...
	}

	public int WriteFile (IntPtr fd, byte[] data, int count)
	{
		do_something_windows_specific_here...
	}
}

internal class PlatformFactory
{
	private const int LINUX = 0;
	private const int WINDOWS = 1;

	[MethodImplAttribute(MethodImplOptions.InternalCall)]
	private extern int GetPlatformConst ();

	public IOperatingSystem OperatingSystem
	{
		get
		{
			switch (GetPlatformConst ()) {
				case LINUX: return new Linux();
				case WINDOWS: return new Windows();
			}
		}
	}
}

public class System.IO.FileStream
{
	private IOperatingSystem _os = PlatformFactory.OperatingSystem;
	private IntPtr fd;

	public void Open (string path, FileAccess access)
	{
		fd = _os.OpenFile (path, access);
	}

	public void Write (byte[] data, int count)
	{
		_os.WriteFile (fd, data, count);
	}
}

The interpreter has to know what platform it is on anyway, and by nature
must be different by platform and distributed as such.  However, by
using this scheme we eliminate the need to have two distributions of the
class library (since all classes are are distributed to everyone).

Notice also that in this way System.IO.FileStream doesn't care what
platform it's running on.  It knows that it has to use features of the
Operating System, but by defining the interface IOperatingSystem it
doesn't have to know which one it is using.

Also someone implementing the class libraries for a new OS need only
make a class that implements IOperatingSystem and need not sift through
the implementation details of the other operatings system in order to
see what needs to be implemented.

If nothing more we need this in the interpreter anyway as a lot of
functions return interfaces (and choose internally what class to make,
see Environment.GetEnvironmentVariables as an example).

BTW, I already have this working on my local copy and will check in once
it works with mint :)

Sean