Thread overview
Issue with template function
Feb 06, 2015
Charles
Feb 06, 2015
Charles
Feb 06, 2015
FG
Feb 06, 2015
ketmar
Feb 07, 2015
Charles
Feb 07, 2015
Nicholas Wilson
Feb 07, 2015
Charles
Feb 08, 2015
Nicholas Wilson
February 06, 2015
I'm trying to create a template function that can take in any type of array and convert it to a ubyte array. I'm not concerned with endianness at the moment, but I ran into a roadblock when trying to do this with strings. It already works with ints, chars, etc.

Here's the relevant test code:

    module byteReader;

    public import std.system : Endian;

    ubyte[] toBytes(T)(T[] arr)
    {
        if (arr == null)
        {
            return null;
        }

        ubyte[] result = new ubyte[arr.length];

        foreach (key, val; arr)
        {
            result[key] = cast(ubyte) val;    // This is line 16
        }

        return result;
    }

    string readString(ubyte[] buffer, uint offset, uint length)
    {
        assert( buffer.length >= offset + length );

        char[] chars = new char[length];
        foreach(key, val; buffer[offset .. offset + length])
        {
            chars[key] = cast(char) val;
        }

        return cast(string)chars;

    }

    void main() {
        import std.stdio;
        readString(toBytes!char(['t','e','s','t']),0,4).writeln;
        readString(toBytes!string("test"),0,4).writeln;    // This is line 39
    }

Here's the output:
    byteReader.d(16): Error: cannot cast val of type string to type ubyte
    byteReader.d(39): Error: template instance byteReader.toBytes!string error instantiating
February 06, 2015
Can I not do this cast because it's immutable?
February 06, 2015
On 2015-02-06 at 18:09, Charles wrote:
>          readString(toBytes!char(['t','e','s','t']),0,4).writeln;
>          readString(toBytes!string("test"),0,4).writeln;    // This is line 39

That second line makes no sense (you didn't provide an array of strings).
Why toBytes!string("test") and not toBytes!char("test") or rather:
toBytes!(immutable char)("test") ?
February 06, 2015
On Fri, 06 Feb 2015 17:09:28 +0000, Charles wrote:

>          readString(toBytes!string("test"),0,4).writeln;

if you'll take a look into druntime sources, you'll find that string is just an alias to `immutable(char)[]`. so you actually doing thing:

  readString(toBytes!(immutable(char)[])("test"),0,4).writeln;

i bet that this is not what you meant. ;-)

February 07, 2015
On Friday, 6 February 2015 at 17:40:31 UTC, ketmar wrote:
> On Fri, 06 Feb 2015 17:09:28 +0000, Charles wrote:
>
>>          readString(toBytes!string("test"),0,4).writeln;
>
> if you'll take a look into druntime sources, you'll find that string is
> just an alias to `immutable(char)[]`. so you actually doing thing:
>
>   readString(toBytes!(immutable(char)[])("test"),0,4).writeln;
>
> i bet that this is not what you meant. ;-)

Thanks!
February 07, 2015
On Friday, 6 February 2015 at 17:09:29 UTC, Charles wrote:
> I'm trying to create a template function that can take in any type of array and convert it to a ubyte array. I'm not concerned with endianness at the moment, but I ran into a roadblock when trying to do this with strings. It already works with ints, chars, etc.
>
> Here's the relevant test code:
>
>     module byteReader;
>
>     public import std.system : Endian;
>
>     ubyte[] toBytes(T)(T[] arr)
>     {
>         if (arr == null)
>         {
>             return null;
>         }
>
>         ubyte[] result = new ubyte[arr.length];
>
>         foreach (key, val; arr)
>         {
>             result[key] = cast(ubyte) val;    // This is line 16
>         }
>
>         return result;
>     }
>
>     string readString(ubyte[] buffer, uint offset, uint length)
>     {
>         assert( buffer.length >= offset + length );
>
>         char[] chars = new char[length];
>         foreach(key, val; buffer[offset .. offset + length])
>         {
>             chars[key] = cast(char) val;
>         }
>
>         return cast(string)chars;
>
>     }
>
>     void main() {
>         import std.stdio;
>         readString(toBytes!char(['t','e','s','t']),0,4).writeln;
>         readString(toBytes!string("test"),0,4).writeln;    // This is line 39
>     }
>
> Here's the output:
>     byteReader.d(16): Error: cannot cast val of type string to type ubyte
>     byteReader.d(39): Error: template instance byteReader.toBytes!string error instantiating

