Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does std.algorithm.fill not accept character arrays?

Tags:

d

If I try to use std.algorithm.fill(Range1, Range2)(Range1 range, Range2 filler), I keep getting the error message that no template match could be found. It looks like the compiler is trying to match with fill (Range, Value) rather than the other one.

auto test = new char[256];
fill(test, "abc".dup);

Is it not possible to fill a character array using fill?

The error

test.d(13): Error: template std.algorithm.fill(Range,Value) if (isForwardRange!(Range) && is(typeof(range.front = filler))) does not match any function template declaration

test.d(13): Error: template std.algorithm.fill(Range,Value) if (isForwardRange!(Range) && is(typeof(range.front = filler))) cannot deduce t emplate function from argument types !()(char[],char[])

like image 858
fwend Avatar asked Jun 19 '11 08:06

fwend


2 Answers

std.algorithm.fill takes a range. All string types are ranges of dchar. This is because they're all unicode. char is a UTF-8 code unit wchar is a UTF-16 code unit, and dchar is a UTF-32 code unit. Multiple code units make up a code point, which is a character. For UTF-8, a code point could be up to 6 code units. For UTF-16, it could be up to 2. For UTF-32, 1 code unit is always 1 code point, so a dchar is always guaranteed to be a valid character. char and wchar by themselves, however, are not guaranteed to be valid characters at all, and it's generally a bug if you see an individual char or wchar used. Multiple chars or wchars often must be combined to make up a single character. So, you can't deal with chars or wchars individually. That's why if you're iterating over a string of any kind with foreach, you should always give its type as dchar.

foreach(dchar c; str)
    ...

If you didn't specify dchar, then it would be whatever the character type of str is, which would invariably cause bugs unless str were an array of dchar, because you'd end up with code units (pieces of characters) instead of code points (whole characters). Only dchars can be dealt with individually.

It's because of all of this that all string types and character arrays are considered to be ranges of dchar, regardless of what their actual element types are. So, when you call popFront on a string, it could pop off a lot more than just one char or wchar. And if you call front, it may have to decode multiple chars or wchars to return the dchar that is the first character in the string. This means that you can't treat char or wchar arrays as being random access. The 4th character could be the 4th element, or it could be the 12th. And regardless of which index it starts at, it could be multiple code units long, so you can't just grab a random index in char or wchar array and expect it to be valid. So, arrays of char and wchar are not random access ranges. They're not output ranges either.

Think about it. Let's take the character '\U00010143' (𐅃) for instance. It has a code unit length of 4 for UTF-8 and 2 for UTF-16. You can't just fill any random char or wchar array with it. If it's a char array, and its length isn't a multiple of 4, then the last (𐅃) isn't going to fit. If it's a wchar array, and its length isn't a multiple of 2, then it'll have the same problem. So, it really doesn't work to use a function like fill on a char or wchar array.

Now, it'll work just fine with a dchar array. Because a UTF-32 code unit is guaranteed to be a code point, every character in a dchar array is one element, and it can be both a random access range and an output range. So, if you want to use a function like fill with character arrays, you need to use an array of dchar. You can use std.conv.to to convert it to the type of character array that you want after that, but you can't fill an array of char or wchar directly.

Arrays of char and wchar work great with algorithms which don't need output ranges or random access ranges, but they don't work with those. For those, you need arrays of dchar.

like image 53
Jonathan M Davis Avatar answered Oct 20 '22 13:10

Jonathan M Davis


Because typeof(test.front) == dchar, not char

It might be a bug, but it's because typeof(test.front) is dchar, not char, since it's interpreted as a UTF-8 string and it gives you the first Unicode character, rather than the first char.

But why is it interpreted that way?

One of your imports has probably either directly or indirectly imported a module like std.array (or something of that sort), whose front() function returns dchar for char[]. Try finding out which one it is, and instead of using that, do an import std.iterator instead; that one returns char instead of dchar.

like image 2
user541686 Avatar answered Oct 20 '22 12:10

user541686