[phobos] Fwd: [Issue 4025] New: Making network with the std.stdio.File interface

Adam D. Ruppe destructionator at gmail.com
Thu Apr 8 12:55:43 PDT 2010


On Thu, Apr 08, 2010 at 11:23:29AM -0700, Steve Schveighoffer wrote:
> I think we can probably come up with an abstraction layer that uses FILE* only when dealing with standard handles.

I've got an idea; let me throw it out here and see what you all think.

Let's say File (or some internal component of it) was changed to be a template,
taking one of three types: FILE*, int, or ubyte[]/some ubyte returning range.

The one taking FILE* does what we have now. The one taking the int wraps the low
level operating system handle, and ubyte does it for memory.

Let me give an example:

struct FileImp(BASE) {
    T[] rawRead(T)(T[] buffer)
    {
        enforce(buffer.length);
	static if(is(BASE == FILE*))
            invariant result =
                .fread(buffer.ptr, T.sizeof, buffer.length, p.handle);
	else static if(is(BASE == int))
            invariant result =
                posix.read(p.handle, buffer.ptr, T.sizeof * buffer.length);
        else static if(is(BASE == ubyte[])) {
	    invariant size_t result;
	    if(p.contents.length < buffer.length) {
                buffer[0..p.contents.length] = p.contents;
		result = p.contents.length;
		p.contents.length = 0;
            } else {
                buffer[] = p.contents[0..buffer.length];
		result = buffer.length;
		p.contents = p.contents[buffer.length..$];
	    }
	} else static assert(0, "Unsupported operation on underlying file");

        errnoEnforce(!error);
        return result ? buffer[0 .. result] : null;
    }
}

The list of static ifs is really ugly. I'd prefer to put the primitives for each
type together somewhere, but I'm not sure how to best do that.

Anyway, the ideal end result would look like this:

auto stdin = FileImp!(FILE*)(std.c.stdin);
auto socket = FileImp!(int)(openSocket("example.com", 80));
auto memory = FileImp!(ubyte[])(cast(ubyte[]) "hello, world"); // cast needed so
// it matches the template and so it doesn't try to call the filename constructor

Then, they all work the same way to the outside observer.

To keep the easy

       auto file = File("file.txt");

working, we can just:

alias FileImpl!(FILE*) File;

Or whatever underlying implementation ends up working the best for generic use.

File wouldn't be the same as a range, but it can take certain ones and give out
a variety of them, so it is still pretty compatible with them while being able
to do file specific stuff efficiently as well.

BTW, something I think is important is to have at least some capability of
non-blocking calls, but this capability can be limited to just rawRead and
rawWrite to be good enough for me.


More information about the phobos mailing list