Generic collection/element function signatures in D2 versus D1

Jonathan M Davis jmdavisprog at gmail.com
Sat Sep 4 17:16:18 PDT 2010


On Saturday 04 September 2010 00:57:48 Nick Sabalausky wrote:
> In D1 I did this sort of thing a fair amount:
> 
> void foo(T)(T[] collection, T elem)
> {
>     // Blah, whatever
> }
> 
> Worked for any of the string types, worked for any array, or anything with
> the appropriate opIndexes, and for all I know there may be some improvement
> that could still be made. But of course, in D2 strings have that extra
> immutable part that mucks up the above for strings (and then there's
> ranges), so: Is there a typical generally-best way in D2 to declare a
> function signature for operating on collections and elements? I know it
> would involve using the standard range interfaces in the body and choosing
> the most restrictive range type that gets the job done, and I'm fine with
> all that, but is there a good example of a typical "best-practice"
> generic-function signature in D2?
> 
> -------------------------------
> Not sent from an iPhone.

Okay, here's a shot at it:

import std.algorithm;
import std.array;
import std.container;
import std.stdio;
import std.traits;

void foo(T, U)(T collection, U elem)
    if(is(U : Unqual!(typeof(collection[].front))))
{
    if(elem == collection.front)
        writefln("%s == %s", elem, collection.front);
    else
        writefln("%s != %s", elem, collection.front);
}

void bar(R, U)(R range, U elem)
    if(is(U : Unqual!(typeof(range.front))))
{
    if(elem == range.front)
        writefln("%s == %s", elem, range.front);
    else
        writefln("%s != %s", elem, range.front);
}

void main()
{
    dstring a = "hello world";
    foo(a, 'h');
    foo(a, 'g');
    bar(a, 'h');
    bar(a, 'g');

    writeln();
    Array!int b;
    b.insert(1);
    b.insert(2);
    b.insert(3);

    foo(b, 1);
    foo(b, 2);
    bar(b, 1);
    bar(b, 2);

    writeln();
    SList!int c;
    c.insert(3);
    c.insert(2);
    c.insert(1);

    foo(c, 1);
    foo(c, 2);
    bar(c, 1);
    bar(c, 2);

    writeln();
    auto d = find(c[], 2);

    bar(d, 1);
    bar(d, 2);
}


foo() takes a container type where you can get a range to the whole thing with a 
slice, and bar() works on ranges. However, it looks like all of the containers 
in std.container currently implement front(), so both foo() and bar() work on 
them. Of course, if you doing stuff that isn't on a range, then bar() wouldn't 
work, and foo() won't work on ranges. So, which you want probably depends on 
what you're trying to do. A function like bar will work on more but will be 
restricted to range-base operations only. And depending on what you do in a 
function like bar(), you might need more template constraints to constrain the 
type of range.

I haven't tried any of this with CTFE, but the function signatures should work 
just fine with it. Whether you can use CTFE should therefore be a question of 
what you try to do in the function.

In any case, I think that Unqual is the secret sauce that you're looking for 
here.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list