mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 14:40:30 +03:00
423 lines
10 KiB
D
423 lines
10 KiB
D
// 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:
|
|
*
|
|
* $(WEB erdani.org, Andrei Alexandrescu)
|
|
*
|
|
* Credits:
|
|
*
|
|
* Brad Roberts came up with the name $(D_PARAM contracts).
|
|
*/
|
|
|
|
module std.contracts;
|
|
private import std.conv;
|
|
private import std.algorithm;
|
|
private import std.iterator;
|
|
private import std.traits;
|
|
private import std.string;
|
|
private import std.c.stdlib;
|
|
version(unittest)
|
|
{
|
|
private import std.stdio;
|
|
}
|
|
|
|
/*
|
|
* 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, string file = __FILE__, int line = __LINE__)
|
|
(T value, lazy string msg = null)
|
|
{
|
|
if (!value) bailOut(file, line, msg);
|
|
return value;
|
|
}
|
|
|
|
private void bailOut(string file, int line, string msg)
|
|
{
|
|
throw new Exception(text(file, '(', line, "): ",
|
|
msg ? msg : "Enforcement failed"));
|
|
}
|
|
|
|
/**
|
|
* If $(D_PARAM value) is nonzero, returns it. Otherwise, throws
|
|
* $(D_PARAM ex).
|
|
* Example:
|
|
* ----
|
|
* auto f = enforce(fopen("data.txt"));
|
|
* auto line = readln(f);
|
|
* enforce(line.length, new IOException); // expect a non-empty line
|
|
* ----
|
|
*/
|
|
|
|
T enforce(T)(T value, lazy Exception ex)
|
|
{
|
|
if (!value) throw ex();
|
|
return value;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
enforce(true, new Exception("this should not be thrown"));
|
|
try
|
|
{
|
|
enforce(false, new Exception("this should be thrown"));
|
|
assert(false);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
}
|
|
}
|
|
|
|
/**
|
|
If $(D value) is nonzero, returns it. Otherwise, throws $(D new
|
|
ErrnoException(msg)). The $(D ErrnoException) class assumes that the
|
|
last operation has set $(D errno) to an error code.
|
|
*
|
|
* Example:
|
|
*
|
|
* ----
|
|
* auto f = errnoEnforce(fopen("data.txt"));
|
|
* auto line = readln(f);
|
|
* enforce(line.length); // expect a non-empty line
|
|
* ----
|
|
*/
|
|
|
|
T errnoEnforce(T, string file = __FILE__, int line = __LINE__)
|
|
(T value, lazy string msg = null)
|
|
{
|
|
if (!value) throw new ErrnoException(msg, file, line);
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* 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) throw new E(msg);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
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 $(WEB 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 be 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);
|
|
}
|
|
|
|
invariant(T[U]) assumeUnique(T, U)(ref T[U] array)
|
|
{
|
|
auto result = cast(invariant(T[U])) array;
|
|
array = null;
|
|
return result;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
int[string] arr = ["a":1];
|
|
auto arr1 = assumeUnique(arr);
|
|
assert(is(typeof(arr1) == invariant(int[string])) && arr == null);
|
|
}
|
|
|
|
/**
|
|
Returns $(D true) if $(D source)'s representation embeds a pointer
|
|
that points to $(D target)'s representation or somewhere inside
|
|
it. Note that evaluating $(D pointsTo(x, x)) checks whether $(D x) has
|
|
internal pointers.
|
|
*/
|
|
bool pointsTo(S, T)(ref S source, ref T target)
|
|
{
|
|
static if (is(S P : U*, U))
|
|
{
|
|
const void * m = source, b = &target, e = b + target.sizeof;
|
|
return b <= m && m < e;
|
|
}
|
|
else static if (is(S == struct))
|
|
{
|
|
foreach (i, subobj; source.tupleof)
|
|
{
|
|
static if (!isStaticArray!(typeof(subobj)))
|
|
if (pointsTo(subobj, target)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
else static if (isDynamicArray!(S))
|
|
{
|
|
const void* p1 = source.ptr, p2 = p1 + source.length,
|
|
b = &target, e = b + target.sizeof;
|
|
return overlap(range(p1, p2), range(b, e)).length != 0;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
struct S1 { int a; S1 * b; }
|
|
S1 a1;
|
|
S1 * p = &a1;
|
|
assert(pointsTo(p, a1));
|
|
|
|
S1 a2;
|
|
a2.b = &a1;
|
|
assert(pointsTo(a2, a1));
|
|
|
|
struct S3 { int[10] a; }
|
|
S3 a3;
|
|
auto a4 = a3.a[2 .. 3];
|
|
assert(pointsTo(a4, a3));
|
|
|
|
auto a5 = new double[4];
|
|
auto a6 = a5[1 .. 2];
|
|
assert(!pointsTo(a5, a6));
|
|
|
|
auto a7 = new double[3];
|
|
auto a8 = new double[][1];
|
|
a8[0] = a7;
|
|
assert(!pointsTo(a8[0], a8[0]));
|
|
}
|
|
|
|
/*********************
|
|
* Thrown if errors that set $(D errno) happen.
|
|
*/
|
|
class ErrnoException : Exception
|
|
{
|
|
uint errno; // operating system error code
|
|
this(string msg, string file = null, uint line = 0)
|
|
{
|
|
errno = getErrno;
|
|
version (linux)
|
|
{
|
|
char[1024] buf = void;
|
|
auto s = std.string.strerror_r(errno, buf.ptr, buf.length);
|
|
}
|
|
else
|
|
{
|
|
auto s = std.string.strerror(errno);
|
|
}
|
|
super((file ? file~'('~to!(string)(line)~"): " : "")
|
|
~msg~" ("~std.string.toString(s)~")");
|
|
}
|
|
}
|
|
|