TDPL: Overloading template functions

Andrej Mitrovic andrej.mitrovich at gmail.com
Sat Jul 31 08:24:14 PDT 2010


I think there really is some bug here. I tried to use your template helper
function, and it seems the order in which I put two constraints in a
template signature changes the way dmd compiles the code. Consider this:

import std.stdio;

void main()
{
}

template areComparable(T1, T2)
{
   enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
template trick
}

T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool) &&
    areComparable!(T, E))
{
    while (haystack.length > 0 && haystack[0] != needle) {
        haystack = haystack[1 .. $];
    }
    return haystack;
}

T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (is(typeof(longer[0 .. 1] == shorter) : bool) && (areComparable!(T1,
T2)))

{
    while (longer.length >= shorter.length) {
        if (longer[0 .. shorter.length] == shorter)
            break;
        longer = longer[1 .. $];
    }
    return longer;
}

unittest {
    double[] d = [1.5, 2.4];
    assert(find(d, 1.0) == null);
    assert(find(d, 1.5) == d);
    string[] s = ["one", "two"];
    assert(find(s, "two") == ["two"]);
}

This will give out the error "incompatible types for comparing of string and
immutable(char)". But if I reverse the order of the constraints in the
second template, like so:

import std.stdio;

void main()
{
}

template areComparable(T1, T2)
{
   enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
template trick
}

T[] find(T, E)(T[] haystack, E needle)
    if (is(typeof(haystack[0] != needle) == bool) &&
    areComparable!(T, E))
{
    while (haystack.length > 0 && haystack[0] != needle) {
        haystack = haystack[1 .. $];
    }
    return haystack;
}


T1[] find(T1, T2)(T1[] longer, T2[] shorter)
    if (areComparable!(T1, T2) && is(typeof(longer[0 .. 1] == shorter) :
bool))
{
    while (longer.length >= shorter.length) {
        if (longer[0 .. shorter.length] == shorter)
            break;
        longer = longer[1 .. $];
    }
    return longer;
}

unittest {
    double[] d = [1.5, 2.4];
    assert(find(d, 1.0) == null);
    assert(find(d, 1.5) == d);
    string[] s = ["one", "two"];
    assert(find(s, "two") == ["two"]);
}

Then this happily compiles. All of the asserts will call the first templated
function.


On Sat, Jul 31, 2010 at 12:04 AM, Philippe Sigaud <philippe.sigaud at gmail.com
> wrote:

> First,  trying a copy-paste of your code gives me an error in object.di
> (DMD 2.047 on Windows)
>
> C:\dmd\windows\bin\..\..\src\druntime\import\object.di|487|Error:
> incompatible types for ((a) != (a2[i])): 'string' and 'immutable(char)'|
> ||=== Build finished: 1 errors, 0 warnings ===|
>
> And Code::Blocks jumps me to object.di:
>
> bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
> {
>     if (a1.length != a2.length)
>  return false;
>     foreach(i, a; a1)
>     { if (a != a2[i]) // <-- line 487. Uh oh, trying to compare strings
> and chars
>     return false;
>     }
>     return true;
> }
>
> I had to modify it:
>
> bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2)
>  if (is(typeof(T1.init != T2.init))) // Are T1 and T2 comparable? If not,
> do not bother to compile
> {
>    if (a1.length != a2.length) return false;
>     foreach(i, a; a1)
>     { if (a != a2[i])
>         return false;
>     }
>     return true;
> }
>
> Another possibility would have it return false, but in that case, array
> comparisons always compile and we lose the ability to use them as test for
> instantiating a template.
>
> I guess _ArrayEq is some internal called by the constraint, while doing the
> comparison. Saving object.di and recompiling everything allows the templates
> constraints to function and gives me a call to the first find.
>
> Without the '&& false', it still calls the first find... because it still
> cannot instantiate the second template:
>
> The first find can be instantiated with T = immutable(char)[] and E =
> immutable(char)[], there is nothing stopping it: T and E are the same type,
> so they are trivially comparable. "two" is a valid target value for ["one",
> "two"].
> The second find would have T1 = immutable(char)[] and T2 = immutable(char)
> and those do not compare.
>
>
> testing it, find("one two three", "two") correctly calls the second version
> and returns "two three".
>
> So, maybe you just found a bug in object.di._ArrayEq. I'll post on the main
> list. Anyway, for me, it seems to work, if I understand correctly what
> you're doing.
>
> Btw, if you find yourself using the same constraint regularly, you can
> abstract it away in another template that returns (er, becomes, rather) a
> boolean:
>
> template areComparable(T1, T2)
> {
>    enum bool areComparable = is(typeof(T1.init == T2.init)); // eponymous
> template trick
> }
>
> And then:
>
> T[] find(T, E)(T[] haystack, E needle)
>     if (areComparable!(T, E))
> {
> }
>
>
> T[] find(T, E)(T[] longer, E[] shorter)
>     if (areComparable!(T, E))
>
> {
> }
>
> Which makes for cleaner code. I renamed T1 and T2 to T and E, so showcase
> the difference between the two.
>
>
> Philippe
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20100731/8d9fe0d4/attachment.html>


More information about the Digitalmars-d mailing list