Thread overview | |||||||||
---|---|---|---|---|---|---|---|---|---|
|
July 15, 2016 Are templates with variadic value parameters possible? | ||||
---|---|---|---|---|
| ||||
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. |
July 15, 2016 Re: Are templates with variadic value parameters possible? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Devin Hill | 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. |
July 15, 2016 Re: Are templates with variadic value parameters possible? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Basile B. | 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!
|
July 15, 2016 Re: Are templates with variadic value parameters possible? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Devin Hill | 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))
|
July 15, 2016 Re: Are templates with variadic value parameters possible? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Basile B. | 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;
}
|
July 15, 2016 Re: Are templates with variadic value parameters possible? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Basile B. | 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.
|
July 15, 2016 Re: Are templates with variadic value parameters possible? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Devin Hill | 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]);
}
```
|
Copyright © 1999-2021 by the D Language Foundation