www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - get parameters of a function

reply Alex <sascha.orlov gmail.com> writes:
Hi all,
I have a question about the Parameters trait from
https://dlang.org/phobos/std_traits.html#Parameters

The following code does not compile. Why?

import std.traits : Parameters;

void main()
{
	static assert(is(Parameters!S1 == Parameters!S2));
}

struct S1
{
	auto opCall() {}
	auto opCall(int i){}	
}

struct S2
{
	auto opCall(int i){}
	auto opCall() {}
}

So, of course, if I change the both opCall methods, e. g. in S1 
it will. But I'm wondering, how to use the trait in this case?

Is it mainly assumed to use it with functions without overloads?
Apr 28
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Friday, 28 April 2017 at 20:43:50 UTC, Alex wrote:
 Hi all,
 I have a question about the Parameters trait from
 https://dlang.org/phobos/std_traits.html#Parameters

 The following code does not compile. Why?
 Is it mainly assumed to use it with functions without overloads?
Rather, it is to be used on a concrete overload. If you want to check if two structs are callable with the same parameters, you'll need to iterate opCall overloads for both and look for a match, e.g. like this: void main() { foreach(o1; __traits(getOverloads, S1, "opCall")) { alias P1 = Parameters!o1; foreach(o2; __traits(getOverloads, S2, "opCall")) { alias P2 = Parameters!o2; static if (is(P1 == P2)) { pragma(msg, "Matching params: "~P1.stringof); } } } }
Apr 28
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 29 April 2017 at 05:58:35 UTC, Stanislav Blinov 
wrote:
 On Friday, 28 April 2017 at 20:43:50 UTC, Alex wrote:

 void main()
 {
     foreach(o1; __traits(getOverloads, S1, "opCall"))
     {
         alias P1 = Parameters!o1;
         foreach(o2; __traits(getOverloads, S2, "opCall"))
         {
             alias P2 = Parameters!o2;
             static if (is(P1 == P2))
             {
                 pragma(msg, "Matching params: "~P1.stringof);
             }
         }
     }
 }
The problem is another one: say I have something like this: import std.traits; struct A(alias T) if(isCallable!T) { auto opCall(U...)(U args) if(is(Parameters!T == U)) //if(__traits(compiles, T(args))) { return T(args); } } void main() { S s; A!s c; assert(c(4) == 42); } struct S { auto opCall(){ return 42; } auto opCall(int i){return 42; } } This doesn't work because of Parameters trait and the both opCalls defined. However, I see the point... If I take the compiles-trait, it works as expected: In this case the constrained is fulfilled, and with the second opCall commented out - it isn't. Thanks :)
Apr 28
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 29 April 2017 at 06:18:34 UTC, Alex wrote:

 The problem is another one: say I have something like this:

 import std.traits;

 struct A(alias T) if(isCallable!T)
 {	
 	auto opCall(U...)(U args) if(is(Parameters!T == U)) 
 //if(__traits(compiles, T(args)))
 	{
 		return T(args);
 	}
 }

 void main()
 {
 	S s;
 	A!s c;
 	assert(c(4) == 42);
 }

 struct S
 {
 	auto opCall(){ return 42; }
 	auto opCall(int i){return 42; }
 }

 This doesn't work because of Parameters trait and the both 
 opCalls defined.
 However, I see the point...
 If I take the compiles-trait, it works as expected:
 In this case the constrained is fulfilled, and with the second 
 opCall commented out - it isn't.

 Thanks :)
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))); // 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); } }
Apr 29
parent Alex <sascha.orlov gmail.com> writes:
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!
Apr 29