let's talk about output ranges
Adam D. Ruppe
destructionator at gmail.com
Thu Feb 6 10:26:52 PST 2014
I just slapped this together in the other thread, let me
copy/paste it here, talk about it a bit, then I'll finish reading
what you wrote:
struct GCSink(T) {
// so this is a reference type
private struct Impl {
T[] data;
void put(T t) { data ~= t; }
T[] finish() { return data; }
}
Impl* impl;
alias impl this;
void start() {
impl = new Impl;
}
}
struct StaticSink(T) {
T[] container;
this(T[] c) { container = c; }
size_t size;
void start() { size = 0; }
void put(T t) { container[size++] = t; }
T[] finish() { return container[0 .. size]; }
}
StaticSink!T staticSink(T)(T[] t) {
return StaticSink!T(t);
}
T[] toUpper(T, OR = GCSink!T)(in T[] data, OR output = OR()) {
output.start();
foreach(d; data)
output.put(d & ~0x20);
return output.finish();
}
void main() {
import std.stdio;
writeln(toUpper("cool")); // default: GC
char[10] buffer;
auto received = toUpper("cool", staticSink(buffer[])); //
custom static sink
assert(buffer.ptr is received.ptr);
assert(received == "COOL");
}
====
In addition to put(), I also added start() and finish(). There's
precedent for this in Phobos already: the std.digest output
ranges have methods like this. Like std.digest, put builds it up,
then finish returns the final product.
These wouldn't be required functions on all ranges. Finish might
even return null or void, if it was a sink into a write function
or something. It could also close a file or something like that.
But, if it does build into an array, finish ought to be defined
to return an analogous input range (or slice) into the data it
just built for easier chaining and basic simple usage.
(Appender in phobos has a kinda similar thing called data, but I
think this breaks things since it might be called at any time.
Finish, on the other hand, could be defined that any puts after
it are invalid.
start is used to restart things. Calling start at any time should
reset the range to reuse the buffer.
I used the Impl* on the GC one so the default worked. Ref with a
default argument would fail because it isn't an lvalue, so that
would kill the simplicity.
I called staticSink on the buffer to build the range... which my
first post said I wanted to avoid but I kind like it this way
better. It isn't a hassle to call and keeps a nice separation
between the view and the container.
More information about the Digitalmars-d
mailing list