Search code examples
templatesdintrospection

How to get the name of a function alias in a D template?


I have a function template that accepts a function as an argument. I'd like to be able to get the name of the function argument from within the template. A complete example is below. See the line marked "COMPILE ERROR" -- I've tried to do a variety of things similar to this but I keep getting the same error, "function throws.thrower (int n) is not callable".

import std.array;
import std.conv;
import std.format;
import std.stdio;

int thrower(int n)
{
    if(n > 5)
        throw new core.exception.RangeError("too big");
    return n * 2;
}

int thrower2(int x, int y)
{
    int product = x * y;
    if(product > 25)
        throw new core.exception.RangeError("too big");
    return product;
}

void assertThrows(alias fun, E, T...)(T t)
{
    try
    {
        fun(t);

        auto writer = appender!string();
        formattedWrite(writer,
                       "Expected %s to throw %s, but it did not",
// throws.d(32): Error: function throws.thrower (int n) is not callable using argument types ()

                       //fun.stringof, // <<-- COMPILE ERROR
                       "?",
                       E.stringof);

        throw new core.exception.AssertError(writer.data);
    }
    catch(E ex)
    {
        // Success - we got the expected exception - do nothing.
    }
    // We don't catch any other exceptions -- if these occur they will
    // cause a failure directly, or be handled by other test code that
    // may be expecting the exception. Either way we don't want to
    // interfere.
}

int main()
{
    assert(thrower(5) == 10);
    assertThrows!(thrower, core.exception.RangeError)(6);
    assertThrows!(thrower2, core.exception.RangeError)(9, 9);
    assertThrows!(thrower2, core.exception.RangeError)(1, 1); // Should fail

    return 0;
}

Solution

  • The thing I was looking for is std.traits.fullyQualifiedName; changing this line fixes the compile error and gives the desired output.

        formattedWrite(writer,
                       "Expected %s to throw %s, but it did not",
                       fullyQualifiedName!fun,
                       E.stringof);
    

    This gives output for the program in the question:

    core.exception.AssertError@throws.d(37): Expected throws.thrower2 to throw RangeError, but it did not
    

    which is what I am looking for.