diff --git a/Readme.md b/Readme.md index 403511d..31bb1b3 100644 --- a/Readme.md +++ b/Readme.md @@ -132,7 +132,7 @@ msg.readTuple!(typeof(args))().assertEqual(args); ``` ### Basic types These are the basic types supported by `ddbus`: -`bool`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `double`, `string`, `ObjectPath`, `InterfaceName`, `BusName` +`bool`, `ubyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `double`, `string`, `ObjectPath`, `InterfaceName`, `BusName` ObjectPath, InterfaceName and BusName are typesafe wrappers or aliases around strings which should be used to ensure type-safety. They do not allow implicit casts to each other but can be manually converted to strings either by casting to string. diff --git a/source/ddbus/conv.d b/source/ddbus/conv.d index d705116..a56913c 100644 --- a/source/ddbus/conv.d +++ b/source/ddbus/conv.d @@ -17,7 +17,7 @@ import std.variant : VariantN; void buildIter(TS...)(DBusMessageIter* iter, TS args) if (allCanDBus!TS) { foreach (index, arg; args) { - alias TS[index] T; + alias T = Unqual!(TS[index]); static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) { immutable(char)* cStr = arg.toStringz(); dbus_message_iter_append_basic(iter, typeCode!T, &cStr); @@ -172,10 +172,10 @@ void buildIter(TS...)(DBusMessageIter* iter, TS args) } T readIter(T)(DBusMessageIter* iter) - if (is(T == enum) && !is(T == InterfaceName) && !is(T == BusName) && !is(T == FileDescriptor)) { + if (is(T == enum) && !is(Unqual!T == InterfaceName) && !is(Unqual!T == BusName) && !is(Unqual!T == FileDescriptor)) { import std.algorithm.searching : canFind; - alias OriginalType!T B; + alias B = Unqual!(OriginalType!T); B value = readIter!B(iter); enforce(only(EnumMembers!T).canFind(value), new InvalidValueException(value, T.stringof)); @@ -197,9 +197,11 @@ T readIter(T)(DBusMessageIter* iter) return T(cast(E) value); } -T readIter(T)(DBusMessageIter* iter) - if (!(is(T == enum) && !is(T == InterfaceName) && !is(T == BusName) && !is(T == FileDescriptor)) - && !isInstanceOf!(BitFlags, T) && canDBus!T) { +U readIter(U)(DBusMessageIter* iter) + if (!(is(U == enum) && !is(Unqual!U == InterfaceName) && !is(Unqual!U == BusName) && !is(U == FileDescriptor)) + && !isInstanceOf!(BitFlags, U) && canDBus!U) { + alias T = Unqual!U; + auto argType = dbus_message_iter_get_arg_type(iter); T ret; @@ -215,7 +217,7 @@ T readIter(T)(DBusMessageIter* iter) ret.explicitVariant = true; } dbus_message_iter_next(iter); - return ret; + return cast(U) ret; } } @@ -227,7 +229,7 @@ T readIter(T)(DBusMessageIter* iter) const(char)* cStr; dbus_message_iter_get_basic(iter, &cStr); string str = cStr.fromStringz().idup; // copy string - static if (is(T == string) || is(T == InterfaceName) || is(T == BusName)) { + static if (is(T == string) || is(T : InterfaceName) || is(T : BusName)) { ret = cast(T)str; } else { ret = ObjectPath(str); @@ -240,20 +242,20 @@ T readIter(T)(DBusMessageIter* iter) DBusMessageIter sub; dbus_message_iter_recurse(iter, &sub); readIterTuple!T(&sub, ret); - } else static if (is(T t : U[], U)) { - assert(dbus_message_iter_get_element_type(iter) == typeCode!U); + } else static if (is(T t : S[], S)) { + assert(dbus_message_iter_get_element_type(iter) == typeCode!S); DBusMessageIter sub; dbus_message_iter_recurse(iter, &sub); while (dbus_message_iter_get_arg_type(&sub) != 0) { - static if (is(U == DictionaryEntry!(K, V), K, V)) { + static if (is(S == DictionaryEntry!(K, V), K, V)) { DBusMessageIter entry; dbus_message_iter_recurse(&sub, &entry); - ret ~= U(readIter!K(&entry), readIter!V(&entry)); + ret ~= S(readIter!K(&entry), readIter!V(&entry)); dbus_message_iter_next(&sub); } else { - ret ~= readIter!U(&sub); + ret ~= readIter!S(&sub); } } } else static if (isInstanceOf!(Variant, T)) { @@ -296,13 +298,13 @@ T readIter(T)(DBusMessageIter* iter) if (ret.type == 's') { ret.str = readIter!string(iter); - return ret; + return cast(U) ret; } else if (ret.type == 'o') { ret.obj = readIter!ObjectPath(iter); - return ret; + return cast(U) ret; } else if (ret.type == 'b') { ret.boolean = readIter!bool(iter); - return ret; + return cast(U) ret; } else if (dbus_type_is_basic(ret.type)) { dbus_message_iter_get_basic(iter, &ret.int64); } else if (ret.type == 'a') { @@ -352,7 +354,7 @@ T readIter(T)(DBusMessageIter* iter) } dbus_message_iter_next(iter); - return ret; + return cast(U) ret; } void readIterTuple(Tup)(DBusMessageIter* iter, ref Tup tuple) @@ -451,7 +453,7 @@ unittest { z = 4 } - alias V = Algebraic!(byte, short, int, long, string); + alias V = Algebraic!(ubyte, short, int, long, string); Message msg = Message(busName("org.example.wow"), ObjectPath("/wut"), interfaceName("org.test.iface"), "meth2"); V v1 = "hello from variant"; diff --git a/source/ddbus/router.d b/source/ddbus/router.d index ea02fc0..e6827fc 100644 --- a/source/ddbus/router.d +++ b/source/ddbus/router.d @@ -275,7 +275,7 @@ unittest { } struct S { - byte b; + ubyte b; ulong ul; F f; } diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index b0fa6c4..b2f8338 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -46,6 +46,10 @@ struct ObjectPath { return hashOf(_value); } + T opCast(T : ObjectPath)() const pure @nogc nothrow @safe { + return this; + } + T opCast(T : string)() const pure @nogc nothrow @safe { return value; } @@ -233,7 +237,7 @@ struct DBusAny { union { /// - byte int8; + ubyte uint8; /// short int16; /// @@ -284,8 +288,8 @@ struct DBusAny { +/ this(T)(T value) { static if (is(T == byte) || is(T == ubyte)) { - this(typeCode!byte, null, false); - int8 = cast(byte) value; + this(typeCode!ubyte, null, false); + uint8 = cast(ubyte) value; } else static if (is(T == short)) { this(typeCode!short, null, false); int16 = cast(short) value; @@ -424,8 +428,8 @@ struct DBusAny { string toString() const { string valueStr; switch (type) { - case typeCode!byte: - valueStr = int8.to!string; + case typeCode!ubyte: + valueStr = uint8.to!string; break; case typeCode!short: valueStr = int16.to!string; @@ -499,8 +503,9 @@ struct DBusAny { TypeMismatchException if the DBus type of the current value of the DBusAny object is not the same as the DBus type used to represent T. +/ - T get(T)() @property const - if (staticIndexOf!(T, BasicTypes) >= 0) { + U get(U)() @property const + if (staticIndexOf!(Unqual!U, BasicTypes) >= 0) { + alias T = Unqual!U; enforce(type == typeCode!T, new TypeMismatchException( "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ typeSig ~ "'.", typeCode!T, type)); @@ -509,17 +514,17 @@ struct DBusAny { return fileDescriptor(int32); } else static if (isIntegral!T) { enum memberName = (isUnsigned!T ? "uint" : "int") ~ (T.sizeof * 8).to!string; - return __traits(getMember, this, memberName); + return cast(U) __traits(getMember, this, memberName); } else static if (is(T == double)) { - return float64; + return cast(U) float64; } else static if (is(T == string)) { - return str; + return cast(U) str; } else static if (is(T == InterfaceName) || is(T == BusName)) { - return cast(T) str; + return cast(U) str; } else static if (is(T == ObjectPath)) { - return obj; + return cast(U) obj; } else static if (is(T == bool)) { - return boolean; + return cast(U) boolean; } else { static assert(false); } @@ -715,6 +720,21 @@ struct DBusAny { return uint64 == b.uint64; } } + + /// Returns: true if this variant is an integer. + bool typeIsIntegral() const @property { + return type.dbusIsIntegral; + } + + /// Returns: true if this variant is a floating point value. + bool typeIsFloating() const @property { + return type.dbusIsFloating; + } + + /// Returns: true if this variant is an integer or a floating point value. + bool typeIsNumeric() const @property { + return type.dbusIsNumeric; + } } unittest { @@ -725,27 +745,30 @@ unittest { return v; } - void test(T)(T value, DBusAny b) { + void test(bool testGet = false, T)(T value, DBusAny b) { assertEqual(DBusAny(value), b); assertEqual(b.to!T, value); + static if (testGet) + assertEqual(b.get!T, value); b.toString(); } - test(cast(ubyte) 184, set!"int8"(DBusAny('y', null, false), cast(byte) 184)); - test(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184)); - test(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184)); - test(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184)); - test(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184)); - test(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184)); - test(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184)); - test(1.84, set!"float64"(DBusAny('d', null, false), 1.84)); - test(true, set!"boolean"(DBusAny('b', null, false), true)); - test("abc", set!"str"(DBusAny('s', null, false), "abc")); - test(ObjectPath("/foo/Bar"), set!"obj"(DBusAny('o', null, false), ObjectPath("/foo/Bar"))); + test!true(cast(ubyte) 184, set!"uint8"(DBusAny('y', null, false), cast(ubyte) 184)); + test(cast(byte) 184, set!"uint8"(DBusAny('y', null, false), cast(byte) 184)); + test!true(cast(short) 184, set!"int16"(DBusAny('n', null, false), cast(short) 184)); + test!true(cast(ushort) 184, set!"uint16"(DBusAny('q', null, false), cast(ushort) 184)); + test!true(cast(int) 184, set!"int32"(DBusAny('i', null, false), cast(int) 184)); + test!true(cast(uint) 184, set!"uint32"(DBusAny('u', null, false), cast(uint) 184)); + test!true(cast(long) 184, set!"int64"(DBusAny('x', null, false), cast(long) 184)); + test!true(cast(ulong) 184, set!"uint64"(DBusAny('t', null, false), cast(ulong) 184)); + test!true(1.84, set!"float64"(DBusAny('d', null, false), 1.84)); + test!true(true, set!"boolean"(DBusAny('b', null, false), true)); + test!true("abc", set!"str"(DBusAny('s', null, false), "abc")); + test!true(ObjectPath("/foo/Bar"), set!"obj"(DBusAny('o', null, false), ObjectPath("/foo/Bar"))); test(cast(ubyte[])[1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false), cast(ubyte[])[1, 2, 3])); - test(variant(cast(ubyte) 184), set!"int8"(DBusAny('y', null, true), cast(byte) 184)); + test(variant(cast(ubyte) 184), set!"uint8"(DBusAny('y', null, true), cast(ubyte) 184)); test(variant(cast(short) 184), set!"int16"(DBusAny('n', null, true), cast(short) 184)); test(variant(cast(ushort) 184), set!"uint16"(DBusAny('q', null, true), cast(ushort) 184)); test(variant(cast(int) 184), set!"int32"(DBusAny('i', null, true), cast(int) 184)); diff --git a/source/ddbus/util.d b/source/ddbus/util.d index 3d43eb6..256624f 100644 --- a/source/ddbus/util.d +++ b/source/ddbus/util.d @@ -54,10 +54,11 @@ template allCanDBus(TS...) { AliasSeq of all basic types in terms of the DBus typesystem +/ package // Don't add to the API yet, 'cause I intend to move it later -alias BasicTypes = AliasSeq!(bool, byte, short, ushort, int, uint, long, ulong, +alias BasicTypes = AliasSeq!(bool, ubyte, short, ushort, int, uint, long, ulong, double, string, ObjectPath, InterfaceName, BusName, FileDescriptor); -template basicDBus(T) { +template basicDBus(U) { + alias T = Unqual!U; static if (staticIndexOf!(T, BasicTypes) >= 0) { enum basicDBus = true; } else static if (is(T B == enum)) { @@ -70,7 +71,8 @@ template basicDBus(T) { } } -template canDBus(T) { +template canDBus(U) { + alias T = Unqual!U; static if (basicDBus!T || is(T == DBusAny)) { enum canDBus = true; } else static if (isInstanceOf!(Variant, T)) { @@ -107,9 +109,10 @@ unittest { (canDBus!FileDescriptor).assertTrue(); } -string typeSig(T)() - if (canDBus!T) { - static if (is(T == byte)) { +string typeSig(U)() + if (canDBus!U) { + alias T = Unqual!U; + static if (is(T == ubyte)) { return "y"; } else static if (is(T == bool)) { return "b"; @@ -208,6 +211,33 @@ int typeCode(T)() return 'e'; } +/** + Params: + type = the type code of a type (first character in a type string) + Returns: true if the given type is an integer. +*/ +bool dbusIsIntegral(int type) @property { + return type == 'y' || type == 'n' || type == 'q' || type == 'i' || type == 'u' || type == 'x' || type == 't'; +} + +/** + Params: + type = the type code of a type (first character in a type string) + Returns: true if the given type is a floating point value. +*/ +bool dbusIsFloating(int type) @property { + return type == 'd'; +} + +/** + Params: + type = the type code of a type (first character in a type string) + Returns: true if the given type is an integer or a floating point value. +*/ +bool dbusIsNumeric(int type) @property { + return dbusIsIntegral(type) || dbusIsFloating(type); +} + unittest { import dunit.toolkit; @@ -219,16 +249,20 @@ unittest { typeSig!int().assertEqual("i"); typeSig!bool().assertEqual("b"); typeSig!string().assertEqual("s"); + typeSig!InterfaceName().assertEqual("s"); + typeSig!(immutable(InterfaceName))().assertEqual("s"); + typeSig!ObjectPath().assertEqual("o"); + typeSig!(immutable(ObjectPath))().assertEqual("o"); typeSig!(Variant!int)().assertEqual("v"); typeSig!FileDescriptor().assertEqual("h"); // enums - enum E : byte { + enum E : ubyte { a, b, c } - typeSig!E().assertEqual(typeSig!byte()); + typeSig!E().assertEqual(typeSig!ubyte()); enum U : string { One = "One", Two = "Two" @@ -267,7 +301,7 @@ unittest { // arrays typeSig!(int[]).assertEqual("ai"); typeSig!(Variant!int[]).assertEqual("av"); - typeSig!(Tuple!(byte)[][]).assertEqual("aa(y)"); + typeSig!(Tuple!(ubyte)[][]).assertEqual("aa(y)"); // dictionaries typeSig!(int[string]).assertEqual("a{si}"); typeSig!(DictionaryEntry!(string, int)[]).assertEqual("a{si}");