www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.conv.to!string(array), strange compile error

reply Relja <ljubobratovic.relja gmail.com> writes:
I've got this strange compile error using 
std.conv.to!string(double[3]) - or any static array type. It's 
called in toString override function of a template matrix class, 
I'm building as a D learning project.

Here is the toString method:
override string toString() const
{
     string outs;
     outs ~= "[";

     for (size_t i = 0; i < rows; ++i)
     {
         outs ~= this.opIndex(i, 0).to!string;
         for (size_t j = 1; j < cols; ++j)
         {
             outs~= ", " ~ this.opIndex(i, j).to!string;
         }
         if (i == rows - 1) // last row
             outs ~= "]";
         else
             outs ~= ";\n";
     }
     return outs;
}

And here is the opIndex (const version):
const(T) opIndex(ulong r, ulong c) const
in
{
     assert(r < _rows && c < _cols);
}
body
{
     return _begin[c*_strides[1] + r*_strides[0]];
}

Error occurs on lines with "this.opIndex().to!string", when 
template type is static array, e.g. double[3]:

source/matrix.d(248,30): Error: template std.conv.to cannot 
deduce function from argument types !(string)(const(double[3])), 
candidates are:
/usr/include/dlang/dmd/std/conv.d(293,1):        std.conv.to(T)
source/matrix.d(251,37): Error: template std.conv.to cannot 
deduce function from argument types !(string)(const(double[3])), 
candidates are:
/usr/include/dlang/dmd/std/conv.d(293,1):        std.conv.to(T)
source/main.d(17,2): Error: template instance 
matrix.Matrix!(double[3]) error instantiating

Lines 248 and 251 are the opIndex.to!string calls.


Anyhow, I've changed opIndex() calls in the toString with matrix 
array indexing implementation (_begin[row*col_stride + 
col*item_stride]), and the conversion works properly (as it 
should on any array type, and gives normal output). So I've made 
a little test with compile-time messages:

