1 /**
2  * Named method and struct literal arguments
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.meta.args;
15 
16 import std.traits;
17 
18 // Inspired by
19 // http://forum.dlang.org/post/awjuoemsnmxbfgzhgkgx@forum.dlang.org
20 
21 /// Simulates named arguments for function calls.
22 /// Accepts arguments as lambdas (name => value) on the template parameter list,
23 /// and positional arguments on the runtime parameter list (see examples below).
24 template args(alias fun, dgs...)
25 if (is(typeof(fun) == function))
26 {
27 	auto args(PosArgs...)(auto ref PosArgs posArgs)
28 	{
29 		ParameterTypeTuple!fun args;
30 		enum names = ParameterIdentifierTuple!fun;
31 
32 		foreach (i, ref arg; posArgs)
33 			args[i] = posArgs[i];
34 		foreach (i, arg; ParameterDefaults!fun)
35 			static if (i >= posArgs.length)
36 				args[i] = ParameterDefaults!fun[i];
37 
38 		// anything works here, but use a custom type to avoid user errors
39 		static struct DummyType {}
40 
41 		foreach (dg; dgs)
42 		{
43 			alias fun = dg!DummyType;
44 			static if (is(FunctionTypeOf!fun PT == __parameters))
45 			{
46 				enum name = __traits(identifier, PT);
47 				foreach (i, argName; names)
48 					static if (name == argName)
49 						args[i] = fun(DummyType.init);
50 			}
51 			else
52 				static assert(false, "Failed to extract parameter name from " ~ fun.stringof);
53 		}
54 		return fun(args);
55 	}
56 }
57 
58 ///
59 unittest
60 {
61 	static int fun(int a=1, int b=2, int c=3, int d=4, int e=5)
62 	{
63 		return a+b+c+d+e;
64 	}
65 
66 	assert(args!(fun) == 15);
67 	assert(args!(fun, b=>3) == 16);
68 	assert(args!(fun, b=>3, d=>3) == 15);
69 }
70 
71 /// Mixing named and positional arguments
72 unittest
73 {
74 	static int fun(int a, int b=2, int c=3, int d=4, int e=5)
75 	{
76 		return a+b+c+d+e;
77 	}
78 
79 	assert(args!(fun)(1) == 15);
80 	assert(args!(fun, b=>3)(1) == 16);
81 }
82 
83 /// Simulates named arguments for struct literals.
84 template args(S, dgs...)
85 if (is(S == struct))
86 {
87 	@property S args()
88 	{
89 		S s;
90 
91 		// anything works here, but use a custom type to avoid user errors
92 		static struct DummyType {}
93 
94 		foreach (dg; dgs)
95 		{
96 			alias fun = dg!DummyType;
97 			static if (is(FunctionTypeOf!fun PT == __parameters))
98 			{
99 				enum name = __traits(identifier, PT);
100 				foreach (i, field; s.tupleof)
101 					static if (__traits(identifier, S.tupleof[i]) == name)
102 						s.tupleof[i] = fun(DummyType.init);
103 			}
104 			else
105 				static assert(false, "Failed to extract parameter name from " ~ fun.stringof);
106 		}
107 		return s;
108 	}
109 }
110 
111 unittest
112 {
113 	static struct S
114 	{
115 		int a = 1, b = 2, c = 3, d = 4, e = 5;
116 		@property int sum() { return a + b + c + d + e; }
117 	}
118 
119 	assert(args!(S).sum == 15);
120 	assert(args!(S, b=>3).sum == 16);
121 	assert(args!(S, b=>3, d=>3).sum == 15);
122 
123 	static assert(!is(typeof(args!(S, b=>b))));
124 }