www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Unittests, templates

reply bearophile <bearophileHUGS lycos.com> writes:
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
Oct 30 2008
parent reply "Bill Baxter" <wbaxter gmail.com> writes:
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
Oct 30 2008
parent bearophile <bearophileHUGS lycos.com> writes:
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
Oct 30 2008