override string toString() const
{
     string outs;
     outs ~= "[";

     pragma(msg, typeof(_begin[0]).stringof);
     pragma(msg, typeof(this.opIndex(0, 0)).stringof);

     this._begin[0].to!string;
     (this.opIndex(0, 0)).to!string;

     .....

And here's the compile output:

const(double[3])
const(double[3])
source/matrix.d(250): Error: template std.conv.to cannot deduce 
function from argument types !(string)(const(double[3])), 
candidates are:
/usr/include/dlang/dmd/std/conv.d(293):        std.conv.to(T)

Line 250 is opIndex().to!string, line above compiles ok.

So even opIndex has the same return type as the array itself, 
to!string produces compile error on the opIndex.to!string call, 
and on the array.to!string does not. Could this be a bug in the 
compiler, or am I doing something wrong with the opIndex (or any 
other) method declaration?

System is x64 Manjaro linux(arch based distro), compiler is dmd 
v2.069 (up-to-date from AUR, as libphobos is), and I have no 
other D compilers installed. Im' not sure how to check exact 
version of the libphobos - if anyone can tell me how to check it, 
please tell me, and I'll post it.

Any other suggestion about above code is welcome!

Thanks,
Relja Ljubobratovic
Nov 14 2015
parent reply BBaz <b2.temp gmx.com> writes:
On Saturday, 14 November 2015 at 12:46:21 UTC, Relja wrote:
 I've got this strange compile error using 
 std.conv.to!string(double[3]) - or any static array type. It's 
 called in toString override function of a template matrix 
 class, I'm building as a D learning project.

 [...]
Maybe try to use a full slice after a 'static if' in case the template type is static array: -- { ElementType!T[] something; static if (isStaticArray!T) something = value[]; // slice to get a dyn array else something = value; // already a dyn array } -- and you work on something when it only works on dyn array. You can also slice the whole static array each time the problem occurrs
Nov 14 2015
parent reply Relja <ljubobratovic.relja gmail.com> writes:
On Saturday, 14 November 2015 at 12:55:52 UTC, BBaz wrote:
 On Saturday, 14 November 2015 at 12:46:21 UTC, Relja wrote:
 I've got this strange compile error using 
 std.conv.to!string(double[3]) - or any static array type. It's 
 called in toString override function of a template matrix 
 class, I'm building as a D learning project.

 [...]
Maybe try to use a full slice after a 'static if' in case the template type is static array: -- { ElementType!T[] something; static if (isStaticArray!T) something = value[]; // slice to get a dyn array else something = value; // already a dyn array } -- and you work on something when it only works on dyn array. You can also slice the whole static array each time the problem occurrs
I'm not sure if I understand you fully, but I think you misunderstood me - std.conv.to!string() works on a static array, when called directly on the array object, but gives the compile error when called on the returning object from a function. Here is simpler example, which shows the same compile error: import std.conv; float[3] getFloat3() { return [1, 2, 3]; } void main() { getFloat3().to!string; // does not compile (new float[3]).to!string; // compiles } On this example, compiler error message is the same as above: source/app.d(10,12): Error: template std.conv.to cannot deduce function from argument types !(string)(float[3]), candidates are: /usr/include/dlang/dmd/std/conv.d(293,1): std.conv.to(T) dmd failed with exit code 1. But then again, your suggestion got me to try this: getFloat3()[].to!string; This does compile! Can somebody elaborate, and tell us whats going on here? I feel that this does not solve my issue, but only makes me stay away from calling std.conv.to in this way. Thanks, Relja
Nov 14 2015
parent reply anonymous <anonymous example.com> writes:
On 14.11.2015 15:17, Relja wrote:
 - std.conv.to!string() works on a static array, when called directly on
 the array object, but gives the compile error when called on the
 returning object from a function.
[...]
 void main() {
      getFloat3().to!string; // does not compile
      (new float[3]).to!string; // compiles
 }
`new float[3]` is not a static array. Its type is not `float[3]`, it's `float[]`.
Nov 14 2015
parent reply Relja <ljubobratovic.relja gmail.com> writes:
On Saturday, 14 November 2015 at 14:30:06 UTC, anonymous wrote:
 On 14.11.2015 15:17, Relja wrote:
 - std.conv.to!string() works on a static array, when called 
 directly on
 the array object, but gives the compile error when called on 
 the
 returning object from a function.
[...]
 void main() {
      getFloat3().to!string; // does not compile
      (new float[3]).to!string; // compiles
 }
`new float[3]` is not a static array. Its type is not `float[3]`, it's `float[]`.
Oh, my bad, sorry. No mather, issue still stands: float[3] array; array.to!string; // compiles
Nov 14 2015
parent reply anonymous <anonymous example.com> writes:
On 14.11.2015 15:40, Relja wrote:
 float[3] array;
 array.to!string; // compiles
Alright, full test case: ---- import std.conv; float[3] getFloat3() { return [1, 2, 3]; } void main() { getFloat3().to!string; // does not compile float[3] a; a.to!string; // compiles } ---- Yeah, that's odd. Looking at std.conv, `to` is this [1]: ---- template to(T) { T to(A...)(A args) if (!isRawStaticArray!A) { return toImpl!T(args); } // Fix issue 6175 T to(S)(ref S arg) if (isRawStaticArray!S) { return toImpl!T(arg); } } ---- So static arrays are taken by reference. That explains why return values from functions are rejected: they're rvalues and can't be passed by reference. Apparently, that oddity is a fix for issue 6175 [2] which seems to be about `to` returning a slice of a local variable (which is bad). I don't know the details of the issue or the fix, but I've got a feeling that there must be a better fix. Taking the data by reference, but otherwise doing the same as before would mean that `to` simply slices a static array when asked to make a dynamic array of it, rather than copying the data. And indeed, that seems to be the case: ---- float[] f() { import std.conv; float[3] a = [1, 2, 3]; return a.to!(float[]); } void main() { auto a = f(); import std.stdio; writeln(a); /* prints garbage */ } ---- I don't know if that's considered acceptable. I'm not a fan. [1] https://github.com/D-Programming-Language/phobos/blob/48d57e36e74379291a52087fcd1aa0dc19ff9a70/std/conv.d#L293-L307 [2] https://issues.dlang.org/show_bug.cgi?id=6175
Nov 14 2015
parent Relja <ljubobratovic.relja gmail.com> writes:
On Saturday, 14 November 2015 at 18:52:54 UTC, anonymous wrote:
 On 14.11.2015 15:40, Relja wrote:
 [...]
Alright, full test case: ---- import std.conv; [...]
Great explanation! Thank you!
Nov 14 2015