Jump to page: 1 2
Thread overview
Check for presence of function
Mar 23, 2014
Steve Teale
Mar 23, 2014
Dicebot
Mar 23, 2014
Adam D. Ruppe
Mar 23, 2014
Dicebot
Mar 23, 2014
Steve Teale
Mar 23, 2014
Philippe Sigaud
Mar 23, 2014
Philippe Sigaud
Mar 23, 2014
Adam D. Ruppe
Mar 23, 2014
Adam D. Ruppe
Mar 23, 2014
Andrej Mitrovic
Mar 23, 2014
Philippe Sigaud
Mar 23, 2014
Andrej Mitrovic
March 23, 2014
What's the cool/idiomatic D way to test at compile time if a struct has a member function with a particular signature? Along the lines of:

struct Unrelated
{
   ...
}

template isSomething(T)
{
    enum bool isSomething = is(typeof(
    (inout int = 0)
    {
       T???; // has a function void doSomething(Unrelated* up);
       // etc
    }));
}

struct Thingie
{
   ...
}

static assert(isSomething!(Thingie));

Thanks
Steve
March 23, 2014
template isSomething(T)
{
    enum isSomething =
		is(typeof(__traits(getMember, T, "doSomething")) == function)
	 && is(typeof(&__traits(getMember, T, "doSomething")) : void function(Unrelated*));
}
March 23, 2014
On Sun, Mar 23, 2014 at 1:40 PM, Steve Teale <steve.teale@britseyeview.com> wrote:
> What's the cool/idiomatic D way to test at compile time if a struct has a member function with a particular signature? Along the lines of:
>
> struct Unrelated
> {
>    ...
> }
>
> template isSomething(T)
> {
>     enum bool isSomething = is(typeof(
>     (inout int = 0)
>     {
>        T???; // has a function void doSomething(Unrelated* up);
>        // etc
>     }));
> }
>
> struct Thingie
> {
>    ...
> }
>
> static assert(isSomething!(Thingie));

You can use __traits(compiles, /* some code */), like this:


struct Unrelated {}

template isSomething(T)
{
    enum bool isSomething = __traits(compiles,
        {
           Unrelated* up;
           T.init.doSomething(up);
        }
    );
}

struct Thingie
{
    void doSomething(Unrelated* up) {}
}

void main()
{
    pragma(msg, isSomething!Thingie); // true
    pragma(msg, isSomething!Unrelated); // false
    pragma(msg, isSomething!int); // false
}
March 23, 2014
Ideally, the alias part of the grammar could be extended and isSomething simplified as:

alias isSomething(T) = __traits(compiles,
        {
           Unrelated* up;
           T.init.doSomething(up);
        }
    );

But this is not accepted by the grammar right now, because of __traits()
March 23, 2014
On Sunday, 23 March 2014 at 12:57:36 UTC, Philippe Sigaud wrote:
> But this is not accepted by the grammar right now, because of __traits()

That's easy enough to work around with an alias helper:

alias helper(alias T) = T;


alias isSomething(T) = alias!(__traits(compiles,
        {
           Unrelated* up;
           T.init.doSomething(up);
        }
    ));
March 23, 2014
On Sunday, 23 March 2014 at 13:00:09 UTC, Adam D. Ruppe wrote:
> alias isSomething(T) = alias!(__traits(compiles,

oops that should say helper! not alias!
March 23, 2014
On Sunday, 23 March 2014 at 12:53:39 UTC, Dicebot wrote:
> template isSomething(T)
> {
>     enum isSomething =
> 		is(typeof(__traits(getMember, T, "doSomething")) == function)
> 	 && is(typeof(&__traits(getMember, T, "doSomething")) : void function(Unrelated*));
> }

That won't necessarily work in the presence of overloaded functions since getMember only gets one of them.

You could though do this:

template isSomething(T) {
    bool checker() {
        foreach(overload; __traits(getOverloads, T, "doSomething"))
                static if(is(typeof(overload) == void function(Unrelated*)))
                       return true;
        return false;
    }
    enum isSomething = checker();
}

getOverloads returns an empty tuple for non-functions so you don't even have to check that ahead of time.
March 23, 2014
On Sunday, 23 March 2014 at 13:03:07 UTC, Adam D. Ruppe wrote:
> That won't necessarily work in the presence of overloaded functions since getMember only gets one of them.

Yeah, forgot to add this part, will make end result a bit less pretty indeed.

Updated version with overloads will be more reliable than proposed duck-typing version as it won't false trigger in presence of "alias this" or opDispatch. Though Steve may actually want it to trigger, I don't know.
March 23, 2014
On 3/23/14, Philippe Sigaud <philippe.sigaud@gmail.com> wrote:
> But this is not accepted by the grammar right now, because of __traits()

Pretty sure it's because you're using 'alias' instead of 'enum'. This works:

-----
enum isSomething(T) = __traits(compiles,
        {
           int up;
           T.init.doSomething(up);
        }
    );

void main()
{
    static struct S { void doSomething(int); }
    static struct X { void doSomething(string); }
    static assert(isSomething!S);
    static assert(!isSomething!X);
}
-----
March 23, 2014
On Sunday, 23 March 2014 at 13:23:58 UTC, Dicebot wrote:
> On Sunday, 23 March 2014 at 13:03:07 UTC, Adam D. Ruppe wrote:
>> That won't necessarily work in the presence of overloaded functions since getMember only gets one of them.
>
> Yeah, forgot to add this part, will make end result a bit less pretty indeed.
>
> Updated version with overloads will be more reliable than proposed duck-typing version as it won't false trigger in presence of "alias this" or opDispatch. Though Steve may actually want it to trigger, I don't know.

DB,

In my particular question, I would not have wanted, or cared if alias this was triggered, but that was a narrow view.

My primary point is that there should be something in the wiki to help old-fashioned programmers like me to change from an OOP style that is not 100% necessary, to a struct based  style that should be quicker, but that allows the compiler to help me, or other contributors to the same project, to write add-on components that are at least checked at compile time.

So what I'm after really, is how do you do interfaces outside of OOP.

Std.range does that sort of thing in what seem to me to be very simple cases, but there is a lot of scope for different scenarios.

Thanks for your answer
Steve

« First   ‹ Prev
1 2