digitalmars.D.learn - simple (I think) eponymous template question ... what is proper
- james.p.leblanc (18/18) Aug 17 2021 Evening All,
- Alexandru Ermicioi (10/14) Aug 17 2021 That is template specialization:
- H. S. Teoh (25/36) Aug 17 2021 This is exactly what "signature constraints" are for. For example:
- =?UTF-8?Q?Ali_=c3=87ehreli?= (25/28) Aug 17 2021 types.
- Steven Schveighoffer (4/20) Aug 17 2021 [Template
- james.p.leblanc (19/25) Aug 17 2021 Dear All,
- H. S. Teoh (17/29) Aug 17 2021 [...]
- james.p.leblanc (7/19) Aug 17 2021 Dear H.S. Teoh,
- Paul Backus (6/32) Aug 17 2021 FYI: in this particular case, you can use std.meta.staticIndexOf
- james.p.leblanc (41/47) Aug 17 2021 All, Thanks again ... Paul, I will try this staticIndexOf as you
- Bastiaan Veelo (7/41) Aug 17 2021 https://run.dlang.io/is/m5svQ2
- Alexandru Ermicioi (3/8) Aug 17 2021 Just don't over rely on it. It can cause compilation slowdowns,
- james.p.leblanc (19/31) Aug 17 2021 I've been happily trying to absorb all the helpful concepts
- Dominikus Dittes Scherkl (4/8) Aug 17 2021 I very much prefer the ususal constraint syntax
- Tejas (61/95) Aug 17 2021 A template specialization is basically a template overload.
- james.p.leblanc (17/32) Aug 18 2021 Domninikus, Tejas, and All,
- H. S. Teoh (12/32) Aug 17 2021 [...]
Evening All, Eponymous templates allow a nice calling syntax. For example, "foo" here can be called without needing the exclamation mark (!) at calling sites. We see that foo is restricting a, and b to be of the same type ... so far, so good. auto foo(T)(T a, T b) { ... } Now, suppose I need to restrict "T" only certain subsets of variable types. I can imagine putting in some "static if" statements in my function body, is one solution. (Also a bit ugly as the allowed types might grow). Is there a more elegant way, to do this? Regards, James PS Any violations should be caught at compile time.
Aug 17 2021
On Tuesday, 17 August 2021 at 18:11:56 UTC, james.p.leblanc wrote:Is there a more elegant way, to do this? Regards, James PS Any violations should be caught at compile time.That is template specialization: ``` auto moo(T : YourSpecialClassOrDType)(T myMoo) {•••} ``` You can also declare other overloads of the method, just make sure they don't overlap in the constraints, otherwise you'd get ambiguous call exception. Regards, Alexandru.
Aug 17 2021
On Tue, Aug 17, 2021 at 06:11:56PM +0000, james.p.leblanc via Digitalmars-d-learn wrote:Evening All, Eponymous templates allow a nice calling syntax. For example, "foo" here can be called without needing the exclamation mark (!) at calling sites. We see that foo is restricting a, and b to be of the same type ... so far, so good. auto foo(T)(T a, T b) { ... } Now, suppose I need to restrict "T" only certain subsets of variable types.This is exactly what "signature constraints" are for. For example: auto foo(T)(T a, T b) if (is(T == string)) // <--- this is a signature constraint { ... // deal with strings here } auto foo(T)(T a, T b) if (is(T == int)) // you can overload on signature constraints { ... // deal with ints here } auto foo(T)(T a, T b) if (is(T == float) || is(T == double)) // you can use complex conditions { ... // deal with floats or doubles here } Be aware that a signature constraint failure is not an error: the compiler simply skips that overload of the function. So if you make a mistake in a sig constraint, it may *always* fail, and the compiler may try instead to instantiate a completely unintended overload and generate an error that may have nothing to do with the actual problem. T -- Give a man a fish, and he eats once. Teach a man to fish, and he will sit forever.
Aug 17 2021
On 8/17/21 11:11 AM, james.p.leblanc wrote:auto foo(T)(T a, T b) { ... } Now, suppose I need to restrict "T" only certain subsets of variabletypes. There are template constraints: import std.traits; auto foo(T)(T a, T b) if (isArray!T) { // ... } auto foo(T)(T a, T b) if (isFloatingPoint!T) { // ... } void main() { foo(1.5, 2.5); } See __traits as well and of course you can use any compile-time check in the template constraint.I can imagine putting in some "static if" statements in my function body,That method can display an intelligible error message if there is only one template implementation. Template constraints on the other hand, are not errors; they just determine what template implementations are available for instantiation for the used parameters. And of course, 'static if' has more uses other than just error reporting; which might be performed better with 'static assert'. Ali
Aug 17 2021
On 8/17/21 2:11 PM, james.p.leblanc wrote:Evening All, Eponymous templates allow a nice calling syntax. For example, "foo" here can be called without needing the exclamation mark (!) at calling sites. We see that foo is restricting a, and b to be of the same type ... so far, so good. auto foo(T)(T a, T b) { ... } Now, suppose I need to restrict "T" only certain subsets of variable types. I can imagine putting in some "static if" statements in my function body, is one solution. (Also a bit ugly as the allowed types might grow). Is there a more elegant way, to do this?[Template constraints](https://dlang.org/spec/template.html#template_constraints). -Steve
Aug 17 2021
On Tuesday, 17 August 2021 at 18:28:53 UTC, Steven Schveighoffer wrote:On 8/17/21 2:11 PM, james.p.leblanc wrote:Dear All, Thanks! I was aware of, and have used template constraints in simple ways. But, I should express my question a bit better: "... is there a more elegant way to expression these constraints?" Perhaps by extending Alexandru's "moo" concepts, with a list of allowed types: auto moo(T : (int || float || mySpecialStruct )(T myMoo) {•••} When re-using any such sets, it would be nice to define the set as follows: S = (int || float || mySpecialStruct) and then define "moo" more concisely as: auto moo(T < S)(T myMoo) {•••} ( where I have used "<" to mean "T is a member of S"). Possible? Best Regards, JamesEvening All,[Template constraints](https://dlang.org/spec/template.html#template_constraints). -Steve
Aug 17 2021
On Tue, Aug 17, 2021 at 07:22:54PM +0000, james.p.leblanc via Digitalmars-d-learn wrote: [...]auto moo(T : (int || float || mySpecialStruct )(T myMoo) {•••} When re-using any such sets, it would be nice to define the set as follows: S = (int || float || mySpecialStruct) and then define "moo" more concisely as: auto moo(T < S)(T myMoo) {•••} ( where I have used "<" to mean "T is a member of S").[...] You could use a helper template and an AliasSeq for this: template isAmong(T, S...) { static if (S.length == 0) enum isAmong = false; else enum isAmong = is(T == S) || isAmong(T, S[1..$]); } import std.meta : AliasSeq; alias MyTypes = AliasSeq!(int, float, MySpecialStruct); auto myFunc(T)(T a, T b) if (isAmong!(T, MyTypes)) { ... } T -- I am a consultant. My job is to make your job redundant. -- Mr Tom
Aug 17 2021
On Tuesday, 17 August 2021 at 19:44:29 UTC, H. S. Teoh wrote:You could use a helper template and an AliasSeq for this: template isAmong(T, S...) { static if (S.length == 0) enum isAmong = false; else enum isAmong = is(T == S) || isAmong(T, S[1..$]); } import std.meta : AliasSeq; alias MyTypes = AliasSeq!(int, float, MySpecialStruct); auto myFunc(T)(T a, T b) if (isAmong!(T, MyTypes)) { ... } TDear H.S. Teoh, Wow! That is absolutely beautiful ... I had never seen (or even imagined) a recursive template! This expands my mind in a good way ... and is going into my toolbox immediately. Best Regards, James
Aug 17 2021
On Tuesday, 17 August 2021 at 19:53:52 UTC, james.p.leblanc wrote:On Tuesday, 17 August 2021 at 19:44:29 UTC, H. S. Teoh wrote:FYI: in this particular case, you can use std.meta.staticIndexOf instead of writing the recursion out yourself: import std.meta: staticIndexOf; enum isAmong(T, S...) = staticIndexOf!(T, S) >= 0; Docs: https://phobos.dpldocs.info/std.meta.staticIndexOf.htmlYou could use a helper template and an AliasSeq for this: template isAmong(T, S...) { static if (S.length == 0) enum isAmong = false; else enum isAmong = is(T == S) || isAmong(T, S[1..$]); } import std.meta : AliasSeq; alias MyTypes = AliasSeq!(int, float, MySpecialStruct); auto myFunc(T)(T a, T b) if (isAmong!(T, MyTypes)) { ... } TDear H.S. Teoh, Wow! That is absolutely beautiful ... I had never seen (or even imagined) a recursive template! This expands my mind in a good way ... and is going into my toolbox immediately. Best Regards, James
Aug 17 2021
On Tuesday, 17 August 2021 at 20:13:59 UTC, Paul Backus wrote:FYI: in this particular case, you can use std.meta.staticIndexOf instead of writing the recursion out yourself: import std.meta: staticIndexOf; enum isAmong(T, S...) = staticIndexOf!(T, S) >= 0; Docs: https://phobos.dpldocs.info/std.meta.staticIndexOf.htmlAll, Thanks again ... Paul, I will try this staticIndexOf as you mention. I had been receiving "circular reference to variable" error messages from the "isAmong" suggestion. (But, I very likely had misunderstood how to use this.) I would still be interested to learn what I have done wrong. So, below is my code: import std.stdio; import std.meta : AliasSeq; template isAmong(T, S...) { static if (S.length == 0) enum isAmong = false; else enum isAmong = is(T == S) || isAmong(T, S[1..$]); } alias MyTypes = AliasSeq!(int, float); auto myFunc(T)(T a, T b) if (isAmong!(T, MyTypes)) { writeln(" in myFunc "); return; } void main(){ writeln("started ..."); auto a = 1; auto b = 2; myFunc!(int)(a, b); return; } And, here are the error message: (master) Notes > dmd recursive_template.d recursive_template.d(9): Error: circular reference to variable `recursive_template.isAmong!(int, int, float).isAmong` recursive_template.d(17): Error: template instance `recursive_template.isAmong!(int, int, float)` error instantiating recursive_template.d(29): while looking for match for `myFunc!int` Can anyone see what is going on? Best Regards, James
Aug 17 2021
On Tuesday, 17 August 2021 at 20:29:51 UTC, james.p.leblanc wrote:So, below is my code: import std.stdio; import std.meta : AliasSeq; template isAmong(T, S...) { static if (S.length == 0) enum isAmong = false; else enum isAmong = is(T == S) || isAmong(T, S[1..$]); } alias MyTypes = AliasSeq!(int, float); auto myFunc(T)(T a, T b) if (isAmong!(T, MyTypes)) { writeln(" in myFunc "); return; } void main(){ writeln("started ..."); auto a = 1; auto b = 2; myFunc!(int)(a, b); return; } And, here are the error message: (master) Notes > dmd recursive_template.d recursive_template.d(9): Error: circular reference to variable `recursive_template.isAmong!(int, int, float).isAmong` recursive_template.d(17): Error: template instance `recursive_template.isAmong!(int, int, float)` error instantiating recursive_template.d(29): while looking for match for `myFunc!int` Can anyone see what is going on? Best Regards, Jameshttps://run.dlang.io/is/m5svQ2 The error was in line 8. T (Teoh) forgot to take the first of `S` in `is(T == S[0])`. The error message improves after adding the `!` in `isAmong!(T, S[1..$])`, which, surprisingly, is optional! — Bastiaan.
Aug 17 2021
On Tuesday, 17 August 2021 at 19:53:52 UTC, james.p.leblanc wrote:Wow! That is absolutely beautiful ... I had never seen (or even imagined) a recursive template! This expands my mind in a good way ... and is going into my toolbox immediately. Best Regards, JamesJust don't over rely on it. It can cause compilation slowdowns, so avoid it if you can.
Aug 17 2021
On Tuesday, 17 August 2021 at 20:28:20 UTC, Alexandru Ermicioi wrote:On Tuesday, 17 August 2021 at 19:53:52 UTC, james.p.leblanc wrote:I've been happily trying to absorb all the helpful concepts posted. A final related question in the quest for simplicity and robustness. If I wanted to ensure that a function accepts only arguments of byte, int, uint, long, etc. (i.e. integer-like types). Is the accepted way to do this like so?: **auto foo( T : long )(T a, T b){ ... }** I really wish I could find a good explanation of the ":" (colon) used in templates. I am sure one exists ...but haven't come upon it just yet. (I thought "isIntegral" in traits module would be helpful...but I failed in my experiments.) Thanks for patience with this question! Best Regards, JamesWow! That is absolutely beautiful ... I had never seen (or even imagined) a recursive template! This expands my mind in a good way ... and is going into my toolbox immediately. Best Regards, JamesJust don't over rely on it. It can cause compilation slowdowns, so avoid it if you can.
Aug 17 2021
On Wednesday, 18 August 2021 at 05:33:13 UTC, james.p.leblanc wrote:If I wanted to ensure that a function accepts only arguments of byte, int, uint, long, etc. (i.e. integer-like types). Is the accepted way to do this like so?: **auto foo( T : long )(T a, T b){ ... }**I very much prefer the ususal constraint syntax **auto foo(T)(T a, T b) if(isIntegral!T) { ... }**
Aug 17 2021
On Wednesday, 18 August 2021 at 05:33:13 UTC, james.p.leblanc wrote:On Tuesday, 17 August 2021 at 20:28:20 UTC, Alexandru Ermicioi wrote:A template specialization is basically a template overload. For example: ```d void func(int a){ writeln("argument is integer"); } void func(long a){ writeln("argument is long"); } void main(){ int a; long b; func(a); func(b); } ``` The above does what you expect. Now the template specialization way: ```d void funcTemplate(T:int)(T a){ writeln("argument is int"); } void funcTemplate(T : long)(T a){ writeln("argument is long"); } void main(){ int c; long d; funcTemplate(c); funcTemplate(d); } ``` The above will also do what you expect. Template specialization is basically overloading for templates, nothing more. Below is a complete working copy-paste example combining both: ```d import std; void func(int a){ writeln("argument is integer"); } void func(long a){ writeln("argument is long"); } void funcTemplate(T:int)(T a){ writeln("argument is int"); } void funcTemplate(T : long)(T a){ writeln("argument is long integer"); } void main(){ int a; long b; func(a); func(b); funcTemplate(a); funcTemplate(b); } ```On Tuesday, 17 August 2021 at 19:53:52 UTC, james.p.leblanc wrote:I've been happily trying to absorb all the helpful concepts posted. A final related question in the quest for simplicity and robustness. If I wanted to ensure that a function accepts only arguments of byte, int, uint, long, etc. (i.e. integer-like types). Is the accepted way to do this like so?: **auto foo( T : long )(T a, T b){ ... }** I really wish I could find a good explanation of the ":" (colon) used in templates. I am sure one exists ...but haven't come upon it just yet. (I thought "isIntegral" in traits module would be helpful...but I failed in my experiments.) Thanks for patience with this question! Best Regards, JamesWow! That is absolutely beautiful ... I had never seen (or even imagined) a recursive template! This expands my mind in a good way ... and is going into my toolbox immediately. Best Regards, JamesJust don't over rely on it. It can cause compilation slowdowns, so avoid it if you can.
Aug 17 2021
On Wednesday, 18 August 2021 at 06:53:51 UTC, Tejas wrote:void funcTemplate(T:int)(T a){ writeln("argument is int"); } void funcTemplate(T : long)(T a){ writeln("argument is long integer"); } void main(){ int a; long b; func(a); func(b); funcTemplate(a); funcTemplate(b); } ```Domninikus, Tejas, and All, I see that I had been (stupidly) omitting the exclamation point after **"isIntegral"** in my clumsy attempts to use the traits ... thanks for your clear example helping me identify my error. Also, thanks for mentioning the words **"template specialization"** , I did not know what to call the use of **":"** in templates. Now, I have a search term I can use to learn more ...and find it is in Phillippe Signaud's informative "D-templates-tutorial". Thanks again, James PS Also, I am enjoying, a entertaining and educational tutorial that Phillippe has linked in his tutorial. Other may learn from this as well: http://www.semitwist.com/articles/EfficientAndFlexible/SinglePage/
Aug 18 2021
On Tue, Aug 17, 2021 at 07:53:52PM +0000, james.p.leblanc via Digitalmars-d-learn wrote:On Tuesday, 17 August 2021 at 19:44:29 UTC, H. S. Teoh wrote:[...]You could use a helper template and an AliasSeq for this: template isAmong(T, S...) { static if (S.length == 0) enum isAmong = false; else enum isAmong = is(T == S) || isAmong(T, S[1..$]); } import std.meta : AliasSeq; alias MyTypes = AliasSeq!(int, float, MySpecialStruct); auto myFunc(T)(T a, T b) if (isAmong!(T, MyTypes)) { ... }Dear H.S. Teoh, Wow! That is absolutely beautiful ... I had never seen (or even imagined) a recursive template! This expands my mind in a good way ... and is going into my toolbox immediately.[...] I didn't want to spoil your joy of discovery, but since others have already done that -- beware of recursive templates, because if used too often they can become a source of big slowdowns to your compilation times (as well as the compiler consuming ridiculous amounts of memory). Simple, linearly-recursive templates like this one are probably harmless (unless you artificially generate pathologically-long lists of types). But if the recursion gets too deep or grows superlinearly, you probably want to consider alternative implementations instead. ;-) --T
Aug 17 2021