Are you wanting to to convert each element in arr to a byte thus truncating and losing data (when T.sizeof != 1)?
as in
    toBytes([1,2,3, 42, 500 /*this will be truncated to 244 */]);// T  == int here
or are you wanting to convert each element to a ubyte array and then concatenate it to the result.
as is
     ubyte[] toBytes(T)(T[] arr)
     {
         ubyte[T.sizeof] buf;
         if (arr is null)
         {
             return null;
         }

         ubyte[] result = new ubyte[arr.length * T.sizeof];

         foreach (i, val; arr)
         {
             buf[] = cast(ubyte[T.sizeof])&(arr[i])[0 .. T.sizeof]
             result ~= buf;
         }

         return result;
     }
?
February 07, 2015
On Saturday, 7 February 2015 at 12:04:12 UTC, Nicholas Wilson wrote:
> Are you wanting to to convert each element in arr to a byte thus truncating and losing data (when T.sizeof != 1)?
> as in
>     toBytes([1,2,3, 42, 500 /*this will be truncated to 244 */]);// T  == int here
> or are you wanting to convert each element to a ubyte array and then concatenate it to the result.
> as is
>      ubyte[] toBytes(T)(T[] arr)
>      {
>          ubyte[T.sizeof] buf;
>          if (arr is null)
>          {
>              return null;
>          }
>
>          ubyte[] result = new ubyte[arr.length * T.sizeof];
>
>          foreach (i, val; arr)
>          {
>              buf[] = cast(ubyte[T.sizeof])&(arr[i])[0 .. T.sizeof]
>              result ~= buf;
>          }
>
>          return result;
>      }
> ?

The original code I was using was written in Java, and only had a method for strings. This is closer to what I wanted. My unit tests were just going back and forth with readString function, so I was completely missing this for other types. Nice catch!

There were a couple issues with your code so I've included the corrected version:

    ubyte[] toUbytes(T)(T[] arr)
    {
        if (arr is null)
        {
            return null;
        }

        ubyte[T.sizeof] buffer;
        ubyte[] result = new ubyte[arr.length * T.sizeof];

        foreach (i, val; arr)
        {
            buffer[] = cast(ubyte[T.sizeof])(&(arr[i]))[0 .. T.sizeof]; // Parenthesis and missing semicolon
            result[i * T.sizeof .. (i * T.sizeof) + T.sizeof] = buffer; // Specify appropriate slice for buffer to be inserted into
        }

        return result;
    }
February 08, 2015
> The original code I was using was written in Java, and only had a method for strings. This is closer to what I wanted. My unit tests were just going back and forth with readString function, so I was completely missing this for other types. Nice catch!
>
> There were a couple issues with your code so I've included the corrected version:
>

That's what I get for replying at 11pm =p
>     ubyte[] toUbytes(T)(T[] arr)
>     {
>         if (arr is null)
>         {
>             return null;
>         }
>
>         ubyte[T.sizeof] buffer;
>         ubyte[] result = new ubyte[arr.length * T.sizeof];
>
>         foreach (i, val; arr)
>         {
>             buffer[] = cast(ubyte[T.sizeof])(&(arr[i]))[0 .. T.sizeof]; // Parenthesis and missing semicolon
>             result[i * T.sizeof .. (i * T.sizeof) + T.sizeof] = buffer; // Specify appropriate slice for buffer to be inserted into
>         }
>
>         return result;
>     }
 thinking about it again this can be done in a single memcpy

     ubyte[] toUbytes(T)(T[] arr)
     {
         if (arr is null)
         {
             return null;
         }

         ubyte[] result = new ubyte[arr.length * T.sizeof];

        memcpy(result.ptr, arr.ptr , arr.length * T.sizeof);
        return result;
    }

and an asUbytes can be done as a cast

     ubyte[] toUbytes(T)(T[] arr)
     {
         if (arr is null)
         {
             return null;
         }
         return cast(ubyte[]) arr.ptr [0 .. arr.length * T.sizeof];
     }