www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - My template tuple code does not compile

reply Victor Porton <porton narod.ru> writes:
Compilation of unittest at the bottom of this file fails with an 
error. What is my error?

https://github.com/vporton/struct-params-dlang/blob/c32cfde60dbb03cb80a4a8aeb8185f5c86705790/source/struct_params.d

It is very important both for this useful little D project and my 
bigger research project. Please help.

source/struct_params.d(74,30): Error: found `,` when expecting 
`.` following int
source/struct_params.d(74,32): Error: found `"x"` when expecting 
identifier following `int`.
source/struct_params.d(74,44): Error: found `,` when expecting 
`.` following float
source/struct_params.d(74,46): Error: found `"y"` when expecting 
identifier following `float`.
Feb 26
parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 26 February 2019 at 21:43:31 UTC, Victor Porton wrote:
 Compilation of unittest at the bottom of this file fails with 
 an error. What is my error?
I cannot tell you, why exactly you get these error messages. I can explain you the probable cause of the errors. I have not tested anything of what I tell you, as it is very vague. You have the line ProviderParams("S", ((int, "x"), (float, "y"))); Why do you even expect it to compile? First, ProviderParams is a mixin template, so you'd need to instantiate it with the ! operator. Then, the expressions (int, "x") and so on do not make sense in D. You could have compile-time tuples (using AliasSeq from std.meta) containing types and other compile-time known stuff, but it wouldn't help directly. You'd use the mixin template like this: mixin ProviderParams!("S", int, "x", float, "y"); ^-- necessary ^-- necessary Grouping arguments could be done, but from my experience, it does not buy you anything; rather it makes it worse. Better just deal with heterogeneous stuff just like std.typecons.Tuple does. You should definitely comment your code. Explain what you intend to do. It helps people helping you.
Feb 26
parent reply Victor Porton <porton narod.ru> writes:
On Tuesday, 26 February 2019 at 22:51:15 UTC, Q. Schroll wrote:
 On Tuesday, 26 February 2019 at 21:43:31 UTC, Victor Porton 
 wrote:
 Compilation of unittest at the bottom of this file fails with 
 an error. What is my error?
...
 You have the line

    ProviderParams("S", ((int, "x"), (float, "y")));

 Why do you even expect it to compile?
 First, ProviderParams is a mixin template, so you'd need to 
 instantiate it with the ! operator. Then, the expressions (int, 
 "x") and so on do not make sense in D. You could have 
 compile-time tuples (using AliasSeq from std.meta) containing 
 types and other compile-time known stuff, but it wouldn't help 
 directly.
 You'd use the mixin template like this:

    mixin ProviderParams!("S", int, "x", float, "y");
    ^-- necessary       ^-- necessary

 Grouping arguments could be done, but from my experience, it 
 does not buy you anything; rather it makes it worse. Better 
 just deal with heterogeneous stuff just like std.typecons.Tuple 
 does.

 You should definitely comment your code. Explain what you 
 intend to do. It helps people helping you.
After fixing the error you pointed me, it does not work too: mixin ProviderParams!("S", ((int, "x"), (float, "y"))); Also: Can I nevertheless group arguments?
Feb 26
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Feb 26, 2019 at 10:56:37PM +0000, Victor Porton via Digitalmars-d-learn
wrote:
[...]
 After fixing the error you pointed me, it does not work too:
 
 mixin ProviderParams!("S", ((int, "x"), (float, "y")));
Try this: mixin ProviderParams!("S", int, "x", float, "y");
 Also: Can I nevertheless group arguments?
