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;