On Saturday, 29 April 2017 at 08:15:06 UTC, Stanislav Blinov wrote:
Ah, that calls for something like a isCallableWith template. Pay extra care to how you pass parameters in that variadic opCall template though. Doing it like in the code above will pass everything by value, even though the original S.opCall might've expected a reference. To avoid that, auto ref + std.functional.forward can be used.
:)
The following code illustrates the difference.

import std.traits;
import std.functional : forward;

/**
  Evaluates true if `func` is callable with arguments `args`.
  Can only be used as a function template constraint.
*/
enum isCallableWith(alias func, args...) = is(typeof(func(forward!args)));

// That's cool! Much shorter, as I thought.


// Your original A
struct A(alias func) if (isCallable!func)
{
auto opCall(Args...)(Args args) if (__traits(compiles, func(args)))
    {
// all arguments are copied, any "ref/out" semantics are lost
        return func(args);
    }
}

// Modified to forward arguments
struct B(alias func) if (isCallable!func)
{
    // auto ref: preserve lvalues
auto opCall(Args...)(auto ref Args args) if (isCallableWith!(func, args))
    {
        // forward: preserve lvalues
        return func(forward!args);
    }
}

struct S
{
    auto opCall() { return 42; }
    auto opCall(int i) { return 42; }
    // Note: parameter passed by reference!
    auto opCall(out float f) { f = 42.0f; }
}

void main()
{
    S s;

    {
        A!s c;
        int i = 4;
        static assert(is(typeof(c(i))));
        static assert(is(typeof(c(4))));
        static assert(is(typeof(c())));

        assert(c(i) == 42);
        assert(c(4) == 42);
        assert(c() == 42);
        // Should pass but doesn't.
        //static assert(!is(typeof(c(3.5f))));
        float f;
        static assert(is(typeof(c(f))));
        c(f);
        // Will assert:
        assert(f == 42.0f);
    }

    {
        B!s c;
        int i = 4;
        static assert(is(typeof(c(i))));
        static assert(is(typeof(c(4))));
        static assert(is(typeof(c())));

        assert(c(i) == 42);
        assert(c(4) == 42);
        assert(c() == 42);
        // Passes
        static assert(!is(typeof(c(3.5f))));
        float f;
        static assert(is(typeof(c(f))));
        c(f);
        // Won't assert
        assert(f == 42.0f);
    }
}

Ha, that is exactly what I am after :)
Thanks a lot!

Reply via email to