digitalmars.D.learn - Are templates with variadic value parameters possible?
- Devin Hill (40/40) Jul 14 2016 Hi everyone,
- Basile B. (18/58) Jul 14 2016 With D style variadics it works, you can build the array from the
- Devin Hill (10/27) Jul 14 2016 Thanks, that way of doing it does work. I guess that means
- Basile B. (5/8) Jul 14 2016 Yes, immediatly, now, I think that a contraint has to be used.
- Basile B. (10/13) Jul 14 2016 even better:
- Devin Hill (10/19) Jul 15 2016 Yeah, that's basically what I ended up doing, but since I also
- Mike Parker (43/50) Jul 15 2016 It isn't too much effort to add support for both:
Hi everyone, I have a struct template which takes an integer n, and then has a constructor taking that many arguments of type long, which looks like: struct Struct(int n) { this(long[n] nums...) { /* stuff */ } } This works and lets me change n for each instantiation, but I wanted to make a function to construct it to gain the benefit of not having to write e.g. Struct!2(1, 2); // 2 could technically be inferred from the number of arguments Struct!3(1, 2, 3); // similar story // instead, with a function makeStruct!(1, 2, 3, etc...); // ideal scenario returning Struct!n for some n I tried writing this: auto makeStruct(long[] nums...)() { return Struct!(nums.length)(nums); } but sadly it seems that the syntax is not recognized, despite the fact that it can be used in the non-template parameter section. I have had to settle for a non-variadic version, which complicates the syntax a little bit: // non-variadic; negatively affects calling syntax auto makeStruct(long[] nums)() { return Struct!(nums.length)(nums); } makeStruct!([1, 2, 3]); // less appealing because of the extra brackets makeStruct![1, 2, 3]; // apparently invalid, despite no obvious conflict This started out because I just wanted a cleaner-looking constructor, so it's kind of nitpicky, but now I'm just interested in whether this is possible at all. Is there any way to achieve what I'm trying to do without dissecting an AliasSeq? In other words, can I get a type-safe variadic value template parameter without conditionals? If there's anything I've explained inadequately, just ask. Thanks.
Jul 14 2016
On Friday, 15 July 2016 at 03:43:49 UTC, Devin Hill wrote:Hi everyone, I have a struct template which takes an integer n, and then has a constructor taking that many arguments of type long, which looks like: struct Struct(int n) { this(long[n] nums...) { /* stuff */ } } This works and lets me change n for each instantiation, but I wanted to make a function to construct it to gain the benefit of not having to write e.g. Struct!2(1, 2); // 2 could technically be inferred from the number of arguments Struct!3(1, 2, 3); // similar story // instead, with a function makeStruct!(1, 2, 3, etc...); // ideal scenario returning Struct!n for some n I tried writing this: auto makeStruct(long[] nums...)() { return Struct!(nums.length)(nums); } but sadly it seems that the syntax is not recognized, despite the fact that it can be used in the non-template parameter section. I have had to settle for a non-variadic version, which complicates the syntax a little bit: // non-variadic; negatively affects calling syntax auto makeStruct(long[] nums)() { return Struct!(nums.length)(nums); } makeStruct!([1, 2, 3]); // less appealing because of the extra brackets makeStruct![1, 2, 3]; // apparently invalid, despite no obvious conflict This started out because I just wanted a cleaner-looking constructor, so it's kind of nitpicky, but now I'm just interested in whether this is possible at all. Is there any way to achieve what I'm trying to do without dissecting an AliasSeq? In other words, can I get a type-safe variadic value template parameter without conditionals? If there's anything I've explained inadequately, just ask. Thanks.With D style variadics it works, you can build the array from the list and have a static array: ===== void foo(T...)(T t) { T[0][T.length] tt = [t]; // T[0] is the type writeln(tt); // [1,2,3] static assert(isStaticArray!(typeof(tt))); } void main(string[] args) { foo(1,2,3); } ===== Note that you should check that the elements of T[] have all the same type. It shouldn't be a big problem.
Jul 14 2016
On Friday, 15 July 2016 at 04:08:19 UTC, Basile B. wrote:With D style variadics it works, you can build the array from the list and have a static array: ===== void foo(T...)(T t) { T[0][T.length] tt = [t]; // T[0] is the type writeln(tt); // [1,2,3] static assert(isStaticArray!(typeof(tt))); } void main(string[] args) { foo(1,2,3); } ===== Note that you should check that the elements of T[] have all the same type. It shouldn't be a big problem.Thanks, that way of doing it does work. I guess that means there's no easy way to make sure all T are the same type without a template constraint? It's not that hard, you're right, but it's less elegant I think. Just a shame that auto makeStruct(long[] nums...)(); doesn't work; it seems like it would have. Oh well, we can't have everything. Thanks for the help though!
Jul 14 2016
On Friday, 15 July 2016 at 04:31:08 UTC, Devin Hill wrote:Thanks, that way of doing it does work. I guess that means there's no easy way to make sure all T are the same type without a template constraint?Yes, immediatly, now, I think that a contraint has to be used. But you have several choices for the constraint, two obvious: - recursive template. - staticIota in a foreach. (aliasSeqOf!(iota(1, T.length))
Jul 14 2016
On Friday, 15 July 2016 at 04:38:03 UTC, Basile B. wrote:two obvious: - recursive template. - staticIota in a foreach. (aliasSeqOf!(iota(1, T.length))even better: template sameType(T...) { import std.meta; static if (!T.length) enum sameType = false; else enum sameType = NoDuplicates!T.length == 1; }
Jul 14 2016
On Friday, 15 July 2016 at 05:23:15 UTC, Basile B. wrote:even better: template sameType(T...) { import std.meta; static if (!T.length) enum sameType = false; else enum sameType = NoDuplicates!T.length == 1; }Yeah, that's basically what I ended up doing, but since I also needed to constrain the type of T, I added is(T[0] : long) to the condition. It works pretty well! Granted, it doesn't allow for calling it in two ways like a variadic version would have: foo(1, 2, 3) // works with this setup foo([1, 2, 3]) // doesn't, but would only be occasionally useful anyway but all in all it's a decent workaround for the problem.
Jul 15 2016
On Friday, 15 July 2016 at 15:04:22 UTC, Devin Hill wrote:to the condition. It works pretty well! Granted, it doesn't allow for calling it in two ways like a variadic version would have: foo(1, 2, 3) // works with this setup foo([1, 2, 3]) // doesn't, but would only be occasionally useful anyway but all in all it's a decent workaround for the problem.It isn't too much effort to add support for both: ``` import std.traits : isArray, ForeachType; import std.stdio : writeln; void func(Args...)(Args args) if(is(Args[0] : long) || (isArray!(Args[0]) && is(ForeachType!(Args[0]) : long))) { static if(isArray!(Args[0])) { foreach(i; args[0]) writeln(i); } else { foreach(arg; args) writeln(arg); } } void main() { func(10, 20, 30, 40); func([1, 2, 3, 4, 5]); } ``` Or, alternatively, to support multiple arrays: ``` void func(Args...)(Args args) if(is(Args[0] : long) || (isArray!(Args[0]) && is(ForeachType!(Args[0]) : long))) { foreach(arg; args) { static if(isArray!(Args[0])) { foreach(i; arg) writeln(i); } else writeln(arg); } } void main() { func(10, 20, 30, 40); func([1, 2, 3, 4, 5], [100, 200, 300, 400]); } ```
Jul 15 2016