7e2d1dd038
The library is able to support simple TCP servers in the current state. The API is still mostly compatible with mainline vibe.d, but the driver systen has been replaced by the eventcore library and sockets/files/timers/... are now structs with automatic reference counting instead of GC collected classes. The stream interfaces have been removed for now.
385 lines
10 KiB
D
385 lines
10 KiB
D
/**
|
|
Extensions to `std.traits` module of Phobos. Some may eventually make it into Phobos,
|
|
some are dirty hacks that work only for vibe.d
|
|
|
|
Copyright: © 2012 RejectedSoftware e.K.
|
|
License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
|
|
Authors: Sönke Ludwig, Михаил Страшун
|
|
*/
|
|
|
|
module vibe.internal.traits;
|
|
|
|
import vibe.internal.typetuple;
|
|
|
|
|
|
/**
|
|
Checks if given type is a getter function type
|
|
|
|
Returns: `true` if argument is a getter
|
|
*/
|
|
template isPropertyGetter(T...)
|
|
if (T.length == 1)
|
|
{
|
|
import std.traits : functionAttributes, FunctionAttribute, ReturnType,
|
|
isSomeFunction;
|
|
static if (isSomeFunction!(T[0])) {
|
|
enum isPropertyGetter =
|
|
(functionAttributes!(T[0]) & FunctionAttribute.property) != 0
|
|
&& !is(ReturnType!T == void);
|
|
}
|
|
else
|
|
enum isPropertyGetter = false;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
interface Test
|
|
{
|
|
@property int getter();
|
|
@property void setter(int);
|
|
int simple();
|
|
}
|
|
|
|
static assert(isPropertyGetter!(typeof(&Test.getter)));
|
|
static assert(!isPropertyGetter!(typeof(&Test.setter)));
|
|
static assert(!isPropertyGetter!(typeof(&Test.simple)));
|
|
static assert(!isPropertyGetter!int);
|
|
}
|
|
|
|
/**
|
|
Checks if given type is a setter function type
|
|
|
|
Returns: `true` if argument is a setter
|
|
*/
|
|
template isPropertySetter(T...)
|
|
if (T.length == 1)
|
|
{
|
|
import std.traits : functionAttributes, FunctionAttribute, ReturnType,
|
|
isSomeFunction;
|
|
|
|
static if (isSomeFunction!(T[0])) {
|
|
enum isPropertySetter =
|
|
(functionAttributes!(T) & FunctionAttribute.property) != 0
|
|
&& is(ReturnType!(T[0]) == void);
|
|
}
|
|
else
|
|
enum isPropertySetter = false;
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
interface Test
|
|
{
|
|
@property int getter();
|
|
@property void setter(int);
|
|
int simple();
|
|
}
|
|
|
|
static assert(isPropertySetter!(typeof(&Test.setter)));
|
|
static assert(!isPropertySetter!(typeof(&Test.getter)));
|
|
static assert(!isPropertySetter!(typeof(&Test.simple)));
|
|
static assert(!isPropertySetter!int);
|
|
}
|
|
|
|
/**
|
|
Deduces single base interface for a type. Multiple interfaces
|
|
will result in compile-time error.
|
|
|
|
Params:
|
|
T = interface or class type
|
|
|
|
Returns:
|
|
T if it is an interface. If T is a class, interface it implements.
|
|
*/
|
|
template baseInterface(T)
|
|
if (is(T == interface) || is(T == class))
|
|
{
|
|
import std.traits : InterfacesTuple;
|
|
|
|
static if (is(T == interface)) {
|
|
alias baseInterface = T;
|
|
}
|
|
else
|
|
{
|
|
alias Ifaces = InterfacesTuple!T;
|
|
static assert (
|
|
Ifaces.length == 1,
|
|
"Type must be either provided as an interface or implement only one interface"
|
|
);
|
|
alias baseInterface = Ifaces[0];
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
interface I1 { }
|
|
class A : I1 { }
|
|
interface I2 { }
|
|
class B : I1, I2 { }
|
|
|
|
static assert (is(baseInterface!I1 == I1));
|
|
static assert (is(baseInterface!A == I1));
|
|
static assert (!is(typeof(baseInterface!B)));
|
|
}
|
|
|
|
|
|
/**
|
|
Determins if a member is a public, non-static data field.
|
|
*/
|
|
template isRWPlainField(T, string M)
|
|
{
|
|
static if (!isRWField!(T, M)) enum isRWPlainField = false;
|
|
else {
|
|
//pragma(msg, T.stringof~"."~M~":"~typeof(__traits(getMember, T, M)).stringof);
|
|
enum isRWPlainField = __traits(compiles, *(&__traits(getMember, Tgen!T(), M)) = *(&__traits(getMember, Tgen!T(), M)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
Determines if a member is a public, non-static, de-facto data field.
|
|
|
|
In addition to plain data fields, R/W properties are also accepted.
|
|
*/
|
|
template isRWField(T, string M)
|
|
{
|
|
import std.traits;
|
|
import std.typetuple;
|
|
|
|
static void testAssign()() {
|
|
T t = void;
|
|
__traits(getMember, t, M) = __traits(getMember, t, M);
|
|
}
|
|
|
|
// reject type aliases
|
|
static if (is(TypeTuple!(__traits(getMember, T, M)))) enum isRWField = false;
|
|
// reject non-public members
|
|
else static if (!isPublicMember!(T, M)) enum isRWField = false;
|
|
// reject static members
|
|
else static if (!isNonStaticMember!(T, M)) enum isRWField = false;
|
|
// reject non-typed members
|
|
else static if (!is(typeof(__traits(getMember, T, M)))) enum isRWField = false;
|
|
// reject void typed members (includes templates)
|
|
else static if (is(typeof(__traits(getMember, T, M)) == void)) enum isRWField = false;
|
|
// reject non-assignable members
|
|
else static if (!__traits(compiles, testAssign!()())) enum isRWField = false;
|
|
else static if (anySatisfy!(isSomeFunction, __traits(getMember, T, M))) {
|
|
// If M is a function, reject if not @property or returns by ref
|
|
private enum FA = functionAttributes!(__traits(getMember, T, M));
|
|
enum isRWField = (FA & FunctionAttribute.property) != 0;
|
|
} else {
|
|
enum isRWField = true;
|
|
}
|
|
}
|
|
|
|
unittest {
|
|
import std.algorithm;
|
|
|
|
struct S {
|
|
alias a = int; // alias
|
|
int i; // plain RW field
|
|
enum j = 42; // manifest constant
|
|
static int k = 42; // static field
|
|
private int privateJ; // private RW field
|
|
|
|
this(Args...)(Args args) {}
|
|
|
|
// read-write property (OK)
|
|
@property int p1() { return privateJ; }
|
|
@property void p1(int j) { privateJ = j; }
|
|
// read-only property (NO)
|
|
@property int p2() { return privateJ; }
|
|
// write-only property (NO)
|
|
@property void p3(int value) { privateJ = value; }
|
|
// ref returning property (OK)
|
|
@property ref int p4() { return i; }
|
|
// parameter-less template property (OK)
|
|
@property ref int p5()() { return i; }
|
|
// not treated as a property by DMD, so not a field
|
|
@property int p6()() { return privateJ; }
|
|
@property void p6(int j)() { privateJ = j; }
|
|
|
|
static @property int p7() { return k; }
|
|
static @property void p7(int value) { k = value; }
|
|
|
|
ref int f1() { return i; } // ref returning function (no field)
|
|
|
|
int f2(Args...)(Args args) { return i; }
|
|
|
|
ref int f3(Args...)(Args args) { return i; }
|
|
|
|
void someMethod() {}
|
|
|
|
ref int someTempl()() { return i; }
|
|
}
|
|
|
|
enum plainFields = ["i"];
|
|
enum fields = ["i", "p1", "p4", "p5"];
|
|
|
|
foreach (mem; __traits(allMembers, S)) {
|
|
static if (isRWField!(S, mem)) static assert(fields.canFind(mem), mem~" detected as field.");
|
|
else static assert(!fields.canFind(mem), mem~" not detected as field.");
|
|
|
|
static if (isRWPlainField!(S, mem)) static assert(plainFields.canFind(mem), mem~" not detected as plain field.");
|
|
else static assert(!plainFields.canFind(mem), mem~" not detected as plain field.");
|
|
}
|
|
}
|
|
|
|
package T Tgen(T)(){ return T.init; }
|
|
|
|
|
|
/**
|
|
Tests if the protection of a member is public.
|
|
*/
|
|
template isPublicMember(T, string M)
|
|
{
|
|
import std.algorithm, std.typetuple : TypeTuple;
|
|
|
|
static if (!__traits(compiles, TypeTuple!(__traits(getMember, T, M)))) enum isPublicMember = false;
|
|
else {
|
|
alias MEM = TypeTuple!(__traits(getMember, T, M));
|
|
enum isPublicMember = __traits(getProtection, MEM).among("public", "export");
|
|
}
|
|
}
|
|
|
|
unittest {
|
|
class C {
|
|
int a;
|
|
export int b;
|
|
protected int c;
|
|
private int d;
|
|
package int e;
|
|
void f() {}
|
|
static void g() {}
|
|
private void h() {}
|
|
private static void i() {}
|
|
}
|
|
|
|
static assert (isPublicMember!(C, "a"));
|
|
static assert (isPublicMember!(C, "b"));
|
|
static assert (!isPublicMember!(C, "c"));
|
|
static assert (!isPublicMember!(C, "d"));
|
|
static assert (!isPublicMember!(C, "e"));
|
|
static assert (isPublicMember!(C, "f"));
|
|
static assert (isPublicMember!(C, "g"));
|
|
static assert (!isPublicMember!(C, "h"));
|
|
static assert (!isPublicMember!(C, "i"));
|
|
|
|
struct S {
|
|
int a;
|
|
export int b;
|
|
private int d;
|
|
package int e;
|
|
}
|
|
static assert (isPublicMember!(S, "a"));
|
|
static assert (isPublicMember!(S, "b"));
|
|
static assert (!isPublicMember!(S, "d"));
|
|
static assert (!isPublicMember!(S, "e"));
|
|
|
|
S s;
|
|
s.a = 21;
|
|
assert(s.a == 21);
|
|
}
|
|
|
|
/**
|
|
Tests if a member requires $(D this) to be used.
|
|
*/
|
|
template isNonStaticMember(T, string M)
|
|
{
|
|
import std.typetuple;
|
|
import std.traits;
|
|
|
|
alias MF = TypeTuple!(__traits(getMember, T, M));
|
|
static if (M.length == 0) {
|
|
enum isNonStaticMember = false;
|
|
} else static if (anySatisfy!(isSomeFunction, MF)) {
|
|
enum isNonStaticMember = !__traits(isStaticFunction, MF);
|
|
} else {
|
|
enum isNonStaticMember = !__traits(compiles, (){ auto x = __traits(getMember, T, M); }());
|
|
}
|
|
}
|
|
|
|
unittest { // normal fields
|
|
struct S {
|
|
int a;
|
|
static int b;
|
|
enum c = 42;
|
|
void f();
|
|
static void g();
|
|
ref int h() { return a; }
|
|
static ref int i() { return b; }
|
|
}
|
|
static assert(isNonStaticMember!(S, "a"));
|
|
static assert(!isNonStaticMember!(S, "b"));
|
|
static assert(!isNonStaticMember!(S, "c"));
|
|
static assert(isNonStaticMember!(S, "f"));
|
|
static assert(!isNonStaticMember!(S, "g"));
|
|
static assert(isNonStaticMember!(S, "h"));
|
|
static assert(!isNonStaticMember!(S, "i"));
|
|
}
|
|
|
|
unittest { // tuple fields
|
|
struct S(T...) {
|
|
T a;
|
|
static T b;
|
|
}
|
|
|
|
alias T = S!(int, float);
|
|
auto p = T.b;
|
|
static assert(isNonStaticMember!(T, "a"));
|
|
static assert(!isNonStaticMember!(T, "b"));
|
|
|
|
alias U = S!();
|
|
static assert(!isNonStaticMember!(U, "a"));
|
|
static assert(!isNonStaticMember!(U, "b"));
|
|
}
|
|
|
|
|
|
/**
|
|
Tests if a Group of types is implicitly convertible to a Group of target types.
|
|
*/
|
|
bool areConvertibleTo(alias TYPES, alias TARGET_TYPES)()
|
|
if (isGroup!TYPES && isGroup!TARGET_TYPES)
|
|
{
|
|
static assert(TYPES.expand.length == TARGET_TYPES.expand.length);
|
|
foreach (i, V; TYPES.expand)
|
|
if (!is(V : TARGET_TYPES.expand[i]))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/// Test if the type $(D DG) is a correct delegate for an opApply where the
|
|
/// key/index is of type $(D TKEY) and the value of type $(D TVALUE).
|
|
template isOpApplyDg(DG, TKEY, TVALUE) {
|
|
import std.traits;
|
|
static if (is(DG == delegate) && is(ReturnType!DG : int)) {
|
|
private alias PTT = ParameterTypeTuple!(DG);
|
|
private alias PSCT = ParameterStorageClassTuple!(DG);
|
|
private alias STC = ParameterStorageClass;
|
|
// Just a value
|
|
static if (PTT.length == 1) {
|
|
enum isOpApplyDg = (is(PTT[0] == TVALUE));
|
|
} else static if (PTT.length == 2) {
|
|
enum isOpApplyDg = (is(PTT[0] == TKEY))
|
|
&& (is(PTT[1] == TVALUE));
|
|
} else
|
|
enum isOpApplyDg = false;
|
|
} else {
|
|
enum isOpApplyDg = false;
|
|
}
|
|
}
|
|
|
|
unittest {
|
|
static assert(isOpApplyDg!(int delegate(int, string), int, string));
|
|
static assert(isOpApplyDg!(int delegate(ref int, ref string), int, string));
|
|
static assert(isOpApplyDg!(int delegate(int, ref string), int, string));
|
|
static assert(isOpApplyDg!(int delegate(ref int, string), int, string));
|
|
}
|
|
|
|
// Synchronized statements are logically nothrow but dmd still marks them as throwing.
|
|
// DMD#4115, Druntime#1013, Druntime#1021, Phobos#2704
|
|
import core.sync.mutex : Mutex;
|
|
enum synchronizedIsNothrow = __traits(compiles, (Mutex m) nothrow { synchronized(m) {} });
|