diff --git a/linux-2.mak b/linux-2.mak
index ab29fad38..dd1fde03b 100644
--- a/linux-2.mak
+++ b/linux-2.mak
@@ -71,7 +71,7 @@ INTERNAL_GC_EXTRAFILES = \
internal/gc/win32.mak \
internal/gc/linux.mak
-STD_MODULES = array asserterror base64 bind bitarray boxer compiler \
+STD_MODULES = array asserterror base64 bind bitarray boxer compiler contracts \
conv cover cpuid cstream ctype date dateparse demangle file format gc \
getopt hiddenfunc intrinsic loader math math2 md5 metastrings mmfile \
moduleinit openrj outbuffer outofmemory path perf process random \
diff --git a/std.ddoc b/std.ddoc
index a379cba6e..c91613dfe 100644
--- a/std.ddoc
+++ b/std.ddoc
@@ -116,6 +116,7 @@ NAVIGATION_PHOBOS=
$(LI std.bitarray)
$(LI std.boxer)
$(LI std.compiler)
+ $(LI std.contracts)
$(LI std.conv)
$(LI std.cover)
$(LI std.cpuid)
diff --git a/std/array.d b/std/array.d
index 3becb8aa8..978aa2272 100644
--- a/std/array.d
+++ b/std/array.d
@@ -3,7 +3,7 @@
module std.array;
private import std.c.stdio;
-import std.conv;
+import std.contracts;
class ArrayBoundsError : Error
{
diff --git a/std/asserterror.d b/std/asserterror.d
index 70a1573dc..937b77cd3 100644
--- a/std/asserterror.d
+++ b/std/asserterror.d
@@ -4,7 +4,7 @@ module std.asserterror;
import std.c.stdio;
import std.c.stdlib;
-import std.conv;
+import std.contracts;
class AssertError : Error
{
diff --git a/std/boxer.d b/std/boxer.d
index 9f978cfab..d185c4cc4 100644
--- a/std/boxer.d
+++ b/std/boxer.d
@@ -69,7 +69,7 @@ module std.boxer;
private import std.format;
private import std.string;
private import std.utf;
-import std.conv;
+import std.contracts;
/* These functions and types allow packing objects into generic containers
* and recovering them later. This comes into play in a wide spectrum of
diff --git a/std/contracts.d b/std/contracts.d
new file mode 100644
index 000000000..d706fcbd0
--- /dev/null
+++ b/std/contracts.d
@@ -0,0 +1,257 @@
+// Written in the D programming language.
+
+/**
+ * This module defines tools for effecting contracts and enforcing
+ * predicates (a la $(D_PARAM assert)).
+ *
+ * Macros:
+ * WIKI = Phobos/StdContracts
+ *
+ * Synopsis:
+ *
+ * ----
+ * string synopsis()
+ * {
+ * FILE* f = enforce(fopen("some/file"));
+ * // f is not null from here on
+ * FILE* g = enforceEx!(WriteException)(fopen("some/other/file", "w"));
+ * // g is not null from here on
+ * Exception e = collectException(write(g, readln(f)));
+ * if (e)
+ * {
+ * ... an exception occurred...
+ * }
+ * char[] line;
+ * enforce(readln(f, line));
+ * return assumeUnique(line);
+ * }
+ * ----
+ *
+ * Author:
+ * Andrei Alexandrescu
+ *
+ * Credits:
+ *
+ * Brad Roberts came up with the name $(D_PARAM contracts).
+ */
+
+module std.contracts;
+
+/*
+ * 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.
+ */
+
+/**
+ * If $(D_PARAM value) is nonzero, returns it. Otherwise, throws
+ * $(D_PARAM new Exception(msg)).
+ * Example:
+ * ----
+ * auto f = enforce(fopen("data.txt"));
+ * auto line = readln(f);
+ * enforce(line.length); // expect a non-empty line
+ * ----
+ */
+
+T enforce(T)(T value, lazy string msg = "")
+{
+ if (value) return value;
+ throw new Exception(msg);
+}
+
+/**
+ * If $(D_PARAM value) is nonzero, returns it. Otherwise, throws
+ * $(D_PARAM new E(msg)).
+ * Example:
+ * ----
+ * auto f = enforceEx!(FileMissingException)(fopen("data.txt"));
+ * auto line = readln(f);
+ * enforceEx!(DataCorruptionException)(line.length);
+ * ----
+ */
+
+template enforceEx(E)
+{
+ T enforceEx(T)(T value, lazy string msg = "")
+ {
+ if (value) return value;
+ throw new E(msg);
+ }
+}
+
+unittest
+{
+ enforce(true);
+ enforce(true, "blah");
+ typedef Exception MyException;
+ try
+ {
+ enforceEx!(MyException)(false);
+ assert(false);
+ }
+ catch (MyException e)
+ {
+ }
+}
+
+/**
+ * Evaluates $(D_PARAM expression). If evaluation throws an exception,
+ * return that exception. Otherwise, deposit the resulting value in
+ * $(D_PARAM target) and return $(D_PARAM null).
+ * Example:
+ * ----
+ * int[] a = new int[3];
+ * int b;
+ * assert(collectException(a[4], b));
+ * ----
+ */
+
+Exception collectException(T)(lazy T expression, ref T target)
+{
+ try
+ {
+ target = expression();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ return null;
+}
+
+unittest
+{
+ int[] a = new int[3];
+ int b;
+ int foo() { throw new Exception("blah"); }
+ assert(collectException(foo(), b));
+}
+
+/** Evaluates $(D_PARAM expression). If evaluation throws an
+ * exception, return that exception. Otherwise, return $(D_PARAM
+ * null). $(D_PARAM T) can be $(D_PARAM void).
+ */
+
+Exception collectException(T)(lazy T expression)
+{
+ try
+ {
+ expression();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ return null;
+}
+
+unittest
+{
+ int foo() { throw new Exception("blah"); }
+ assert(collectException(foo()));
+}
+
+/**
+ * Casts a mutable array to an invariant array in an idiomatic
+ * manner. Technically, $(D_PARAM assumeUnique) just inserts a cast,
+ * but its name documents assumptions on the part of the
+ * caller. $(D_PARAM assumeUnique(arr)) should only be called when
+ * there are no more active mutable aliases to elements of $(D_PARAM
+ * arr). To strenghten this assumption, $(D_PARAM assumeUnique(arr))
+ * also clears $(D_PARAM arr) before returning. Essentially $(D_PARAM
+ * assumeUnique(arr)) indicates commitment from the caller that there
+ * is no more mutable access to any of $(D_PARAM arr)'s elements
+ * (transitively), and that all future accesses will be done through
+ * the invariant array returned by $(D_PARAM assumeUnique).
+ *
+ * Typically, $(D_PARAM assumeUnique) is used to return arrays from
+ * functions that have allocated and built them.
+ *
+ * Example:
+ *
+ * ----
+ * string letters()
+ * {
+ * char[] result = new char['z' - 'a' + 1];
+ * foreach (i, ref e; result)
+ * {
+ * e = 'a' + i;
+ * }
+ * return assumeUnique(result);
+ * }
+ * ----
+ *
+ * The use in the example above is correct because $(D_PARAM result)
+ * was private to $(D_PARAM letters) and is unaccessible in writing
+ * after the function returns. The following example shows an
+ * incorrect use of $(D_PARAM assumeUnique).
+ *
+ * Bad:
+ *
+ * ----
+ * private char[] buffer;
+ * string letters(char first, char last)
+ * {
+ * if (first >= last) return null; // fine
+ * auto sneaky = buffer;
+ * sneaky.length = last - first + 1;
+ * foreach (i, ref e; sneaky)
+ * {
+ * e = 'a' + i;
+ * }
+ * return assumeUnique(sneaky); // BAD
+ * }
+ * ----
+ *
+ * The example above wreaks havoc on client code because it is
+ * modifying arrays that callers considered immutable. To obtain an
+ * invariant array from the writable array $(D_PARAM buffer), replace
+ * the last line with:
+ * ----
+ * return to!(string)(sneaky); // not that sneaky anymore
+ * ----
+ *
+ * The call will duplicate the array appropriately.
+ *
+ * Checking for uniqueness during compilation is possible in certain
+ * cases (see the $(D_PARAM unique) and $(D_PARAM lent) keywords in the
+ * $(LINK2 http://archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf,ArchJava)
+ * language), but complicates the language considerably. The downside
+ * of $(D_PARAM assumeUnique)'s
+ * convention-based usage is that at this time there is no formal
+ * checking of the correctness of the assumption; on the upside, the
+ * idiomatic use of $(D_PARAM assumeUnique) is simple and rare enough
+ * to make it tolerable.
+ *
+ */
+
+invariant(T)[] assumeUnique(T)(ref T[] array)
+{
+ auto result = cast(invariant(T)[]) array;
+ array = null;
+ return result;
+}
+
+unittest
+{
+ int[] arr = new int[1];
+ auto arr1 = assumeUnique(arr);
+ assert(is(typeof(arr1) == invariant(int)[]) && arr == null);
+}
diff --git a/std/conv.d b/std/conv.d
index 77061d631..26c2a3bc5 100644
--- a/std/conv.d
+++ b/std/conv.d
@@ -52,7 +52,7 @@ class ConvError : Error
{
this(string s)
{
- super(cast(string) ("conversion " ~ s));
+ super("conversion " ~ s);
}
}
@@ -68,7 +68,7 @@ class ConvOverflowError : Error
{
this(string s)
{
- super(cast(string) ("Error: overflow " ~ s));
+ super("Error: overflow " ~ s);
}
}
@@ -699,94 +699,6 @@ private N parseIntegral(S, N)(ref S s)
}
}
-/**
- * Casts a mutable array to an invariant array in an idiomatic
- * manner. Technically, $(D_PARAM assumeUnique) just inserts a cast,
- * but its name documents assumptions on the part of the
- * caller. $(D_PARAM assumeUnique(arr)) should only be called when
- * there are no more active mutable aliases to elements of $(D_PARAM
- * arr). To strenghten this assumption, $(D_PARAM assumeUnique(arr))
- * also clears $(D_PARAM arr) before returning. Essentially $(D_PARAM
- * assumeUnique(arr)) indicates commitment from the caller that there
- * is no more mutable access to any of $(D_PARAM arr)'s elements
- * (transitively), and that all future accesses will be done through
- * the invariant array returned by $(D_PARAM assumeUnique).
- *
- * Typically, $(D_PARAM assumeUnique) is used to return arrays from
- * functions that have allocated and built them.
- *
- * Example:
- *
- * ----
- * string letters()
- * {
- * char[] result = new char['z' - 'a' + 1];
- * foreach (i, ref e; result)
- * {
- * e = 'a' + i;
- * }
- * return assumeUnique(result);
- * }
- * ----
- *
- * The use in the example above is correct because $(D_PARAM result)
- * was private to $(D_PARAM letters) and is unaccessible in writing
- * after the function returns. The following example shows an
- * incorrect use of $(D_PARAM assumeUnique).
- *
- * Bad:
- *
- * ----
- * private char[] buffer;
- * string letters(char first, char last)
- * {
- * if (first >= last) return null; // fine
- * auto sneaky = buffer;
- * sneaky.length = last - first + 1;
- * foreach (i, ref e; sneaky)
- * {
- * e = 'a' + i;
- * }
- * return assumeUnique(sneaky); // BAD
- * }
- * ----
- *
- * The example above wreaks havoc on client code because it is
- * modifying arrays that callers considered immutable. To obtain an
- * invariant array from the writable array $(D_PARAM buffer), replace
- * the last line with:
- * ----
- * return to!(string)(sneaky); // not that sneaky anymore
- * ----
- *
- * The call will duplicate the array appropriately.
- *
- * Checking for uniqueness during compilation is possible in certain
- * cases (see the $(D_PARAM unique) and $(D_PARAM lent) keywords in the
- * $(LINK2 http://archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf,ArchJava)
- * language), but complicates the language considerably. The downside
- * of $(D_PARAM assumeUnique)'s
- * convention-based usage is that at this time there is no formal
- * checking of the correctness of the assumption; on the upside, the
- * idiomatic use of $(D_PARAM assumeUnique) is simple and rare enough
- * to make it tolerable.
- *
- */
-
-invariant(T)[] assumeUnique(T)(ref T[] array)
-{
- auto result = cast(invariant(T)[]) array;
- array = null;
- return result;
-}
-
-unittest
-{
- int[] arr = new int[1];
- auto arr1 = assumeUnique(arr);
- assert(is(typeof(arr1) == invariant(int)[]) && arr == null);
-}
-
/***************************************************************
Convert character string to the return type. These functions will be
deprecated because $(D_PARAM to!(T)) supersedes them.
@@ -1422,7 +1334,7 @@ F parseFloating(S : S[], F)(ref S[] s)
s = s[endptr - sz .. $];
return f;
Lerr:
- conv_error(cast(string) (s ~ " not representable as a " ~ F.stringof));
+ conv_error(to!(string)(s) ~ " not representable as a " ~ F.stringof);
assert(0);
}
@@ -1978,8 +1890,8 @@ private bool getComplexStrings(string s, out string s1, out string s2)
Lerr:
// Display the original string in the error message.
- conv_error(cast(string) ("getComplexStrings() \"" ~ s ~ "\"" ~ " s1=\""
- ~ s1 ~ "\"" ~ " s2=\"" ~ s2 ~ "\""));
+ conv_error("getComplexStrings() \"" ~ s ~ "\"" ~ " s1=\""
+ ~ s1 ~ "\"" ~ " s2=\"" ~ s2 ~ "\"");
return 0;
}
diff --git a/std/date.d b/std/date.d
index 080747a9f..84c1f5408 100644
--- a/std/date.d
+++ b/std/date.d
@@ -21,7 +21,7 @@ module std.date;
private import std.stdio;
private import std.dateparse;
-import std.conv;
+import std.contracts;
/**
* d_time is a signed arithmetic type giving the time elapsed since January 1,
diff --git a/std/dateparse.d b/std/dateparse.d
index a35659a39..6a5eaf2e3 100644
--- a/std/dateparse.d
+++ b/std/dateparse.d
@@ -38,7 +38,7 @@ class DateParseError : Error
{
this(string s)
{
- super(cast(string) ("Invalid date string: " ~ s));
+ super("Invalid date string: " ~ s);
}
}
diff --git a/std/demangle.d b/std/demangle.d
index 7cfc73f1f..57eb3ecfa 100644
--- a/std/demangle.d
+++ b/std/demangle.d
@@ -23,7 +23,7 @@ module std.demangle;
private import std.ctype;
private import std.string;
private import std.utf;
-import std.conv;
+import std.contracts;
private import std.stdio;
@@ -204,24 +204,24 @@ string demangle(string name)
case 'w': p = "dchar"; goto L1;
case 'A': // dynamic array
- p = cast(string) (parseType() ~ "[]");
+ p = parseType() ~ "[]";
goto L1;
case 'P': // pointer
- p = cast(string) (parseType() ~ "*");
+ p = parseType() ~ "*";
goto L1;
case 'G': // static array
{ size_t ns = ni;
parseNumber();
size_t ne = ni;
- p = cast(string) (parseType() ~ "[" ~ name[ns .. ne] ~ "]");
+ p = parseType() ~ "[" ~ name[ns .. ne] ~ "]";
goto L1;
}
case 'H': // associative array
p = parseType();
- p = cast(string) (parseType() ~ "[" ~ p ~ "]");
+ p = parseType() ~ "[" ~ p ~ "]";
goto L1;
case 'D': // delegate
@@ -296,10 +296,9 @@ string demangle(string name)
p ~= parseType() ~ " " ~ identifier ~ "(" ~ args ~ ")";
return p;
}
- p = cast(string) (parseType() ~
+ p = parseType() ~
(isdelegate ? " delegate(" : " function(") ~
- args ~
- ")");
+ args ~ ")";
isdelegate = 0;
goto L1;
}
diff --git a/std/file.d b/std/file.d
index 3aedd51ad..6639186f0 100644
--- a/std/file.d
+++ b/std/file.d
@@ -879,7 +879,7 @@ class FileException : Exception
this(string name, string message)
{
- super(cast(string) (name ~ ": " ~ message));
+ super(name ~ ": " ~ message);
}
this(string name, uint errno)
diff --git a/std/format.d b/std/format.d
index 06fd1c2b8..f23f721e3 100644
--- a/std/format.d
+++ b/std/format.d
@@ -81,9 +81,9 @@ class FormatError : Error
super("std.format");
}
- this(const char[] msg)
+ this(string msg)
{
- super(cast(string) ("std.format " ~ msg));
+ super("std.format " ~ msg);
}
}
@@ -2057,7 +2057,7 @@ void formattedWrite(Writer, F, A...)(ref Writer w, F[] fmt, A args)
// leftover spec?
if (fmt.length)
{
- throw new FormatError("Orphan format specifier: %" ~ fmt);
+ throw new FormatError(cast(string) ("Orphan format specifier: %" ~ fmt));
}
break;
}
diff --git a/std/hiddenfunc.d b/std/hiddenfunc.d
index be93cdf25..8f0eb7bf4 100644
--- a/std/hiddenfunc.d
+++ b/std/hiddenfunc.d
@@ -10,7 +10,7 @@ class HiddenFuncError : Error
this(ClassInfo ci)
{
- super(cast(string) ("hidden method called for " ~ ci.name));
+ super("hidden method called for " ~ ci.name);
}
}
diff --git a/std/math.d b/std/math.d
index 1b56de433..70f1eb580 100644
--- a/std/math.d
+++ b/std/math.d
@@ -70,7 +70,7 @@ class NotImplemented : Error
{
this(string msg)
{
- super(cast(string) (msg ~ "not implemented"));
+ super(msg ~ "not implemented");
}
}
diff --git a/std/md5.d b/std/md5.d
index 15737c425..664e6b0a4 100644
--- a/std/md5.d
+++ b/std/md5.d
@@ -95,7 +95,8 @@ module std.md5;
//debug=md5; // uncomment to turn on debugging printf's
-import std.string, std.conv;
+import std.string;
+import std.contracts;
/***************************************
* Computes MD5 digest of array of data.
diff --git a/std/openrj.d b/std/openrj.d
index c3a66c2da..54dcfbd18 100644
--- a/std/openrj.d
+++ b/std/openrj.d
@@ -718,7 +718,7 @@ private:
}
// Always add on to the previous line
- nextLine = cast(string) (nextLine ~ line);
+ nextLine = nextLine ~ line;
line = null;
diff --git a/std/path.d b/std/path.d
index b96f1f5f1..89fcc0f48 100644
--- a/std/path.d
+++ b/std/path.d
@@ -475,7 +475,7 @@ string defaultExt(string filename, string ext)
if (filename.length && filename[filename.length - 1] == '.')
filename ~= ext;
else
- filename = cast(string) (filename ~ "." ~ ext);
+ filename = filename ~ "." ~ ext;
}
return filename;
}
@@ -515,11 +515,11 @@ string addExt(string filename, string ext)
if (filename.length && filename[filename.length - 1] == '.')
filename ~= ext;
else
- filename = cast(string) (filename ~ "." ~ ext);
+ filename = filename ~ "." ~ ext;
}
else
{
- filename = cast(string) (filename[0 .. $ - existing.length] ~ ext);
+ filename = filename[0 .. $ - existing.length] ~ ext;
}
return filename;
}
@@ -655,7 +655,7 @@ string join(string p1, string p2)
}
else if (p1[p1.length - 1] == sep[0])
{
- p = cast(string) (p1 ~ p2);
+ p = p1 ~ p2;
}
else
{
diff --git a/std/process.d b/std/process.d
index 4bdbdcbb4..c5c3f12e3 100644
--- a/std/process.d
+++ b/std/process.d
@@ -142,9 +142,9 @@ int _spawnvp(int mode, char *pathname, char **argv)
Lerror:
retval = getErrno;
throw new Exception(
- cast(string) ("Cannot spawn " ~ toString(pathname) ~ "; "
+ "Cannot spawn " ~ toString(pathname) ~ "; "
~ toString(strerror(retval))
- ~ " [errno " ~ toString(retval) ~ "]"));
+ ~ " [errno " ~ toString(retval) ~ "]");
} // _spawnvp
private
{
diff --git a/std/socket.d b/std/socket.d
index ddaeeabf7..5993dc103 100644
--- a/std/socket.d
+++ b/std/socket.d
@@ -649,8 +649,7 @@ class InternetAddress: Address
if(!ih.getHostByName(addr))
//throw new AddressException("Invalid internet address");
throw new AddressException(
- cast(string) ("Unable to resolve host '" ~ addr
- ~ "'"));
+ "Unable to resolve host '" ~ addr ~ "'");
uiaddr = ih.addrList[0];
}
sin.sin_addr.s_addr = htonl(uiaddr);
@@ -690,7 +689,7 @@ class InternetAddress: Address
/// Human readable string representing the IPv4 address and port in the form $(I a.b.c.d:e).
string toString()
{
- return cast(string) (toAddrString() ~ ":" ~ toPortString());
+ return toAddrString() ~ ":" ~ toPortString();
}
/**
diff --git a/std/stdio.d b/std/stdio.d
index eaec6f80b..4e598493d 100644
--- a/std/stdio.d
+++ b/std/stdio.d
@@ -27,6 +27,7 @@ import std.c.string;
import std.c.stddef;
import std.conv;
import std.traits;
+import std.contracts;
import std.file, std.typetuple; // for testing only
version (DigitalMars)
@@ -1257,27 +1258,3 @@ unittest
}
fclose(f) == 0 || assert(false);
}
-
-T enforce(T)(T value, lazy string msg = "")
-{
- if (value) return value;
- throw new Exception(msg());
-}
-
-template enforceEx(E)
-{
- T enforceEx(T)(T value, lazy string msg = "")
- {
- if (value) return value;
- throw new E(msg());
- }
-}
-
-unittest
-{
- enforce(true);
- enforce(true, "blah");
- enforceEx!(StdioException)(true);
-// enforce!(Exception)(true, "blah");
-}
-
diff --git a/std/string.d b/std/string.d
index 8a1e7d6bc..a78649c3a 100644
--- a/std/string.d
+++ b/std/string.d
@@ -39,7 +39,7 @@ private import std.array;
private import std.format;
private import std.ctype;
private import std.stdarg;
-import std.conv;
+import std.contracts;
extern (C)
{
@@ -2298,7 +2298,7 @@ string toString(uint u)
ndigits = 0;
if (u < 10)
// Avoid storage allocation for simple stuff
- return cast(string) digits[u .. u + 1];
+ return digits[u .. u + 1];
else
{
while (u)
@@ -2585,8 +2585,7 @@ body
uint i = buffer.length;
if (value < radix && value < hexdigits.length)
- return cast(string)
- hexdigits[cast(size_t)value .. cast(size_t)value + 1];
+ return hexdigits[cast(size_t)value .. cast(size_t)value + 1];
do
{ ubyte c;
@@ -2677,7 +2676,7 @@ string format(...)
* enough to hold the result. Throws ArrayBoundsError if it is not.
* Returns: s
*/
-string sformat(char[] s, ...)
+char[] sformat(char[] s, ...)
{ size_t i;
void putc(dchar c)
@@ -2700,7 +2699,7 @@ string sformat(char[] s, ...)
}
std.format.doFormat(&putc, _arguments, _argptr);
- return cast(string) s[0 .. i];
+ return s[0 .. i];
}
@@ -3087,7 +3086,7 @@ string succ(string s)
default:
if (std.ctype.isalnum(c))
r[i]++;
- return cast(string) r;
+ return assumeUnique(r);
}
}
}
@@ -3562,14 +3561,14 @@ bool isNumeric(TypeInfo[] _arguments, va_list _argptr)
{
wchar[1] t;
t[0] = va_arg!(wchar)(_argptr);
- return isNumeric(cast(string)std.utf.toUTF8(t));
+ return isNumeric(std.utf.toUTF8(t));
}
else if (_arguments[0] == typeid(dchar))
{
dchar[1] t;
t[0] = va_arg!(dchar)(_argptr);
dchar[] t1 = t;
- return isNumeric(cast(string)std.utf.toUTF8(cast(dstring) t1));
+ return isNumeric(std.utf.toUTF8(cast(dstring) t1));
}
//else if (_arguments[0] == typeid(cent))
// return true;
diff --git a/std/thread.d b/std/thread.d
index 69344169a..4c7e1e4ee 100644
--- a/std/thread.d
+++ b/std/thread.d
@@ -580,7 +580,7 @@ class ThreadError : Error
{
this(string s)
{
- super(cast(string) ("Thread error: " ~ s));
+ super("Thread error: " ~ s);
}
}
diff --git a/std/typetuple.d b/std/typetuple.d
index 2b2b7456a..59c3556a9 100644
--- a/std/typetuple.d
+++ b/std/typetuple.d
@@ -63,24 +63,27 @@ template TypeTuple(TList...)
* void foo()
* {
* writefln("The index of long is ",
- * IndexOf!(long, TypeTuple!(int, long, double)));
+ * indexOf!(long, TypeTuple!(int, long, double)));
* // prints: The index of long is 1
* }
* ---
*/
-template IndexOf(T, TList...)
+template indexOf(T, TList...)
{
static if (TList.length == 0)
- const int IndexOf = -1;
+ const int indexOf = -1;
else static if (is(T == TList[0]))
- const int IndexOf = 0;
+ const int indexOf = 0;
else
- const int IndexOf =
- (IndexOf!(T, TList[1 .. length]) == -1)
+ const int indexOf =
+ (indexOf!(T, TList[1 .. length]) == -1)
? -1
- : 1 + IndexOf!(T, TList[1 .. length]);
+ : 1 + indexOf!(T, TList[1 .. length]);
}
+/// Kept for backwards compatibility
+alias indexOf IndexOf;
+
/**
* Returns a typetuple created from TList with the first occurrence,
* if any, of T removed.
diff --git a/std/uri.d b/std/uri.d
index ae4b3f7db..cc2b5deee 100644
--- a/std/uri.d
+++ b/std/uri.d
@@ -48,7 +48,7 @@ private import std.ctype;
private import std.c.stdlib;
private import std.utf;
private import std.stdio;
-import std.conv;
+import std.contracts;
class URIerror : Error
{
diff --git a/std/utf.d b/std/utf.d
index fe5b81b69..2e6a8ee64 100644
--- a/std/utf.d
+++ b/std/utf.d
@@ -43,7 +43,7 @@
module std.utf;
private import std.stdio;
-import std.conv;
+import std.contracts;
//debug=utf; // uncomment to turn on debugging printf's
diff --git a/std/variant.d b/std/variant.d
index 9a343da67..39e35fe6c 100644
--- a/std/variant.d
+++ b/std/variant.d
@@ -13,31 +13,48 @@
* Macros:
* WIKI = Phobos/StdFormat
*
- * Credits:
- *
- * Improvements to $(D_PARAM std.variant)'s code are due to Brad
- * Roberts.
- *
* Synopsis:
*
* ----
- * Variant a; // must assign before use, otherwise exception ensues
- * Variant b = 42; // initialize with an integer; now the type is int
+ * Variant a; // Must assign before use, otherwise exception ensues
+ * // Initialize with an integer; make the type int
+ * Variant b = 42;
* assert(b.type == typeid(int));
- * assert(b.peek!(int) !is null && *b.peek!(int) == 42); // peek at the value
- * auto x = b.get!(real); // automatically convert to real
- * a = b; // assign Variants to one another
- * a = 3.14; // a is assigned a new value and also a new type (double)
+ * // Peek at the value
+ * assert(b.peek!(int) !is null && *b.peek!(int) == 42);
+ * // Automatically convert per language rules
+ * auto x = b.get!(real);
+ * // Assign any other type, including other variants
+ * a = b;
+ * a = 3.14;
* assert(a.type == typeid(double));
- * assert(a > b); // implicit conversions work just as with built-in types
+ * // Implicit conversions work just as with built-in types
+ * assert(a > b);
+ * // Check for convertibility
+ * assert(!a.convertsTo!(int)); // double not convertible to int
+ * // Strings and all other arrays are supported
* a = "now I'm a string";
* assert(a == "now I'm a string");
- * assert(!a.convertsTo!(int)); // check for convertibility
+ * a = new int[42]; // can also assign arrays
+ * assert(a.length == 42);
+ * a[5] = 7;
+ * assert(a[5] == 7);
+ * // Can also assign class values
* class Foo {}
* auto foo = new Foo;
- * a = foo; // can also assign class values
+ * a = foo;
* assert(*a.peek!(Foo) == foo); // and full type information is preserved
* ----
+ *
+ * Author:
+ * Andrei Alexandrescu
+ *
+ * Credits:
+ *
+ * Reviewed by Brad Roberts. Daniel Keep provided a detailed code
+ * review prompting the following improvements: (1) better support for
+ * arrays; (2) support for associative arrays; (3) friendlier behavior
+ * towards the garbage collector.
*/
/*
@@ -81,25 +98,29 @@ private template maxSize(T...)
}
/**
+ * $(D_PARAM VariantN) is a back-end type seldom used directly by user
+ * code. Two commonly-used types using $(D_PARAM VariantN) as
+ * back-end are:
+ *
+ * $(OL $(LI $(B Algebraic): A closed discriminated union with a
+ * limited type universe (e.g., $(D_PARAM Algebraic!(int, double,
+ * string)) only accepts these three types and rejects anything
+ * else).) $(LI $(B Variant): An open discriminated union allowing an
+ * unbounded set of types. The restriction is that the size of the
+ * stored type cannot be larger than the largest built-in type. This
+ * means that $(D_PARAM Variant) can accommodate all primitive types
+ * and all user-defined types except for large $(D_PARAM struct)s.) )
+ *
+ * Both $(D_PARAM Algebraic) and $(D_PARAM Variant) share $(D_PARAM
+ * VariantN)'s interface. (See their respective documentations below.)
+ *
* $(D_PARAM VariantN) is a discriminated union type parameterized
- * with the maximum size of the types stored $(D_PARAM maxDataSize),
- * and with the list of allowed types $(D_PARAM AllowedTypes). If the
- * list is empty, then any type up of size up to $(D_PARAM
+ * with the largest size of the types stored ($(D_PARAM maxDataSize))
+ * and with the list of allowed types ($(D_PARAM AllowedTypes)). If
+ * the list is empty, then any type up of size up to $(D_PARAM
* maxDataSize) (rounded up for alignment) can be stored in a
* $(D_PARAM VariantN) object.
*
- * $(D_PARAM VariantN) is a back-end type seldom used directly by user
- * code. Two commonly-used types using $(D_PARAM VariantN) as backend are:
- *
- * $(OL $(LI $(B Variant): A $(D_PARAM VariantN) allowing all types up
- * to the size of the largest built-in type. This means that they can
- * accommodate all primitive types and all user-defined types except
- * for large $(D_PARAM struct)s.) $(LI $(B Algebraic): A $(D_PARAM
- * VariantN) with a limited type universe, with
- * automatically-computed size (e.g., $(D_PARAM Algebraic!(int,
- * double, string)) only accepts these types and rejects anything
- * else).) )
- *
*/
struct VariantN(size_t maxDataSize, AllowedTypes...)
@@ -113,42 +134,23 @@ private:
}
const size_t size = SizeChecker.sizeof - (int function()).sizeof;
- // Tells whether a type T is allowed
- template allowed(T)
+ /** Tells whether a type $(D_PARAM T) is statically allowed for
+ * storage inside a $(D_PARAM VariantN) object by looking
+ * $(D_PARAM T) up in $(D_PARAM AllowedTypes). If $(D_PARAM
+ * AllowedTypes) is empty, all types of size up to $(D_PARAM
+ * maxSize) are allowed.
+ */
+ public template allowed(T)
{
- static const bool allowed = !AllowedTypes.length
- || IndexOf!(T, AllowedTypes) >= 0;
+ static const bool allowed =
+ (T.sizeof <= size || is(T == VariantN))
+ && (!AllowedTypes.length || indexOf!(T, AllowedTypes) >= 0);
}
- template StoredType(T, Candidates...)
- {
- static if (!AllowedTypes.length)
- {
- alias T Type; // anything allowed
- }
- else static if (!Candidates.length)
- {
- alias void Type; // not allowed
- }
- else
- {
- alias ImplicitConversionTargets!(T) Possible;
- static const IndexOf!(Candidates[0], Possible) cand;
- static if (cand >= 0)
- {
- alias Candidates[0] Type;
- }
- else
- {
- alias StoredType!(T, Candidates[1 .. $]) Type;
- }
- }
- }
-
// Each internal operation is encoded with an identifier. See
// the "handler" function below.
enum OpID { getTypeInfo, get, compare, testConversion, toString,
- index, indexAssign, catAssign, copyOut }
+ index, indexAssign, catAssign, copyOut, length }
// state
int function(OpID selector, ubyte[size]* store, void* data) fptr
@@ -177,13 +179,9 @@ private:
break;
case OpID.compare:
auto rhs = cast(VariantN *) parm;
- auto rhspA = rhs.peek!(A);
- if (!rhspA)
- {
- // types are different
- return int.min; // uninitialized variant is different from any
- }
- return 0; // all uninitialized are equal
+ return rhs.peek!(A)
+ ? 0 // all uninitialized are equal
+ : int.min; // uninitialized variant is not comparable otherwise
break;
case OpID.toString:
string * target = cast(string*) parm;
@@ -194,6 +192,7 @@ private:
case OpID.index:
case OpID.indexAssign:
case OpID.catAssign:
+ case OpID.length:
throw new VariantException(
"Attempt to use an uninitialized VariantN");
break;
@@ -205,19 +204,26 @@ private:
// Handler for all of a type's operations
static int handler(A)(OpID selector, ubyte[size]* pStore, void* parm)
{
- // Input: store points to a TypeInfo object
- // Output: store points to a copy of *me, if me was not null
+ // Input: TypeInfo object
+ // Output: target points to a copy of *me, if me was not null
// Returns: true iff the A can be converted to the type represented
// by the incoming TypeInfo
- static bool tryPutting(A* me, void* target)
+ static bool tryPutting(A* me, TypeInfo targetType, void* target)
{
alias TypeTuple!(A, ImplicitConversionTargets!(A)) AllTypes;
foreach (T ; AllTypes)
{
- if (*cast(TypeInfo*) target != typeid(T)) continue;
+ if (targetType != typeid(T)) continue;
// found!!!
static if (is(typeof(*cast(T*) target = *me)))
+ {
if (me) *cast(T*) target = *me;
+ }
+ else
+ {
+ // type is not assignable
+ if (me) assert(false, typeof(A).stringof);
+ }
return true;
}
return false;
@@ -230,46 +236,73 @@ private:
break;
case OpID.copyOut:
auto target = cast(VariantN *) parm;
- memcpy(&target.store, pStore, target.store.sizeof);
+ memcpy(&target.store, pStore, A.sizeof);
target.fptr = &handler!(A);
break;
case OpID.get:
- return !tryPutting(cast(A*) pStore, parm);
+ return !tryPutting(cast(A*) pStore, *cast(TypeInfo*) parm, parm);
break; // for conformity
case OpID.testConversion:
- return !tryPutting(null, parm);
+ return !tryPutting(null, *cast(TypeInfo*) parm, null);
break;
case OpID.compare:
- auto rhs = cast(VariantN *) parm;
- A* rhspA = rhs.peek!(A);
- if (!rhspA)
- {
- // types are different
- // handling comparisons via conversions
- if (!rhs.convertsTo!(A))
- {
- return int.min; // dunno
- }
- auto rhsA = rhs.get!(A);
- rhspA = cast(typeof(rhspA)) &rhsA;
- }
auto me = cast(A*) pStore;
- if (*rhspA == *me)
+ auto rhsP = cast(VariantN *) parm;
+ auto rhsType = rhsP.type;
+ // Are we the same?
+ if (rhsType == typeid(A))
{
- return 0;
+ // cool! Same type!
+ auto rhsPA = cast(A*) &rhsP.store;
+ if (*rhsPA == *me)
+ {
+ return 0;
+ }
+ static if (is(typeof(A.init < A.init)))
+ {
+ return *me < *rhsPA ? -1 : 1;
+ }
+ else
+ {
+ // type doesn't support ordering comparisons
+ return int.min;
+ }
}
- static if (is(typeof(A.init < A.init)))
+ VariantN temp;
+ // Do I convert to rhs?
+ if (tryPutting(me, rhsType, &temp.store))
{
- return *me < *rhspA ? -1 : 1;
+ // cool, I do; temp's store contains my data in rhs's type!
+ // also fix up its fptr
+ temp.fptr = rhsP.fptr;
+ // now lhsWithRhsType is a full-blown VariantN of rhs's type
+ return temp.opCmp(*rhsP);
}
- else
+ // Does rhs convert to me?
+ *cast(TypeInfo*) &temp.store = typeid(A);
+ if (rhsP.fptr(OpID.get, &rhsP.store, &temp.store) == 0)
{
- return int.min;
+ // cool! Now temp has rhs in my type!
+ auto rhsPA = cast(A*) temp.store;
+ if (*rhsPA == *me)
+ {
+ return 0;
+ }
+ static if (is(typeof(A.init < A.init)))
+ {
+ return *me < *rhsPA ? -1 : 1;
+ }
+ else
+ {
+ // type doesn't support ordering comparisons
+ return int.min;
+ }
}
+ return int.min; // dunno
break;
case OpID.toString:
- string * target = cast(string*) parm;
- A * me = cast(A*) pStore;
+ auto target = cast(string*) parm;
+ auto me = cast(A*) pStore;
static if (is(typeof(to!(string)(*me))))
{
*target = to!(string)(*me);
@@ -322,7 +355,7 @@ private:
break;
case OpID.catAssign:
auto me = cast(A*) pStore;
- static if (is(typeof((*me)[0])))
+ static if (is(typeof((*me)[0])) && !is(typeof(me.keys)))
{
// array type; parm is the element to append
auto arg = cast(VariantN*) parm;
@@ -343,6 +376,17 @@ private:
throw new VariantException(typeid(A), typeid(void[]));
}
break;
+ case OpID.length:
+ auto me = cast(A*) pStore;
+ static if (is(typeof(me.length)))
+ {
+ return me.length;
+ }
+ else
+ {
+ throw new VariantException(typeid(A), typeid(void[]));
+ }
+ break;
default: assert(false);
}
return 0;
@@ -362,10 +406,8 @@ public:
return result;
}
- /**
- * Assigns a $(D_PARAM VariantN) from a generic
- * argument. Statically rejects disallowed types.
- */
+ /** Assigns a $(D_PARAM VariantN) from a generic
+ * argument. Statically rejects disallowed types. */
VariantN opAssign(T)(T rhs)
{
@@ -396,8 +438,7 @@ public:
}
}
- /**
- * Returns true if and only if the $(D_PARAM VariantN) object
+ /** Returns true if and only if the $(D_PARAM VariantN) object
* holds a valid value (has been initialized with, or assigned
* from, a valid value).
* Example:
@@ -416,11 +457,14 @@ public:
{
return fptr != &handler!(void);
}
-
+
/**
- * If the $(D_PARAM VariantN) object holds a value of the $(I
- * exact) type $(D_PARAM T), returns a pointer to that
- * value. Otherwise, returns $(D_PARAM null).
+ * If the $(D_PARAM VariantN) object holds a value of the
+ * $(I exact) type $(D_PARAM T), returns a pointer to that
+ * value. Otherwise, returns $(D_PARAM null). In cases
+ * where $(D_PARAM T) is statically disallowed, $(D_PARAM
+ * peek) will not compile.
+ *
* Example:
* ----
* Variant a = 5;
@@ -430,7 +474,6 @@ public:
* assert(a == 6);
* ----
*/
-
T * peek(T)()
{
static assert(allowed!(T), "Cannot store a " ~ T.stringof
@@ -439,7 +482,7 @@ public:
}
/**
- * Returns the $(D_PARAM typeid) of the currently held object.
+ * Returns the $(D_PARAM typeid) of the currently held value.
*/
TypeInfo type()
@@ -462,7 +505,7 @@ public:
return fptr(OpID.testConversion, null, &info) == 0;
}
- T[] testing123(T)(T*);
+ private T[] testing123(T)(T*);
/**
* A workaround for the fact that functions cannot return
@@ -557,23 +600,10 @@ public:
int opEquals(T)(T rhs)
{
static if (is(T == VariantN))
- {
- return fptr(OpID.compare, &store, &rhs) == 0
- || rhs.fptr(OpID.compare, &rhs.store, this) == 0;
- }
+ alias rhs temp;
else
- {
- if (convertsTo!(T))
- {
- return get!(T) == rhs;
- }
- else
- {
- // try to convert rhs to my type instead
- auto temp = Variant(rhs);
- return fptr(OpID.compare, &store, &temp) == 0;
- }
- }
+ auto temp = Variant(rhs);
+ return fptr(OpID.compare, &store, &temp) == 0;
}
/**
@@ -585,24 +615,15 @@ public:
int opCmp(T)(T rhs)
{
static if (is(T == VariantN))
- {
- auto result = fptr(OpID.compare, &store, &rhs);
- if (result == int.min)
- {
- result = -rhs.fptr(OpID.compare, &rhs.store, this);
- // hacky (shrewd?!) usage of the fact that -int.min == int.min
- if (result == int.min)
- {
- throw new VariantException(type, rhs.type);
- }
- }
- return result;
- }
+ alias rhs temp;
else
+ auto temp = Variant(rhs);
+ auto result = fptr(OpID.compare, &store, &temp);
+ if (result == int.min)
{
- auto lhs = get!(T);
- return lhs < rhs ? -1 : lhs == rhs ? 0 : 1;
+ throw new VariantException(type, rhs.type);
}
+ return result;
}
/**
@@ -614,106 +635,196 @@ public:
return type.getHash(&store);
}
- /**
- * Arithmetic between $(D_PARAM VariantN) objects and numeric values.
- */
-
- // arithmetic
- typeof(T+T) opAdd(T)( T rhs ) { return get!(T) + rhs; }
- ///ditto
- typeof(T+T) opAdd_r(T)( T lhs ) { return lhs + get!(T); }
- ///ditto
- typeof(T-T) opSub(T)( T rhs ) { return get!(T) - rhs; }
- ///ditto
- typeof(T-T) opSub_r(T)( T lhs ) { return lhs - get!(T); }
- ///ditto
- typeof(T*T) opMul(T)( T rhs ) { return get!(T) * rhs; }
- ///ditto
- typeof(T*T) opMul_r(T)( T lhs ) { return lhs * get!(T); }
- ///ditto
- typeof(T/T) opDiv(T)( T rhs ) { return get!(T) / rhs; }
- ///ditto
- typeof(T/T) opDiv_r(T)( T lhs ) { return lhs / get!(T); }
- ///ditto
- typeof(T%T) opMod(T)( T rhs ) { return get!(T) % rhs; }
- ///ditto
- typeof(T%T) opMod_r(T)( T lhs ) { return lhs % get!(T); }
- ///ditto
- typeof(T&T) opAnd(T)( T rhs ) { return get!(T) & rhs; }
- ///ditto
- typeof(T&T) opAnd_r(T)( T lhs ) { return lhs & get!(T); }
- ///ditto
- typeof(T|T) opOr(T)( T rhs ) { return get!(T) | rhs; }
- ///ditto
- typeof(T|T) opOr_r(T)( T lhs ) { return lhs | get!(T); }
- ///ditto
- typeof(T^T) opXor(T)( T rhs ) { return get!(T) ^ rhs; }
- ///ditto
- typeof(T^T) opXor_r(T)( T lhs ) { return lhs ^ get!(T); }
- ///ditto
- typeof(T<>T) opShr(T)( T rhs ) { return get!(T) >> rhs; }
- ///ditto
- typeof(T>>T) opShr_r(T)( T lhs ) { return lhs >> get!(T); }
- ///ditto
- typeof(T>>>T) opUShr(T)( T rhs ) { return get!(T) >>> rhs; }
- ///ditto
- typeof(T>>>T) opUShr_r(T)( T lhs ) { return lhs >>> get!(T); }
- ///ditto
- typeof(T~T) opCat(T)( T rhs ) { return get!(typeof(T~T)) ~ rhs; }
- ///ditto
- typeof(T~T) opCat_r(T)( T lhs ) { return lhs ~ get!(typeof(T~T)); }
-
- ///ditto
- VariantN opAddAssign(T)( T value ) { return *this = get!(T) + value; }
- ///ditto
- VariantN opSubAssign(T)( T value ) { return *this = get!(T) - value; }
- ///ditto
- VariantN opMulAssign(T)( T value ) { return *this = get!(T) * value; }
- ///ditto
- VariantN opDivAssign(T)( T value ) { return *this = get!(T) / value; }
- ///ditto
- VariantN opModAssign(T)( T value ) { return *this = get!(T) % value; }
- ///ditto
- VariantN opAndAssign(T)( T value ) { return *this = get!(T) & value; }
- ///ditto
- VariantN opOrAssign(T)( T value ) { return *this = get!(T) | value; }
- ///ditto
- VariantN opXorAssign(T)( T value ) { return *this = get!(T) ^ value; }
- ///ditto
- VariantN opShlAssign(T)( T value ) { return *this = get!(T) << value; }
- ///ditto
- VariantN opShrAssign(T)( T value ) { return *this = get!(T) >> value; }
- ///ditto
- VariantN opUShrAssign(T)( T value ) { return *this = get!(T) >>> value; }
- ///ditto
- VariantN opCatAssign(T)( T value )
+ private VariantN opArithmetic(T, string op)(T other)
{
- auto toAppend = Variant(value);
- fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false);
- return *this;
- //return *this = get!(typeof(T~T)) ~ value;
+ VariantN result;
+ static if (is(T == VariantN))
+ {
+ if (convertsTo!(uint) && other.convertsTo!(uint))
+ result = mixin("get!(uint) " ~ op ~ " other.get!(uint)");
+ else if (convertsTo!(int) && other.convertsTo!(int))
+ result = mixin("get!(int) " ~ op ~ " other.get!(int)");
+ if (convertsTo!(ulong) && other.convertsTo!(ulong))
+ result = mixin("get!(ulong) " ~ op ~ " other.get!(ulong)");
+ else if (convertsTo!(long) && other.convertsTo!(long))
+ result = mixin("get!(long) " ~ op ~ " other.get!(long)");
+ else if (convertsTo!(double) && other.convertsTo!(double))
+ result = mixin("get!(double) " ~ op ~ " other.get!(double)");
+ else
+ result = mixin("get!(real) " ~ op ~ " other.get!(real)");
+ }
+ else
+ {
+ if (is(typeof(T.max) : uint) && T.min == 0 && convertsTo!(uint))
+ result = mixin("get!(uint) " ~ op ~ " other");
+ else if (is(typeof(T.max) : int) && T.min < 0 && convertsTo!(int))
+ result = mixin("get!(int) " ~ op ~ " other");
+ if (is(typeof(T.max) : ulong) && T.min == 0 && convertsTo!(ulong))
+ result = mixin("get!(ulong) " ~ op ~ " other");
+ else if (is(typeof(T.max) : long) && T.min < 0 && convertsTo!(long))
+ result = mixin("get!(long) " ~ op ~ " other");
+ else if (is(T : double) && convertsTo!(double))
+ result = mixin("get!(double) " ~ op ~ " other");
+ else
+ result = mixin("get!(real) " ~ op ~ " other");
+ }
+ return result;
+ }
+
+ private VariantN opLogic(T, string op)(T other)
+ {
+ VariantN result;
+ static if (is(T == VariantN))
+ {
+ if (convertsTo!(uint) && other.convertsTo!(uint))
+ result = mixin("get!(uint) " ~ op ~ " other.get!(uint)");
+ else if (convertsTo!(int) && other.convertsTo!(int))
+ result = mixin("get!(int) " ~ op ~ " other.get!(int)");
+ if (convertsTo!(ulong) && other.convertsTo!(ulong))
+ result = mixin("get!(ulong) " ~ op ~ " other.get!(ulong)");
+ else
+ result = mixin("get!(long) " ~ op ~ " other.get!(long)");
+ }
+ else
+ {
+ if (is(typeof(T.max) : uint) && T.min == 0 && convertsTo!(uint))
+ result = mixin("get!(uint) " ~ op ~ " other");
+ else if (is(typeof(T.max) : int) && T.min < 0 && convertsTo!(int))
+ result = mixin("get!(int) " ~ op ~ " other");
+ if (is(typeof(T.max) : ulong) && T.min == 0 && convertsTo!(ulong))
+ result = mixin("get!(ulong) " ~ op ~ " other");
+ else
+ result = mixin("get!(long) " ~ op ~ " other");
+ }
+ return result;
}
/**
- * Array operations. If a $(D_PARAM VariantN) contains an array,
- * that array can be indexed into.
+ * Arithmetic between $(D_PARAM VariantN) objects and numeric
+ * values. All arithmetic operations return a $(D_PARAM VariantN)
+ * object typed depending on the types of both values
+ * involved. The conversion rules mimic D's built-in rules for
+ * arithmetic conversions.
+ */
+
+ // Adapted from http://www.prowiki.org/wiki4d/wiki.cgi?DanielKeep/Variant
+ // arithmetic
+ VariantN opAdd(T)(T rhs) { return opArithmetic!(T, "+")(rhs); }
+ ///ditto
+ VariantN opSub(T)(T rhs) { return opArithmetic!(T, "-")(rhs); }
+ ///ditto
+ VariantN opSub_r(T)(T lhs)
+ {
+ return VariantN(lhs).opArithmetic!(VariantN, "-")(*this);
+ }
+ ///ditto
+ VariantN opMul(T)(T rhs) { return opArithmetic!(T, "*")(rhs); }
+ ///ditto
+ VariantN opDiv(T)(T rhs) { return opArithmetic!(T, "/")(rhs); }
+ ///ditto
+ VariantN opDiv_r(T)(T lhs)
+ {
+ return VariantN(lhs).opArithmetic!(VariantN, "/")(*this);
+ }
+ ///ditto
+ VariantN opMod(T)(T rhs) { return opArithmetic!(T, "%")(rhs); }
+ ///ditto
+ VariantN opMod_r(T)(T lhs)
+ {
+ return VariantN(lhs).opArithmetic!(VariantN, "%")(*this);
+ }
+ ///ditto
+ VariantN opAnd(T)(T rhs) { return opLogic!(T, "&")(rhs); }
+ ///ditto
+ VariantN opOr(T)(T rhs) { return opLogic!(T, "|")(rhs); }
+ ///ditto
+ VariantN opXor(T)(T rhs) { return opLogic!(T, "^")(rhs); }
+ ///ditto
+ VariantN opShl(T)(T rhs) { return opLogic!(T, "<<")(rhs); }
+ ///ditto
+ VariantN opShl_r(T)(T lhs)
+ {
+ return VariantN(lhs).opLogic!(VariantN, "<<")(*this);
+ }
+ ///ditto
+ VariantN opShr(T)(T rhs) { return opLogic!(T, ">>")(rhs); }
+ ///ditto
+ VariantN opShr_r(T)(T lhs)
+ {
+ return VariantN(lhs).opLogic!(VariantN, ">>")(*this);
+ }
+ ///ditto
+ VariantN opUShr(T)(T rhs) { return opLogic!(T, ">>>")(rhs); }
+ ///ditto
+ VariantN opUShr_r(T)(T lhs)
+ {
+ return VariantN(lhs).opLogic!(VariantN, ">>>")(*this);
+ }
+ ///ditto
+ VariantN opCat(T)(T rhs)
+ {
+ auto temp = *this;
+ temp ~= rhs;
+ return temp;
+ }
+ ///ditto
+ VariantN opCat_r(T)(T rhs)
+ {
+ VariantN temp = rhs;
+ temp ~= *this;
+ return temp;
+ }
+
+ ///ditto
+ VariantN opAddAssign(T)(T rhs) { return *this = *this + rhs; }
+ ///ditto
+ VariantN opSubAssign(T)(T rhs) { return *this = *this - rhs; }
+ ///ditto
+ VariantN opMulAssign(T)(T rhs) { return *this = *this * rhs; }
+ ///ditto
+ VariantN opDivAssign(T)(T rhs) { return *this = *this / rhs; }
+ ///ditto
+ VariantN opModAssign(T)(T rhs) { return *this = *this % rhs; }
+ ///ditto
+ VariantN opAndAssign(T)(T rhs) { return *this = *this & rhs; }
+ ///ditto
+ VariantN opOrAssign(T)(T rhs) { return *this = *this | rhs; }
+ ///ditto
+ VariantN opXorAssign(T)(T rhs) { return *this = *this ^ rhs; }
+ ///ditto
+ VariantN opShlAssign(T)(T rhs) { return *this = *this << rhs; }
+ ///ditto
+ VariantN opShrAssign(T)(T rhs) { return *this = *this >> rhs; }
+ ///ditto
+ VariantN opUShrAssign(T)(T rhs) { return *this = *this >>> rhs; }
+ ///ditto
+ VariantN opCatAssign(T)(T rhs)
+ {
+ auto toAppend = VariantN(rhs);
+ fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false);
+ return *this;
+ }
+
+ /**
+ * Array and associative array operations. If a $(D_PARAM
+ * VariantN) contains an (associative) array, it can be indexed
+ * into. Otherwise, an exception is thrown.
*
* Example:
* ----
* auto a = Variant(new int[10]);
* a[5] = 42;
* assert(a[5] == 42);
+ * int[int] hash = [ 42:24 ];
+ * a = hash;
+ * assert(a[42] == 24);
* ----
*
* Caveat:
*
* Due to limitations in current language, read-modify-write
- * operations will not work properly:
- *
+ * operations $(D_PARAM op=) will not work properly:
+ *
* ----
* Variant a = new int[10];
* a[5] = 42;
@@ -728,6 +839,15 @@ public:
return result;
}
+ unittest
+ {
+ int[int] hash = [ 42:24 ];
+ Variant v = hash;
+ assert(v[42] == 24);
+ v[42] = 5;
+ assert(v[42] == 5);
+ }
+
/// ditto
VariantN opIndexAssign(T, N)(T value, N i)
{
@@ -735,6 +855,49 @@ public:
fptr(OpID.indexAssign, &store, &args) == 0 || assert(false);
return args[0];
}
+
+ /** If the $(D_PARAM VariantN) contains an (associative) array,
+ * returns the length of that array. Otherwise, throws an
+ * exception.
+ */
+ size_t length()
+ {
+ return cast(size_t) fptr(OpID.length, &store, null);
+ }
+}
+
+/**
+ * Algebraic data type restricted to a closed set of possible
+ * types. It's an alias for a $(D_PARAM VariantN) with an
+ * appropriately-constructed maximum size. $(D_PARAM Algebraic) is
+ * useful when it is desirable to restrict what a discriminated type
+ * could hold to the end of defining simpler and more efficient
+ * manipulation.
+ *
+ * Future additions to $(D_PARAM Algebraic) will allow compile-time
+ * checking that all possible types are handled by user code,
+ * eliminating a large class of errors.
+ *
+ * Bugs:
+ *
+ * Currently, $(D_PARAM Algebraic) does not allow recursive data
+ * types. They will be allowed in a future iteration of the
+ * implementation.
+ *
+ * Example:
+ * ----
+ * auto v = Algebraic!(int, double, string)(5);
+ * assert(v.peek!(int));
+ * v = 3.14;
+ * assert(v.peek!(double));
+ * // auto x = v.peek!(long); // won't compile, type long not allowed
+ * // v = '1'; // won't compile, type char not allowed
+ * ----
+ */
+
+template Algebraic(T...)
+{
+ alias VariantN!(maxSize!(T), T) Algebraic;
}
/**
@@ -749,6 +912,27 @@ public:
alias VariantN!(maxSize!(creal, char[], void delegate())) Variant;
+/**
+ * Returns an array of variants constructed from $(D_PARAM args).
+ * Example:
+ * ----
+ * auto a = variantArray(1, 3.14, "Hi!");
+ * assert(a[1] == 3.14);
+ * auto b = Variant(a); // variant array as variant
+ * assert(b[1] == 3.14);
+ * ----
+ */
+
+Variant[] variantArray(T...)(T args)
+{
+ Variant[] result;
+ foreach (arg; args)
+ {
+ result ~= Variant(arg);
+ }
+ return result;
+}
+
/**
* Thrown in three cases:
*
@@ -773,35 +957,14 @@ static class VariantException : Exception
}
this(TypeInfo source, TypeInfo target)
{
- super(cast(string) ("Variant: attempting to use incompatible types "
+ super("Variant: attempting to use incompatible types "
~ source.toString
- ~ " and " ~ target.toString));
+ ~ " and " ~ target.toString);
this.source = source;
this.target = target;
}
}
-/**
- * Returns an array of variants constructed from $(D_PARAM args).
- * Example:
- * ----
- * auto a = variantArray(1, 3.14, "Hi!");
- * assert(a[1] == 3.14);
- * auto b = Variant(a); // variant array as variant
- * assert(b[1] == 3.14);
- * ----
- */
-
-Variant[] variantArray(T...)(T args)
-{
- Variant[] result;
- foreach (arg; args)
- {
- result ~= Variant(arg);
- }
- return result;
-}
-
unittest
{
// try it with an oddly small size
@@ -810,8 +973,10 @@ unittest
// variantArray tests
auto heterogeneous = variantArray(1, 4.5, "hi");
+ assert(heterogeneous.length == 3);
auto variantArrayAsVariant = Variant(heterogeneous);
assert(variantArrayAsVariant[0] == 1);
+ assert(variantArrayAsVariant.length == 3);
// array tests
auto arr = Variant([1.2].dup);
@@ -829,7 +994,7 @@ unittest
// assign
a = *b.peek!(int);
// comparison
- assert(a == b);
+ assert(a == b, a.type.toString ~ " " ~ b.type.toString);
auto c = Variant("this is a string");
assert(a != c);
// comparison via implicit conversions
@@ -889,12 +1054,12 @@ unittest
// should be string... @@@BUG IN COMPILER
v = "Hello, World!"c;
- assert( v.peek!(char[]) );
+ assert( v.peek!(string) );
- assert( v.get!(char[]) == "Hello, World!" );
+ assert( v.get!(string) == "Hello, World!" );
assert(!is(char[] : wchar[]));
assert( !v.convertsTo!(wchar[]) );
- assert( v.get!(char[]) == "Hello, World!" );
+ assert( v.get!(string) == "Hello, World!" );
v = [1,2,3,4,5];
assert( v.peek!(int[]) );
@@ -987,24 +1152,3 @@ unittest
assert( vhash.get!(int[char[]])["c"] == 3 );
}
}
-
-/**
- * Algebraic data type restricted to a closed set of possible
- * types. It's an alias for a $(D_PARAM VariantN) with an
- * appropriately-constructed maximum size.
- *
- * Example:
- * ----
- * auto v = Algebraic!(int, double, string)(5);
- * assert(v.peek!(int));
- * v = 3.14;
- * assert(v.peek!(double));
- * // auto x = peek!(long); // won't compile, type long not allowed
- * // v = '1'; // won't compile, type char not allowed
- * ----
- */
-
-template Algebraic(T...)
-{
- alias VariantN!(maxSize!(T), T) Algebraic;
-}
diff --git a/std/zip.d b/std/zip.d
index 06813719a..b9fb117d1 100644
--- a/std/zip.d
+++ b/std/zip.d
@@ -21,6 +21,7 @@ module std.zip;
private import std.zlib;
private import std.date;
private import std.intrinsic;
+import std.conv;
//debug=print;
@@ -30,7 +31,7 @@ class ZipException : Exception
{
this(string msg)
{
- super(cast(string) ("ZipException: " ~ msg));
+ super("ZipException: " ~ msg);
}
}
@@ -317,7 +318,7 @@ class ZipArchive
endcommentlength = getUshort(i + 20);
if (i + 22 + endcommentlength > data.length)
continue;
- comment = cast(string)data[i + 22 .. i + 22 + endcommentlength];
+ comment = to!(string)(data[i + 22 .. i + 22 + endcommentlength]);
endrecOffset = i;
break;
}
@@ -378,11 +379,11 @@ class ZipArchive
if (i + namelen + extralen + commentlen > directoryOffset + directorySize)
throw new ZipException("invalid directory entry 2");
- de.name = cast(string)data[i .. i + namelen];
+ de.name = to!(string)(data[i .. i + namelen]);
i += namelen;
de.extra = data[i .. i + extralen];
i += extralen;
- de.comment = cast(string)data[i .. i + commentlen];
+ de.comment = to!(string)(data[i .. i + commentlen]);
i += commentlen;
directory[de.name] = de;