Thread overview
Unittests, templates
Oct 31, 2008
bearophile
Oct 31, 2008
Bill Baxter
Oct 31, 2008
bearophile
October 31, 2008
I like to test all my functions, are there ways to unit test nested functions?

void foo() {
    int add(int x, int y) {
        return x + y;
    }

    unittest { // found 'unittest' instead of statement
        assert(add(10, 20) == 30);
    }
}

unittest {
    assert(foo.add(10, 20) == 30); // Error: no property 'add' for type 'void'
}

void main() {}

If currently there's no way, I think it can be good to add to the language some way to test them.

----------------------

This is a question regarding templates. Let's say I have a simple struct template that is specified by an integer:

struct S(int N) {
    int[N] a;
}

Now I'd like to write a function that takes a S, and for example prints the integers of S.a separated by a space (I don't care of the extra space at the end).

This is easy to to in Haskell because it has type classes, but it's less easy to do in D (I don't know if type classes can be introduced into the D type system):
http://en.wikipedia.org/wiki/Type_class

This is a simple solution, but it compiles only if N == 2, so I'd like something more general:

void printS1(S!(2) s) {
    foreach (el; s.a)
        put(el, " ");
    putr();
}

This is general, a function template, but it requires you to specify N:

void printS2(int N)(S!(N) s) {
    foreach (el; s.a)
        put(el, " ");
    putr();
}

You have to call it for example like this:
printS2!(s.a.length)(s);


The following way ignores S, and just looks if the given type T has the requires qualities:

void printS3(T)(T s) {
    static assert(is(T == struct) && (T.tupleof.length == 1) &&
                  is(typeof(T.a)) && is(ArrayType1!(typeof(T.a)) == int)
                 );
    foreach (el; s.a)
        put(el, " ");
    putr();
}

(Where ArrayType1 gives the type of the items of T.a). printS3 isn't strict enough, because it accepts all typedef-ined types as long as they have that qualities. And if you change S a little, you have to change lot of code.

The following doesn't work, I don't know why:

void printSX(T)(T s) {
    static assert ( is(T == S) );
...


So this is more strict, but requires more RAM during compilation, and it doesn't accept N > 13:

void printS4(T)(T s) {
    //static assert (is(T == S), "printS4: err"); // doesn't work
    static assert (IsType!(T, S!(0), S!(1), S!(2), S!(3), S!(4), S!(5), S!(6),
                              S!(7), S!(8), S!(9), S!(10), S!(11), S!(12), S!(13)
                          ), "printS4: err");
    foreach (el; s.a)
        put(el, " ");
    putr();
}


Of course that can be generalized, up to N =~ 2930 but it requires lot of RAM and some compilation time, so this can't be the way to go:

template IsTN(alias TN, T, int N=0) {
    static if (N >= 2900 || N < 0) // 2900 is an arbitrary high value
        const bool IsTN = false;
    else static if ( is(T == TN!(N)) )
        const bool IsTN = true;
    else
        const bool IsTN = IsTN!(TN, T, N+1);
}

void printS5(T)(T s) {
    static assert (IsTN!(S, T), "printS5: err");
    foreach (el; s.a)
        put(el, " ");
    putr();
}

To reduce the RAM used I have tried to use a CT function, but it doesn't work:

bool isTN(alias TN, T)() { // doesn't work
    int i = 0;
    while ( is(T == TN!(i)) ) {}
    return true;
}

void printS6(T)(T s) {
    static assert (isTN!(S, T)(), "printS6: err");
    foreach (el; s.a)
        put(el, " ");
    putr();
}


I have tried with a recursive CT function, but it doesn't work still (maybe there are ways to make it run):

bool isTN2(alias TN, T)(int n=0) {
    if (n >= 2900 || n < 0)
        return false;
    else if ( is(T == TN!(n)) )
        return true;
    else
        return IsTN2!(TN, T)(n+1);
}

void printS8(T)(T s) {
    static assert (isTN2!(S, T)(), "printS6: err");
    foreach (el; s.a)
        put(el, " ");
    putr();
}


In the end changing totally approch it works and uses little RAM, but is this a good solution?

void printS7(T)(T s) {
    static assert (S.stringof == "S(int N)", "printS7: err");
    foreach (el; s.a)
        put(el, " ");
    putr();
}

Bye,
bearophile
October 31, 2008
On Fri, Oct 31, 2008 at 11:57 AM, bearophile <bearophileHUGS@lycos.com> wrote:
> This is a question regarding templates. Let's say I have a simple struct template that is specified by an integer:
>
> struct S(int N) {
>    int[N] a;
> }
>
> Now I'd like to write a function that takes a S, and for example prints the integers of S.a separated by a space (I don't care of the extra space at the end).
>
> This is easy to to in Haskell because it has type classes, but it's less easy to do in D (I don't know if type classes can be introduced into the D type system):
> http://en.wikipedia.org/wiki/Type_class
>
> This is a simple solution, but it compiles only if N == 2, so I'd like something more general:
>
> void printS1(S!(2) s) {
>    foreach (el; s.a)
>        put(el, " ");
>    putr();
> }
>
> This is general, a function template, but it requires you to specify N:
>
> void printS2(int N)(S!(N) s) {
>    foreach (el; s.a)
>        put(el, " ");
>    putr();
> }
>
> You have to call it for example like this:
> printS2!(s.a.length)(s);
>

I think this one should work.  IFTI should be able to tease apart S to match the int.  If it doesn't work, it seems like a bug to me. Actually I seem to remember a bug like this in the DB, but I couldn't find it just now.  I was also thinking it was marked fixed.

--bb
October 31, 2008
Bill Baxter:
> I think this one should work.  IFTI should be able to tease apart S to match the int.

Trying it again it works! :-)
Thank you.

Bye,
bearophile