From 05708986021259186726da973ab5d02f486ce168 Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Sat, 28 Oct 2017 14:57:44 +0200 Subject: [PATCH 1/6] Refactor DBusAny.to!T() --- source/ddbus/thin.d | 172 +++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 83 deletions(-) diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 8fd7e85..4009607 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -8,7 +8,7 @@ import ddbus.conv; import ddbus.exception : TypeMismatchException; import ddbus.util; -import std.meta : staticIndexOf; +import std.meta : ApplyRight, Filter, staticIndexOf; import std.string; import std.typecons; import std.exception; @@ -419,7 +419,7 @@ struct DBusAny { } /// If the value is an array of DictionaryEntries this will return a HashMap - DBusAny[DBusAny] toAA() { + deprecated("Please use to!(V[K])") DBusAny[DBusAny] toAA() { enforce(type == 'a' && signature && signature[0] == '{'); DBusAny[DBusAny] aa; @@ -451,8 +451,19 @@ struct DBusAny { } } - /// Converts a basic type, a tuple or an array to the D type with type checking. Tuples can get converted to an array too. - T to(T)() { + /++ + Converts a basic type, a tuple or an array to the D type with type checking. + + Tuples can be converted to an array of DBusAny, but not to any other array. + +/ + T to(T)() @property const pure { + // Just use `get` if possible + static if (canDBus!T && __traits(compiles, get!T)) { + if (this.typeSig == .typeSig!T) + return get!T; + } + + // If we get here, we need some type conversion static if (is(T == Variant!R, R)) { static if (is(R == DBusAny)) { auto v = to!R; @@ -463,92 +474,87 @@ struct DBusAny { } } else static if (is(T == DBusAny)) { return this; - } else static if (isIntegral!T || isFloatingPoint!T) { - switch (type) { - case typeCode!byte: - return cast(T) int8; - case typeCode!short: - return cast(T) int16; - case typeCode!ushort: - return cast(T) uint16; - case typeCode!int: - return cast(T) int32; - case typeCode!uint: - return cast(T) uint32; - case typeCode!long: - return cast(T) int64; - case typeCode!ulong: - return cast(T) uint64; - case typeCode!double: - return cast(T) float64; - default: - throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); - } - } else static if (is(T == bool)) { - if (type == 'b') { - return boolean; - } else { - throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); - } - } else static if (isSomeString!T) { - if (type == 's') { - return str.to!T; - } else if (type == 'o') { - return obj.toString(); - } else { - throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); - } - } else static if (is(T == ObjectPath)) { - if (type == 'o') { - return obj; - } else { - throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); - } - } else static if (isDynamicArray!T) { - if (type != 'a' && type != 'r') { - throw new Exception("Can't convert type " ~ cast(char) type ~ " to an array"); - } + } else { + // In here are all static if blocks that may fall through to the throw + // statement at the bottom of this block. - T ret; - if (signature == ['y']) { - static if (isIntegral!(ElementType!T)) { - foreach (elem; binaryData) { - ret ~= elem.to!(ElementType!T); + static if (is(T == DictionaryEntry!(K, V), K, V)) { + if (type == 'e') { + static if (is(T == typeof(entry))) { + return entry; + } else { + return DictionaryEntry(entry.key.to!K, entry.value.to!V); } } + } else static if (isAssociativeArray!T) { + if (type == 'a' && (!array.length || array[0].type == 'e')) { + alias K = Unqual!(KeyType!T); + alias V = Unqual!(ValueType!T); + V[K] ret; + + foreach (pair; array) { + assert(pair.type == 'e'); + ret[pair.entry.key.to!K] = pair.entry.value.to!V; + } + + return cast(T) ret; + } + } else static if (isDynamicArray!T && !isSomeString!T) { + alias E = Unqual!(ElementType!T); + + if (typeSig == "ay") { + static if (is(E == ubyte) || is(E == byte)) { + return cast(T) binaryData.dup; + } else { + T ret; + ret.length = binaryData.length; + + foreach (i, b; binaryData) + ret[i] = b; + + return ret; + } + } else if (type == 'a' || (type == 'r' && is(E == DBusAny))) { + E[] ret; + ret.length = array.length; + + foreach (i, elem; array) + ret[i] = elem.to!(ElementType!T); + + return cast(T) ret; + } + } else static if (isTuple!T) { + if (type == 'r') { + T ret; + + foreach (i, T; ret.Types) + ret[i] = tuple[i].to!T; + + return ret; + } } else { - foreach (elem; array) { - ret ~= elem.to!(ElementType!T); + alias isPreciselyConvertible = ApplyRight!(isImplicitlyConvertible, T); + + template isUnpreciselyConvertible(S) { + enum isUnpreciselyConvertible = !isPreciselyConvertible!S + && __traits(compiles, get!S.to!T); + } + + // Try to be precise + foreach (B; Filter!(isPreciselyConvertible, BasicTypes)) { + if (type == typeCode!B) + return get!B; + } + + // Try to convert + foreach (B; Filter!(isUnpreciselyConvertible, BasicTypes)) { + if (type == typeCode!B) + return get!B.to!T; } } - return ret; - } else static if (isTuple!T) { - if (type != 'r') { - throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); - } - - T ret; - enforce(ret.Types.length == tuple.length, "Tuple length mismatch"); - foreach (index, T; ret.Types) { - ret[index] = tuple[index].to!T; - } - - return ret; - } else static if (isAssociativeArray!T) { - if (type != 'a' || !signature || signature[0] != '{') { - throw new Exception("Can't convert type " ~ cast(char) type ~ " to " ~ T.stringof); - } - - T ret; - foreach (pair; array) { - enforce(pair.type == 'e'); - ret[pair.entry.key.to!(KeyType!T)] = pair.entry.value.to!(ValueType!T); - } - - return ret; - } else { - static assert(false, "Can't convert variant to " ~ T.stringof); + throw new ConvException("Cannot convert from DBus type '" ~ this.typeSig ~ "' to " + ~ T.stringof); } } From c015f0ce15b025201bd5ffc30d55ab020f45a4f7 Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Sat, 4 Nov 2017 12:57:55 +0100 Subject: [PATCH 2/6] Improved documentation comments - Reformat - Added warning against usage of direct DBusAny constructor in user code --- source/ddbus/thin.d | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 4009607..0cc9707 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -175,15 +175,24 @@ struct DBusAny { ubyte[] binaryData; } - /// Manually creates a DBusAny object using a type, signature and implicit specifier. + /++ + Manually creates a DBusAny object using a type, signature and explicit + variant specifier. + + Direct use of this constructor from user code should be avoided. + +/ this(int type, string signature, bool explicit) { this.type = type; this.signature = signature; this.explicitVariant = explicit; } - /// Automatically creates a DBusAny object with fitting parameters from a D type or Variant!T. - /// Pass a `Variant!T` to make this an explicit variant. + /++ + Automatically creates a DBusAny object with fitting parameters from a D + type or Variant!T. + + Pass a `Variant!T` to make this an explicit variant. + +/ this(T)(T value) { static if (is(T == byte) || is(T == ubyte)) { this(typeCode!byte, null, false); From 9b408732ec59d4a1399e1c6dfeb3c10e25d75bdb Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Sat, 4 Nov 2017 13:01:02 +0100 Subject: [PATCH 3/6] Added struct support to DBusAny --- source/ddbus/thin.d | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 0cc9707..64b82f2 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -3,6 +3,7 @@ module ddbus.thin; import core.time : Duration; +import ddbus.attributes : isAllowedField; import ddbus.c_lib; import ddbus.conv; import ddbus.exception : TypeMismatchException; @@ -301,6 +302,25 @@ struct DBusAny { } } + this.signature ~= ')'; + } else static if (is(T == struct) && canDBus!T) { + this.type = 'r'; + this.signature = ['(']; + this.explicitVariant = false; + foreach (index, R; Fields!T) { + static if (isAllowedField!(value.tupleof[index])) { + auto var = DBusAny(value.tupleof[index]); + tuple ~= var; + if (var.explicitVariant) + this.signature ~= 'v'; + else { + if (var.type != 'r') + this.signature ~= cast(char) var.type; + if (var.type == 'a' || var.type == 'r') + this.signature ~= var.signature; + } + } + } this.signature ~= ')'; } else static if (isAssociativeArray!T) { this(value.byDictionaryEntries); @@ -539,6 +559,19 @@ struct DBusAny { foreach (i, T; ret.Types) ret[i] = tuple[i].to!T; + return ret; + } + } else static if (is(T == struct) && canDBus!T) { + if (type == 'r') { + T ret; + size_t j; + + foreach (i, F; Fields!T) { + static if (isAllowedField!(ret.tupleof[i])) { + ret.tupleof[i] = tuple[j++].to!F; + } + } + return ret; } } else { @@ -857,14 +890,16 @@ unittest { enum testStruct = S3(variant(5), "blah", S1(-7, 63.5, "test"), S2(84, -123, 78, 432, &dummy), 16); - msg.build(testStruct); - // Non-marshaled fields should appear as freshly initialized enum expectedResult = S3(variant(5), "blah", S1(int.init, 63.5, "test"), S2(int.init, int.init, 78, 432, null), uint.init); + // Test struct conversion in building/reading messages + msg.build(testStruct); msg.read!S3().assertEqual(expectedResult); + // Test struct conversion in DBusAny + DBusAny(testStruct).to!S3.assertEqual(expectedResult); } Connection connectToBus(DBusBusType bus = DBusBusType.DBUS_BUS_SESSION) { From 238db949163ee620421e9031139f6b0b7204c854 Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Sat, 25 Nov 2017 16:22:06 +0100 Subject: [PATCH 4/6] Update unittest --- source/ddbus/thin.d | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 64b82f2..1d3abbf 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -639,17 +639,6 @@ unittest { void test(T)(T value, DBusAny b) { assertEqual(DBusAny(value), b); - - static if (is(T == Variant!R, R)) { - static if (__traits(compiles, b.get!R)) { - assertEqual(b.get!R, value.data); - } - } else { - static if (__traits(compiles, b.get!T)) { - assertEqual(b.get!T, value); - } - } - assertEqual(b.to!T, value); b.toString(); } @@ -661,7 +650,10 @@ unittest { 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(cast(ubyte[])[1, 2, 3], set!"binaryData"(DBusAny('a', ['y'], false), cast(ubyte[])[1, 2, 3])); @@ -672,7 +664,11 @@ unittest { test(variant(cast(uint) 184), set!"uint32"(DBusAny('u', null, true), cast(uint) 184)); test(variant(cast(long) 184), set!"int64"(DBusAny('x', null, true), cast(long) 184)); test(variant(cast(ulong) 184), set!"uint64"(DBusAny('t', null, true), cast(ulong) 184)); + test(variant(1.84), set!"float64"(DBusAny('d', null, true), 1.84)); test(variant(true), set!"boolean"(DBusAny('b', null, true), true)); + test(variant("abc"), set!"str"(DBusAny('s', null, true), "abc")); + test(variant(ObjectPath("/foo/Bar")), set!"obj"(DBusAny('o', null, true), + ObjectPath("/foo/Bar"))); test(variant(cast(ubyte[])[1, 2, 3]), set!"binaryData"(DBusAny('a', ['y'], true), cast(ubyte[])[1, 2, 3])); From 0841ac69de0627537884b5ca5948887ff2f37a2a Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Sat, 9 Dec 2017 16:33:44 +0100 Subject: [PATCH 5/6] Also use `get` in array conversion --- source/ddbus/thin.d | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 1d3abbf..1a3f9f4 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -532,32 +532,22 @@ struct DBusAny { alias E = Unqual!(ElementType!T); if (typeSig == "ay") { + auto data = get!(const(ubyte)[]); static if (is(E == ubyte) || is(E == byte)) { - return cast(T) binaryData.dup; + return cast(T) data.dup; } else { - T ret; - ret.length = binaryData.length; - - foreach (i, b; binaryData) - ret[i] = b; - - return ret; + return cast(T) data.map!(elem => elem.to!E).array; } } else if (type == 'a' || (type == 'r' && is(E == DBusAny))) { - E[] ret; - ret.length = array.length; - - foreach (i, elem; array) - ret[i] = elem.to!(ElementType!T); - - return cast(T) ret; + return cast(T) get!(const(DBusAny)[]).map!(elem => elem.to!E).array; } } else static if (isTuple!T) { if (type == 'r') { T ret; - foreach (i, T; ret.Types) + foreach (i, T; ret.Types) { ret[i] = tuple[i].to!T; + } return ret; } From 9c34742d966abcdf39ed6e5a141ddfac5e066aaf Mon Sep 17 00:00:00 2001 From: "Harry T. Vennik" Date: Sat, 9 Dec 2017 16:34:31 +0100 Subject: [PATCH 6/6] Fix bug in `get` for arrays Fixes #34 --- source/ddbus/thin.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/ddbus/thin.d b/source/ddbus/thin.d index 1a3f9f4..e684ec3 100644 --- a/source/ddbus/thin.d +++ b/source/ddbus/thin.d @@ -432,7 +432,7 @@ struct DBusAny { if (is(T == const(DBusAny)[])) { enforce((type == 'a' && signature != "y") || type == 'r', new TypeMismatchException( "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.", - typeCode!T, type)); + 'a', type)); return array; } @@ -442,7 +442,7 @@ struct DBusAny { if (is(T == const(ubyte)[])) { enforce(type == 'a' && signature == "y", new TypeMismatchException( "Cannot get a " ~ T.stringof ~ " from a DBusAny with" ~ " a value of DBus type '" ~ this.typeSig ~ "'.", - typeCode!T, type)); + 'a', type)); return binaryData; }