www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Check for presence of function

reply "Steve Teale" <steve.teale britseyeview.com> writes:
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
Mar 23 2014
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
template isSomething(T)
{
     enum isSomething =
		is(typeof(__traits(getMember, T, "doSomething")) == function)
	 && is(typeof(&__traits(getMember, T, "doSomething")) : void 
function(Unrelated*));
}
Mar 23 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Mar 23 2014
parent reply "Dicebot" <public dicebot.lv> writes:
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.
Mar 23 2014
parent "Steve Teale" <steve.teale britseyeview.com> writes:
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
Mar 23 2014
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
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 }
Mar 23 2014
prev sibling next sibling parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
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()
Mar 23 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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); } ));
Mar 23 2014
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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!
Mar 23 2014
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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); } -----
Mar 23 2014
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sun, Mar 23, 2014 at 2:50 PM, Andrej Mitrovic
<andrej.mitrovich gmail.com> wrote:

 Pretty sure it's because you're using 'alias' instead of 'enum'. This works:
Oww! For years, D told me I couldn't use __traits in an easy way like this. I'll have to teach me out of it, and use enum :-) Damn, but using enum is quite natural too. Thanks Andrej. Now, if only __traits could be beautified somewhat... I mean: everyone is using it, it's time to make it more palatable.
Mar 23 2014
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/23/14, Philippe Sigaud <philippe.sigaud gmail.com> wrote:
 Now, if only __traits could be beautified somewhat... I mean: everyone
 is using it, it's time to make it more palatable.
That's what std.traits is for, to hide the __traits and is() uglyness.
Mar 23 2014