phobos/std/typecons.d
2009-04-06 18:35:00 +00:00

836 lines
21 KiB
D

// Written in the D programming language.
/**
This module implements a variety of type constructors, i.e., templates
that allow construction of new, useful general-purpose types.
Macros:
WIKI = Phobos/StdVariant
Synopsis:
----
// value tuples
alias Tuple!(float, "x", float, "y", float, "z") Coord;
Coord c;
c.field[1] = 1; // access by index
c.z = 1; // access by given name
alias Tuple!(string, string) DicEntry; // names can be omitted
// enumerated values with conversions to and from strings
mixin(defineEnum!("Openmode", "READ", "WRITE", "READWRITE", "APPEND"));
void foo()
{
Openmode m = Openmode.READ;
string s = enumToString(m);
assert(s == "READ");
Openmode m1;
assert(enumFromString(s, m1) && m1 == m);
}
// Rebindable references to const and invariant objects
void bar()
{
const w1 = new Widget, w2 = new Widget;
w1.foo();
// w1 = w2 would not work; can't rebind const object
auto r = Rebindable!(const Widget)(w1);
// invoke method as if r were a Widget object
r.foo();
// rebind r to refer to another object
r = w2;
}
----
Author:
$(WEB erdani.org, Andrei Alexandrescu), Bartosz Milewski, Don Clugston
*/
/*
* Copyright (C) 2004-2006 by Digital Mars, www.digitalmars.com
* Written by Andrei Alexandrescu, www.erdani.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* o The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* o Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
* o This notice may not be removed or altered from any source
* distribution.
*/
module std.typecons;
import std.stdio;
import std.metastrings;
import std.contracts;
import std.typetuple;
import std.conv;
import std.traits;
import std.array;
import std.range;
/**
Encapsulates unique ownership of a resource.
Resource of type T is deleted at the end of the scope, unless it is transferred.
The transfer can be explicit, by calling $(D release), or implicit, when returning
Unique from a function. The resource can be a polymorphic class object, in which case
Unique behaves polymorphically too.
Example:
*/
struct Unique(T)
{
static if (is(T:Object))
alias T RefT;
else
alias T * RefT;
public:
/+ Doesn't work yet
/**
The safe constructor. It creates the resource and
guarantees unique ownership of it (unless the constructor
of $(D T) publishes aliases of $(D this)),
*/
this(A...)(A args)
{
_p = new T(args);
}
+/
/**
Constructor that takes an rvalue.
It will ensure uniqueness, as long as the rvalue
isn't just a view on an lvalue (e.g., a cast)
Typical usage:
----
Unique!(Foo) f = new Foo;
----
*/
this(RefT p)
{
writeln("Unique constructor with rvalue");
_p = p;
}
/**
Constructor that takes an lvalue. It nulls its source.
The nulling will ensure uniqueness as long as there
are no previous aliases to the source.
*/
this(ref RefT p)
{
_p = p;
writeln("Unique constructor nulling source");
p = null;
assert(p is null);
}
/+ Doesn't work yet
/**
Constructor that takes a Unique of a type that is convertible to our type:
Disallow construction from lvalue (force the use of release on the source Unique)
If the source is an rvalue, null its content, so the destrutctor doesn't delete it
Typically used by the compiler to return $(D Unique) of derived type as $(D Unique)
of base type.
Example:
----
Unique!(Base) create()
{
Unique!(Derived) d = new Derived;
return d; // Implicit Derived->Base conversion
}
----
*/
this(U)(ref Unique!(U) u) = null;
this(U)(Unique!(U) u)
{
_p = u._p;
u._p = null;
}
+/
~this()
{
writeln("Unique destructor of ", (_p is null)? null: _p);
delete _p;
_p = null;
}
bool isEmpty() const
{
return _p is null;
}
/** Returns a unique rvalue. Nullifies the current contents */
Unique release()
{
writeln("Release");
auto u = Unique(_p);
assert(_p is null);
writeln("return from Release");
return u;
}
/** Forwards member access to contents */
RefT opDot() { return _p; }
/+ doesn't work yet!
/**
Postblit operator is undefined to prevent the cloning of $(D Unique) objects
*/
this(this) = null;
+/
private:
RefT _p;
}
/+ doesn't work yet
unittest
{
writeln("Unique class");
class Bar
{
~this() { writefln(" Bar destructor"); }
int val() const { return 4; }
}
alias Unique!(Bar) UBar;
UBar g(UBar u)
{
return u;
}
auto ub = UBar(new Bar);
assert(!ub.isEmpty);
assert(ub.val == 4);
// should not compile
// auto ub3 = g(ub);
writeln("Calling g");
auto ub2 = g(ub.release);
assert(ub.isEmpty);
assert(!ub2.isEmpty);
}
unittest
{
writeln("Unique struct");
struct Foo
{
~this() { writefln(" Bar destructor"); }
int val() const { return 3; }
}
alias Unique!(Foo) UFoo;
UFoo f(UFoo u)
{
writeln("inside f");
return u;
}
auto uf = UFoo(new Foo);
assert(!uf.isEmpty);
assert(uf.val == 3);
// should not compile
// auto uf3 = f(uf);
writeln("Unique struct: calling f");
auto uf2 = f(uf.release);
assert(uf.isEmpty);
assert(!uf2.isEmpty);
}
+/
private template tupleFields(uint index, T...)
{
static if (!T.length)
{
enum string tupleFields = "";
}
else
{
static if (is(typeof(T[1]) : string))
{
enum string tupleFields = "Type["~ToString!(index)~"] "~T[1]~"; "
~ tupleFields!(index + 1, T[2 .. $]);
}
else
{
enum string tupleFields = "Type["~ToString!(index)~"] _"
~ToString!(index)~"; "
~ tupleFields!(index + 1, T[1 .. $]);
}
}
}
// Tuple
private template noStrings(T...)
{
template A(U...) { alias U A; }
static if (T.length == 0)
alias A!() Result;
else static if (is(typeof(T[0]) : string))
alias noStrings!(T[1 .. $]).Result Result;
else
alias A!(T[0], noStrings!(T[1 .. $]).Result) Result;
}
/**
Tuple of values, for example $(D Tuple!(int, string)) is a record that
stores an $(D int) and a $(D string). $(D Tuple) can be used to bundle
values together, notably when returning multiple values from a
function. If $(D obj) is a tuple, the individual members are
accessible with the syntax $(D obj.field[0]) for the first field, $(D
obj.field[1]) for the second, and so on.
The choice of zero-based indexing instead of one-base indexing was
motivated by the ability to use value tuples with various compile-time
loop constructs (e.g. type tuple iteration), all of which use
zero-based indexing.
Example:
----
Tuple!(int, int) point;
// assign coordinates
point.field[0] = 5;
point.field[1] = 6;
// read coordinates
auto x = point.field[0];
auto y = point.[1];
----
Tuple members can be named. It is legal to mix named and unnamed
members. The method above is still applicable to all fields.
Example:
----
alias Tuple!(int, "index", string, "value") Entry;
Entry e;
e.index = 4;
e.value = "Hello";
assert(e.field[1] == "Hello");
assert(e.field[0] == 4);
----
Tuples with named fields are distinct types from tuples with unnamed
fields, i.e. each naming imparts a separate type for the tuple. Two
tuple differing in naming only are still distinct, even though they
might have the same structure.
Example:
----
Tuple!(int, "x", int, "y") point1;
Tuple!(int, int) point2;
assert(!is(typeof(point1) == typeof(point2))); // passes
----
*/
struct Tuple(T...)
{
public:
/**
The type of the tuple's components.
*/
alias noStrings!(T).Result Type;
union
{
Type field;
mixin(tupleFields!(0, T));
}
// @@@BUG 2800
//alias field this;
/**
Constructor taking one value for each field. Each argument must be
implicitly assignable to the respective element of the target.
*/
this(U...)(U values) if (U.length == Type.length)
{
foreach (i, Unused; T)
{
field[i] = values[i];
}
}
/**
Constructor taking a compatible tuple. Each element of the source
must be implicitly assignable to the respective element of the
target.
*/
// @@@BUG@@@
//this(U)(Tuple!(U) another)
this(U)(U another)
{
static assert(U.length == T.length);
foreach (i, Unused; T)
{
field[i] = another.field[i];
}
}
/**
Comparison for equality.
*/
bool opEquals(T)(T rhs)
{
static assert(field.length == rhs.field.length,
"Length mismatch in attempting to compare a "
~typeof(this).stringof
~" with a "~typeof(rhs).stringof);
foreach (i, f; field)
{
if (f != rhs.field[i]) return false;
}
return true;
}
/**
The number of elements in the tuple.
*/
enum size_t length = Type.length;
/**
Assignment from another tuple. Each element of the source must be
implicitly assignable to the respective element of the target.
*/
void opAssign(U)(U rhs)
{
foreach (i, Unused; noStrings!(T).Result)
{
field[i] = rhs.field[i];
}
}
/**
Takes a slice of the tuple.
Example:
----
Tuple!(int, string, float, double) a;
a.field[1] = "abc";
a.field[2] = 4.5;
auto s = a.slice!(1, 3);
static assert(is(typeof(s) == Tuple!(string, float)));
assert(s.field[0] == "abc" && s.field[1] == 4.5);
----
*/
ref Tuple!(Type[from .. to]) slice(uint from, uint to)()
{
return *cast(typeof(return) *) &(field[from]);
}
unittest
{
.Tuple!(int, string, float, double) a;
a.field[1] = "abc";
a.field[2] = 4.5;
auto s = a.slice!(1, 3);
static assert(is(typeof(s) == Tuple!(string, float)));
assert(s.field[0] == "abc" && s.field[1] == 4.5);
}
static string toStringHeader = Tuple.stringof ~ "(";
static string toStringFooter = ")";
static string toStringSeparator = ", ";
/**
Converts to string.
*/
string toString()
{
char[] result;
auto app = appender(&result);
app.put(toStringHeader);
foreach (i, Unused; noStrings!(T).Result)
{
static if (i > 0) result ~= toStringSeparator;
static if (is(typeof(to!string(field[i]))))
app.put(to!string(field[i]));
else
app.put(typeof(field[i]).stringof);
}
app.put(toStringFooter);
return assumeUnique(result);
}
}
unittest
{
{
Tuple!(int, "a", int, "b") nosh;
nosh.a = 5;
nosh.b = 6;
assert(nosh.a == 5);
assert(nosh.b == 6);
}
{
Tuple!(short, double) b;
b.field[1] = 5;
auto a = Tuple!(int, float)(b);
assert(a.field[0] == 0 && a.field[1] == 5);
a = Tuple!(int, float)(1, 2);
assert(a.field[0] == 1 && a.field[1] == 2);
}
Tuple!(int, int) nosh;
nosh.field[0] = 5;
assert(nosh.field[0] == 5);
// Tuple!(int, int) nosh1;
// assert(!is(typeof(nosh) == typeof(nosh1)));
assert(nosh.toString == "Tuple!(int,int)(5, 0)");
Tuple!(int, short) yessh;
nosh = yessh;
Tuple!(int, "a", float, "b") x;
static assert(x.a.offsetof == x.field[0].offsetof);
static assert(x.b.offsetof == x.field[1].offsetof);
x.b = 4.5;
x.a = 5;
assert(x.field[0] == 5 && x.field[1] == 4.5);
assert(x.a == 5 && x.b == 4.5);
}
/**
Returns a $(D Tuple) object instantiated and initialized according to
the arguments.
Example:
----
auto value = tuple(5, 6.7, "hello");
assert(value.field[0] == 5);
assert(value.field[1] == 6.7);
assert(value.field[2] == "hello");
----
*/
Tuple!(T) tuple(T...)(T args)
{
typeof(return) result;
static if (T.length > 0) result.field = args;
return result;
}
private template enumValuesImpl(string name, BaseType, long index, T...)
{
static if (name.length)
{
enum string enumValuesImpl = "enum "~name~" : "~BaseType.stringof
~" { "~enumValuesImpl!("", BaseType, index, T)~"}\n";
}
else
{
static if (!T.length)
{
enum string enumValuesImpl = "";
}
else
{
static if (T.length == 1
|| T.length > 1 && is(typeof(T[1]) : string))
{
enum string enumValuesImpl = T[0]~" = "~ToString!(index)~", "
~enumValuesImpl!("", BaseType, index + 1, T[1 .. $]);
}
else
{
enum string enumValuesImpl = T[0]~" = "~ToString!(T[1])~", "
~enumValuesImpl!("", BaseType, T[1] + 1, T[2 .. $]);
}
}
}
}
private template enumParserImpl(string name, bool first, T...)
{
static if (first)
{
enum string enumParserImpl = "bool enumFromString(string s, ref "
~name~" v) {\n"
~enumParserImpl!(name, false, T)
~"return false;\n}\n";
}
else
{
static if (T.length)
enum string enumParserImpl =
"if (s == `"~T[0]~"`) return (v = "~name~"."~T[0]~"), true;\n"
~enumParserImpl!(name, false, T[1 .. $]);
else
enum string enumParserImpl = "";
}
}
private template enumPrinterImpl(string name, bool first, T...)
{
static if (first)
{
enum string enumPrinterImpl = "string enumToString("~name~" v) {\n"
~enumPrinterImpl!(name, false, T)~"\n}\n";
}
else
{
static if (T.length)
enum string enumPrinterImpl =
"if (v == "~name~"."~T[0]~") return `"~T[0]~"`;\n"
~enumPrinterImpl!(name, false, T[1 .. $]);
else
enum string enumPrinterImpl = "return null;";
}
}
private template ValueTuple(T...)
{
alias T ValueTuple;
}
private template StringsOnly(T...)
{
static if (T.length == 1)
static if (is(typeof(T[0]) : string))
alias ValueTuple!(T[0]) StringsOnly;
else
alias ValueTuple!() StringsOnly;
else
static if (is(typeof(T[0]) : string))
alias ValueTuple!(T[0], StringsOnly!(T[1 .. $])) StringsOnly;
else
alias ValueTuple!(StringsOnly!(T[1 .. $])) StringsOnly;
}
/**
Defines truly named enumerated values with parsing and stringizing
primitives.
Example:
----
mixin(defineEnum!("Abc", "A", "B", 5, "C"));
----
is equivalent to the following code:
----
enum Abc { A, B = 5, C }
string enumToString(Abc v) { ... }
Abc enumFromString(string s) { ... }
----
The $(D enumToString) function generates the unqualified names
of the enumerated values, i.e. "A", "B", and "C". The $(D
enumFromString) function expects one of "A", "B", and "C", and throws
an exception in any other case.
A base type can be specified for the enumeration like this:
----
mixin(defineEnum!("Abc", ubyte, "A", "B", "C", 255));
----
In this case the generated $(D enum) will have a $(D ubyte)
representation. */
template defineEnum(string name, T...)
{
static if (is(typeof(cast(T[0]) T[0].init)))
enum string defineEnum =
enumValuesImpl!(name, T[0], 0, T[1 .. $])
~ enumParserImpl!(name, true, StringsOnly!(T[1 .. $]))
~ enumPrinterImpl!(name, true, StringsOnly!(T[1 .. $]));
else
alias defineEnum!(name, int, T) defineEnum;
}
unittest
{
mixin(defineEnum!("_24b455e148a38a847d65006bca25f7fe",
"A1", 1, "B1", "C1"));
auto a = _24b455e148a38a847d65006bca25f7fe.A1;
assert(enumToString(a) == "A1");
_24b455e148a38a847d65006bca25f7fe b;
assert(enumFromString("B1", b)
&& b == _24b455e148a38a847d65006bca25f7fe.B1);
}
/**
$(D Rebindable!(T)) is a simple, efficient wrapper that behaves just
like an object of type $(D T), except that you can reassign it to
refer to another object. For completeness, $(D Rebindable!(T)) aliases
itself away to $(D T) if $(D T) is a non-const object type. However,
$(D Rebindable!(T)) does not compile if $(D T) is a non-class type.
Regular $(D const) object references cannot be reassigned:
----
class Widget { int x; int y() const { return a; } }
const a = new Widget;
a.y(); // fine
a.x = 5; // error! can't modify const a
a = new Widget; // error! can't modify const a
----
However, $(D Rebindable!(Widget)) does allow reassignment, while
otherwise behaving exactly like a $(D const Widget):
----
auto a = Rebindable!(const Widget)(new Widget);
a.y(); // fine
a.x = 5; // error! can't modify const a
a = new Widget; // fine
----
You may want to use $(D Rebindable) when you want to have mutable
storage referring to $(D const) objects, for example an array of
references that must be sorted in place. $(D Rebindable) does not
break the soundness of D's type system and does not incur any of the
risks usually associated with $(D cast).
*/
template Rebindable(T) if (is(T : Object) || isArray!(T))
{
static if (!is(T X == const(U), U) && !is(T X == invariant(U), U))
{
alias T Rebindable;
}
else static if (isArray!(T))
{
alias const(ElementType!(T))[] Rebindable;
}
else
{
struct Rebindable
{
private union
{
T original;
U stripped;
}
void opAssign(T another)
{
stripped = cast(U) another;
}
static Rebindable opCall(T initializer)
{
Rebindable result;
result = initializer;
return result;
}
alias original get;
T opDot() {
return original;
}
}
}
}
unittest
{
class C { int foo() const { return 42; } }
Rebindable!(C) obj0;
static assert(is(typeof(obj0) == C));
Rebindable!(const(C)) obj1;
static assert(is(typeof(obj1.get) == const(C)), typeof(obj1.get).stringof);
static assert(is(typeof(obj1.stripped) == C));
obj1 = new C;
assert(obj1.get !is null);
obj1 = new const(C);
assert(obj1.get !is null);
Rebindable!(invariant(C)) obj2;
static assert(is(typeof(obj2.get) == invariant(C)));
static assert(is(typeof(obj2.stripped) == C));
obj2 = new invariant(C);
assert(obj1.get !is null);
// test opDot
assert(obj2.foo == 42);
}
/**
Order the provided members to minimize size while preserving alignment.
Returns a declaration to be mixed in.
Example:
---
struct Banner {
mixin(alignForSize!(byte[6], double)(["name", "height"]));
}
---
Alignment is not always optimal for 80-bit reals, nor for structs declared
as align(1).
BUG: bugzilla 2029 prevents the signature from being (string[] names...),
so we need to use an ugly array literal instead.
*/
char [] alignForSize(E...)(string[E.length] names)
{
// Sort all of the members by .alignof.
// BUG: Alignment is not always optimal for align(1) structs
// or 80-bit reals.
// TRICK: Use the fact that .alignof is always a power of 2,
// and maximum 16 on extant systems. Thus, we can perform
// a very limited radix sort.
// Contains the members with .alignof = 64,32,16,8,4,2,1
int [][] alignlist; // workaround for bugzilla 2569
alignlist = [ [],[],[],[],[],[],[]]; // workaround for bugzilla 2562
char[][] declaration;
foreach(int i_bug,T; E) {
int i = i_bug; // workaround for bugzilla 2564 (D2 only)
declaration ~= T.stringof ~ " " ~ names[i].dup ~ ";\n";
int a = T.alignof;
int k = a>=64? 0 : a>=32? 1 : a>=16? 2 : a>=8? 3 : a>=4? 4 : a>=2? 5 : 6;
alignlist[k]~=i;
}
char [] s;
foreach(q; alignlist) {
foreach(int i; q) {
s~= declaration[i];
}
}
return s;
}
unittest {
assert(alignForSize!(int[], char[3], short, double[5])(["x", "y","z", "w"]) =="double[5u] w;\nint[] x;\nshort z;\nchar[3u] y;\n");
struct Foo{ int x; }
assert(alignForSize!(ubyte, Foo, cdouble)(["x", "y","z"]) =="cdouble z;\nFoo y;\nubyte x;\n");
}
/*--*
First-class reference type
*/
struct Ref(T)
{
private T * _p;
this(ref T value) { _p = &value; }
ref T opDot() { return *_p; }
/*ref*/ T opImplicitCastTo() { return *_p; }
ref T value() { return *_p; }
void opAssign(T value)
{
*_p = value;
}
void opAssign(T * value)
{
_p = value;
}
}
unittest
{
Ref!(int) x;
int y = 42;
x = &y;
assert(x.value == 42);
x = 5;
assert(x.value == 5);
assert(y == 5);
}