Add And, Not, and Or to phobos.sys.meta. (#8977)

These correspond to templateAnd, templateNot, and templateOr from
std.meta. The names have been shortened and made PascalCase to make them
consistent with our normal naming rules. It's PascalCase rather than
camelCase, because the result is a template rather than a value, and we
don't normally prefix symbols with "template" just because they're
templates.

The implementation of Not is unchanged. However, unlike their std.meta
counterparts, And and Or do not short-circuit their evaluation. This makes
them consistent with the rest of phobos.sys.meta (whereas std.meta is
inconsistent about whether its templates short-circuit their evaluation),
but it's also because they're implemented to be iterative rather than
recursive, which should make them more efficient.
This commit is contained in:
Jonathan M Davis 2024-04-09 04:20:12 -06:00 committed by GitHub
parent 95a9d9f122
commit 0663564600
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -50,8 +50,8 @@
$(BOOKTABLE ,
$(TR $(TH Category) $(TH Templates))
$(TR $(TD Building blocks) $(TD
$(LREF Alias)
$(LREF AliasSeq)
$(LREF Alias)
$(LREF AliasSeq)
))
$(TR $(TD Alias sequence filtering) $(TD
$(LREF Filter)
@ -67,6 +67,11 @@
$(LREF any)
$(LREF indexOf)
))
$(TR $(TD Template predicates) $(TD
$(LREF And)
$(LREF Not)
$(LREF Or)
))
$(TR $(TD Template instantiation) $(TD
$(LREF ApplyLeft)
$(LREF ApplyRight)
@ -678,6 +683,253 @@ unittest
static assert(indexOf!(isSameType!string, int, int, int, int) == -1);
}
/++
Combines multiple template predicates into a single template predicate using
logical AND - i.e. for the resulting predicate to be $(D true) with a
particular argument, all of the predicates must be $(D true) with that
argument.
Evaluation is $(I not) short-circuited if a $(D false) result is
encountered; the template predicate must be instantiable with all the
elements.
See_Also:
$(LREF Not)
$(LREF Or)
+/
template And(Preds...)
{
enum And(Args...) =
{
static foreach (Pred; Preds)
{
static if (!Pred!Args)
return false;
}
return true;
}();
}
///
@safe unittest
{
import phobos.sys.traits : isNumeric;
template isSameSize(size_t size)
{
enum isSameSize(T) = T.sizeof == size;
}
alias is32BitNumeric = And!(isNumeric, isSameSize!4);
static assert(!is32BitNumeric!short);
static assert( is32BitNumeric!int);
static assert(!is32BitNumeric!long);
static assert( is32BitNumeric!float);
static assert(!is32BitNumeric!double);
static assert(!is32BitNumeric!(int*));
// An empty sequence of predicates always yields true.
alias alwaysTrue = And!();
static assert(alwaysTrue!int);
}
/++
Predicates with multiple parameters are also supported. However, the number
of parameters must match.
+/
@safe unittest
{
import phobos.sys.traits : isImplicitlyConvertible, isInteger, isSameType;
alias isOnlyImplicitlyConvertible
= And!(Not!isSameType, isImplicitlyConvertible);
static assert( isOnlyImplicitlyConvertible!(int, long));
static assert(!isOnlyImplicitlyConvertible!(int, int));
static assert(!isOnlyImplicitlyConvertible!(long, int));
static assert( isOnlyImplicitlyConvertible!(string, const(char)[]));
static assert(!isOnlyImplicitlyConvertible!(string, string));
static assert(!isOnlyImplicitlyConvertible!(const(char)[], string));
// Mismatched numbers of parameters.
alias doesNotWork = And!(isInteger, isImplicitlyConvertible);
static assert(!__traits(compiles, doesNotWork!int));
static assert(!__traits(compiles, doesNotWork!(int, long)));
}
@safe unittest
{
enum testAlways(Args...) = true;
enum testNever(Args...) = false;
static assert( Instantiate!(And!(testAlways, testAlways, testAlways), int));
static assert(!Instantiate!(And!(testAlways, testAlways, testNever), int));
static assert(!Instantiate!(And!(testAlways, testNever, testNever), int));
static assert(!Instantiate!(And!(testNever, testNever, testNever), int));
static assert(!Instantiate!(And!(testNever, testNever, testAlways), int));
static assert(!Instantiate!(And!(testNever, testAlways, testAlways), int));
static assert( Instantiate!(And!(testAlways, testAlways), int));
static assert(!Instantiate!(And!(testAlways, testNever), int));
static assert(!Instantiate!(And!(testNever, testAlways), int));
static assert(!Instantiate!(And!(testNever, testNever), int));
static assert( Instantiate!(And!testAlways, int));
static assert(!Instantiate!(And!testNever, int));
// No short-circuiting.
import phobos.sys.traits : isEqual, isFloatingPoint;
static assert(!Instantiate!(And!isFloatingPoint, int));
static assert(!__traits(compiles, Instantiate!(And!(isFloatingPoint, isEqual), int)));
}
/++
Evaluates to a template predicate which negates the given predicate.
See_Also:
$(LREF And)
$(LREF Or)
+/
template Not(alias Pred)
{
enum Not(Args...) = !Pred!Args;
}
///
@safe unittest
{
import phobos.sys.traits : isDynamicArray, isPointer;
alias isNotPointer = Not!isPointer;
static assert( isNotPointer!int);
static assert(!isNotPointer!(int*));
static assert( all!(isNotPointer, string, char, float));
static assert(!all!(Not!isDynamicArray, string, char[], int[], long));
static assert( any!(Not!isDynamicArray, string, char[], int[], long));
}
/++
Predicates with multiple parameters are also supported.
+/
@safe unittest
{
import phobos.sys.traits : isImplicitlyConvertible, isInteger;
alias notImplicitlyConvertible = Not!isImplicitlyConvertible;
static assert( notImplicitlyConvertible!(long, int));
static assert(!notImplicitlyConvertible!(int, long));
static assert( notImplicitlyConvertible!(const(char)[], string));
static assert(!notImplicitlyConvertible!(string, const(char)[]));
}
/++
Combines multiple template predicates into a single template predicate using
logical OR - i.e. for the resulting predicate to be $(D true) with a
particular argument, at least one of the predicates must be $(D true) with
that argument.
Evaluation is $(I not) short-circuited if a $(D true) result is
encountered; the template predicate must be instantiable with all the
elements.
See_Also:
$(LREF And)
$(LREF Not)
+/
template Or(Preds...)
{
enum Or(Args...) =
{
static foreach (Pred; Preds)
{
static if (Pred!Args)
return true;
}
return false;
}();
}
///
@safe unittest
{
import phobos.sys.traits : isFloatingPoint, isSignedInteger;
alias isSignedNumeric = Or!(isFloatingPoint, isSignedInteger);
static assert( isSignedNumeric!short);
static assert( isSignedNumeric!long);
static assert( isSignedNumeric!double);
static assert(!isSignedNumeric!uint);
static assert(!isSignedNumeric!ulong);
static assert(!isSignedNumeric!string);
static assert(!isSignedNumeric!(int*));
// An empty sequence of predicates always yields false.
alias alwaysFalse = Or!();
static assert(!alwaysFalse!int);
}
/++
Predicates with multiple parameters are also supported. However, the number
of parameters must match.
+/
@safe unittest
{
import phobos.sys.traits : isImplicitlyConvertible, isInteger;
enum isSameSize(T, U) = T.sizeof == U.sizeof;
alias convertibleOrSameSize = Or!(isImplicitlyConvertible, isSameSize);
static assert( convertibleOrSameSize!(int, int));
static assert( convertibleOrSameSize!(int, long));
static assert(!convertibleOrSameSize!(long, int));
static assert( convertibleOrSameSize!(int, float));
static assert( convertibleOrSameSize!(float, int));
static assert(!convertibleOrSameSize!(double, int));
static assert(!convertibleOrSameSize!(float, long));
static assert( convertibleOrSameSize!(int*, string*));
// Mismatched numbers of parameters.
alias doesNotWork = Or!(isInteger, isImplicitlyConvertible);
static assert(!__traits(compiles, doesNotWork!int));
static assert(!__traits(compiles, doesNotWork!(int, long)));
}
@safe unittest
{
enum testAlways(Args...) = true;
enum testNever(Args...) = false;
static assert( Instantiate!(Or!(testAlways, testAlways, testAlways), int));
static assert( Instantiate!(Or!(testAlways, testAlways, testNever), int));
static assert( Instantiate!(Or!(testAlways, testNever, testNever), int));
static assert(!Instantiate!(Or!(testNever, testNever, testNever), int));
static assert( Instantiate!(Or!(testAlways, testAlways), int));
static assert( Instantiate!(Or!(testAlways, testNever), int));
static assert( Instantiate!(Or!(testNever, testAlways), int));
static assert(!Instantiate!(Or!(testNever, testNever), int));
static assert( Instantiate!(Or!testAlways, int));
static assert(!Instantiate!(Or!testNever, int));
static assert(Instantiate!(Or!testAlways, int));
static assert(Instantiate!(Or!testAlways, Map));
static assert(Instantiate!(Or!testAlways, int, Map));
// No short-circuiting.
import phobos.sys.traits : isEqual, isInteger;
static assert( Instantiate!(Or!isInteger, int));
static assert(!__traits(compiles, Instantiate!(Or!(isInteger, isEqual), int)));
}
/++
Instantiates the given template with the given arguments and evaluates to
the result of that template.