Template argument lists auto-expand, so even if you could group arguments, they make no functional difference. If you truly need to distinguish between groups of arguments, you have to pack them into a non-autoexpanding group, for example: template Group(Args...) { // Note: NOT eponymous. alias contents = Args; } mixin ProviderParams!("S", Group!(int, "x"), Group!(float, "y")); Keep in mind, however, that inside ProviderParams you will need to explicitly access .contents. For example: mixin template ProviderParams(Args...) { static foreach (arg; Args[1 .. $]) {{ alias type = arg.contents[0]; alias name = arg.contents[1]; }} } T -- Prosperity breeds contempt, and poverty breeds consent. -- Suck.com
Feb 26
prev sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 26 February 2019 at 22:56:37 UTC, Victor Porton wrote:
 On Tuesday, 26 February 2019 at 22:51:15 UTC, Q. Schroll wrote:
 Grouping arguments could be done, but from my experience, it 
 does not buy you anything; rather it makes it worse. Better 
 just deal with heterogeneous stuff just like 
 std.typecons.Tuple does.
After fixing the error you pointed me, it does not work too: mixin ProviderParams!("S", ((int, "x"), (float, "y"))); Also: Can I nevertheless group arguments?
No, not the way you do. The module std.meta defines AliasSeq as follows: alias AliasSeq(X...) = X; It is literally equivalent to template AliasSeq(X...) { alias AliasSeq = X; // "eponymous template member" } In mixin ProviderParams!("S", ((int, "x"), (float, "y"))); the parenthesized stuff like (int, "x") is invalid in terms of formal grammar. You could use AliasSeq!(int, "x") if you really want to display grouping in your source code. Note however that this does not do anything. The sequence is being flattened by the compiler, i.e. mixin ProviderParams!("S", AliasSeq!(AliasSeq!(int, "x"), AliasSeq!(float, "y"))); is exactly the same as mixin ProviderParams!("S", int, "x", float, "y"); which I wrote in my answer. When you don't use a eponymous template member, you can access them manually: template Pack(X...) { alias Contents = X; // not "eponymous template member" } Using this, you can create what I call packs. For some template T, while T!(AliasSeq!(int, bool), AliasSeq!(float, string, long)) is the same as writing T!(int, bool), the T!(Pack!(int, bool), Pack!(float, string, long)) is different from T!(int, bool, float, string, long). In the AliasSeq template, the eponymous template member feature of D immediately expands the sequence. If the template T is defined like this: template T(Args...) { .. } In the AliasSeq case, Args[0] is int, Args[1] is bool, Args[2] is float, ... In the Pack case, Args[0] is Pack!(int, bool), Args[1] is Pack!(float, string, long), and Args[0].Contents[0] is int, Args[0].Contents[1] is bool, Args[1].Contents[0] is float ... I've used Packs once to get the Cartesian product of an AliasSeq of Packs. It is a mess. I'd only use Packs when absolutely necessary; in your case, it does not seem so. In your case, you seem to need some initial thing "S" and then pairs of things. template you_name_it(Arg, args...) if (args.length % 2 == 0) // ensures pairs { static foreach (i; args.length / 2) { // access the type by args[2*i] // access the name (or whatever) by args[2*i+1] } } You could get the sequence of types and the sequence of names using alias Types = Stride!(2, args); alias Names = Stride!(2, args[1 .. $]);
Feb 26
parent reply Victor Porton <porton narod.ru> writes:
After following your suggestion to rewrite it with Stride it does 
not work either. I assume the error is somehow related to 
allSatisfy!.

https://github.com/vporton/struct-params-dlang/blob/c1adc86672f46fd6b743903cc270dceef120a8fe/source/struct_params.d

Please help. It is important for both D community and world at 
large.
Feb 26
next sibling parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 27 February 2019 at 03:53:35 UTC, Victor Porton 
wrote:
 After following your suggestion to rewrite it with Stride it 
 does not work either. I assume the error is somehow related to 
 allSatisfy!.

 https://github.com/vporton/struct-params-dlang/blob/c1adc86672f46fd6b743903cc270dceef120a8fe/source/struct_params.d

 Please help. It is important for both D community and world at 
 large.
Your problem is exactly here: allSatisfy!(x => is(typeof(x) == string), Names) allSatisfy expects its first parameter to be a template, not a function. We can introduce a simple helper template: template isA(T) { enum isA(alias U) = is(typeof(T) == U); } Then simply replace the code above with allSatisfy!(isA!string, Names). -- Simen
Feb 27
prev sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 27 February 2019 at 03:53:35 UTC, Victor Porton 
wrote:
 After following your suggestion to rewrite it with Stride it 
 does not work either. I assume the error is somehow related to 
 allSatisfy!.

 https://github.com/vporton/struct-params-dlang/blob/c1adc86672f46fd6b743903cc270dceef120a8fe/source/struct_params.d

 Please help. It is important for both D community and world at 
 large.
In static assert(allSatisfy!(x => isType!x, Types) && ... you use `allSatisfy` as if it took a lambda function or something like that. It does not. It takes a expects a template. First things first: The spec¹ says that allSatisfy!(F, T) «tests whether all given items satisfy a template predicate, i.e. evaluates to F!(T[0]) && F!(T[1]) && ... && F!(T[$ - 1]).» Here, `F` is not a lambda, it's a template. In your case, you can use `isTypeTuple!Types` from the library² to check what you intend to check. For the values, unfortunately there is no library function to check that they are all strings. A specific solution is to use a (static) template enum bool isStringValue(alias x) = is(typeof(x) == string); and feed it to `allSatisfy`: allSatisfy!(isStringValue, Names) ¹ https://dlang.org/library/std/meta/all_satisfy.html ² https://dlang.org/phobos/std_traits.html#isTypeTuple
Feb 27
parent reply Victor Porton <porton narod.ru> writes:
I rewrote it again:

https://github.com/vporton/struct-params-dlang/blob/f50f7e5919f90b1d06bf0cc08e3055548aad1797/source/struct_params.d

But it does not work :-( What is my error?

source/struct_params.d(43,60): Error: function expected before 
`()`, not `()` of type `()`
source/struct_params.d(44,43): Error: undefined identifier 
`fieldsWithDefaults`
source/struct_params.d(56,11): Error: template instance 
`struct_params.ProviderParamsCode!("S", int, "x", float, "y")` 
error instantiating
source/struct_params.d(82,5): Error: mixin 
`struct_params.__unittest_L81_C1.ProviderParams!("S", int, "x", 
float, "y")` error instantiating
Feb 27
parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 27 February 2019 at 22:45:03 UTC, Victor Porton 
wrote:
 I rewrote it again:

 https://github.com/vporton/struct-params-dlang/blob/f50f7e5919f90b1d06bf0cc08e3055548aad1797/source/struct_params.d

 But it does not work :-( What is my error?

 source/struct_params.d(43,60): Error: function expected before 
 `()`, not `()` of type `()`
 source/struct_params.d(44,43): Error: undefined identifier 
 `fieldsWithDefaults`
 source/struct_params.d(56,11): Error: template instance 
 `struct_params.ProviderParamsCode!("S", int, "x", float, "y")` 
 error instantiating
 source/struct_params.d(82,5): Error: mixin 
 `struct_params.__unittest_L81_C1.ProviderParams!("S", int, "x", 
 float, "y")` error instantiating
You seem to have misunderstood how staticMap works. It operates on a compile-time list of values, and only takes a single set of parameters: staticMap!(fn, item0, item1, item...), not staticMap!fn(item0, item1, item...). In addition, Types.length.iota will create a run-time range of values, not a compile-time AliasSeq of values. For this, you can use std.meta.aliasSeqOf: staticMap!(regularField, aliasSeqOf!(Types.length.iota)). Next, join expects a range, not an AliasSeq. In other words, staticMap!(...).join('\n') will fail to compile - staticMap!(...) will need to be turned into a range of some sort. We can do this the same way we'd turn any list of values into a range - by putting brackets around it. Just like [1,2,3] is a valid array, so is [staticMap!(...)], supposing the elements have some common type. But wait - there's more! You're using __traits(identifier, Types[i]). As pointed out in https://forum.dlang.org/post/smgsgycpgvtagfsdxapi forum.dlang.org, this will fail for built-in types, as they don't have an identifier. You can get their names, as well as the name of any other type with Types[i].stringof. Lastly, you have not imported std.typecons, so your use of Nullable will fail. So, that's the issues that cause it to not compile. There are issues also in the code that isn't instantiated, i.e. callFunctionWithParamsStruct and callMemberFunctionWithParamsStruct. Since these aren't used, they don't cause compile failure yet. The main issue here is exactly the same as above: a conflation of run-time and compile-time responsibilities, this time when using map. Since map is a run-time function, it can't do compile-time things like __traits(getMember) - you'll need to use staticMap for that. However, there's a much easier solution: auto callFunctionWithParamsStruct(alias f, S)(S s) { return f(s.tupleof); } The same thing can be done for callMemberFunctionWithParamsStruct. Now, I will note that no checking is done that parameter names and field names match, so void fun(int numberOfBeans, string nameOfCat) can easily be called with struct S { int temperatureOfPudding; string declarationOfIndependence; }, but I will assume that's because you haven't gotten around to it yet. -- Simen
Mar 01