Most uses of the 'is expression' allows using a specifier to name the entity that matched. So, inserting ReturnedDelegate in your '== delegate' line gives the name ReturnedDelegate to the delegate.

That name can be used further:

template exT(T, alias nextGen)
    if (
        // Ensure nextGen is a callable entity
           isCallable!nextGen == true
        // Ensure nextGen returns a delegate
&& is(ReturnType!nextGen ReturnedDelegate == delegate) // <--
        // Ensure nextGen takes one argument
        && arity!nextGen == 1
        // Ensure that argument is of type T
        && is(ParameterTypeTuple!nextGen[0] == T)
        // Ensure the returned delegate returns type T
        && is(ReturnType!ReturnedDelegate == T)
        // Ensure the arity and parameter type of the delegate
                // && arity!ReturnedDelegate == 1
&& ParameterTypeTuple!ReturnedDelegate.length == 1 // <-- && is(ParameterTypeTuple!ReturnedDelegate[0] == int) // <--
        )
{
    // ...
}

However, since arity() takes an alias but ReturnedDelegate is a type, I could not make it work. So, I use the length of the parameter type tuple instead.

You can put all of those checks into a template and pass T and nextGen to it:

template exT(T, alias nextGen)
    if SatisfiesMyConditions!(T, nextGen)
{
    // ...
}

I haven't attempted writing it but it will be similar to the following syntax:

template canFlyAndLand(T)
{
    enum canFlyAndLand = is (typeof(
    {
        T object;
        object.prepare();  // should be preparable for flight
object.fly(1); // should be flyable for a certain distance
        object.land();     // should be landable
    }()));
}

I've copied that example from under the "Named constraints" section at

  http://ddili.org/ders/d.en/templates_more.html

Phobos implementation has many examples of that as well.

Ali

Thank you! This is excellent, and I'll make great use of your suggestions.

I would think you can check the delegate signature directly without decomposing it. Something like this:

is(ReturnType!nextGen == T delegate (int))

This is a very concise way to do what I want to do, but this check cares about other attributes of the function/delegate, i.e. if the function or delegate is designated as pure or nothrow and those don't show up in the check, it will fail. As in:

auto nextGen()
{
    T genFunc(int x) nothrow
    {
        return 0;
    }
    return &genFunc;
}

genFunc will fail the is() check because the is check cares whether the delegate is nothrow or not. Since I want folks to be able to use my algorithm without caring about the implementation details, I need to provide constraints for the parameter lists and return types of their functions, but no constraints on whatever attributes they want to slap onto their functions. I will keep this in my toolbox, but I'll have to take the more long-winded approach this time around.

Thank you both!

Reply via email to