digitalmars.D - Fun with templates
- Manu (15/15) Jul 05 2013 Okay, so I feel like this should be possible, but I can't make it work.....
- Daniel Murphy (3/18) Jul 05 2013 void f(T)(T _a) { Unqual!T a = _a; ... }
- Manu (3/26) Jul 05 2013 That doesn't do what I want at all. The signature is still f(T) not
- Daniel Murphy (3/36) Jul 05 2013 Yeah it's possible I didn't finish reading your post.
- Namespace (2/5) Jul 06 2013 For non-const, const and immutable inout would do it.
- Manu (2/7) Jul 06 2013 Not if there's more than 1 argument, and 'inout T' is still not 'T'.
- Marco Leise (7/19) Jul 07 2013 Hey, inout looks like a neat solution! And it only creates one
- TommiT (4/24) Jul 07 2013 const would have the same effect:
- Marco Leise (5/10) Jul 07 2013 That doesn't handle shared.
- Tommi (2/10) Jul 07 2013 That seems like a compiler bug to me.
- finalpatch (13/35) Jul 05 2013 this?
- Manu (3/36) Jul 05 2013 And again, f(T), the signature is wrong. There is also an additional
- Vladimir Panteleev (13/15) Jul 05 2013 I don't think D's IFTI allows different argument and parameter
- Timon Gehr (7/22) Jul 05 2013 void f(T)(const(T) a) {}
- Manu (15/42) Jul 05 2013 So what is the signature of f() in this case? It looks like the function
- monarch_dodra (28/50) Jul 06 2013 You could just forward to an implementation template, passing
- monarch_dodra (5/15) Jul 06 2013 Hum.. just realized you explicitly wanted to do this via template
- Manu (9/23) Jul 06 2013 Nope, still have no good idea of my own. My first 2 impulses were the be...
- TommiT (11/14) Jul 06 2013 This seems like a defect of the language. Perhaps a new language
- TommiT (2/3) Jul 06 2013 That should have been immutable(S)
- Manu (13/28) Jul 06 2013 The way that makes the most sense to me is:
- TommiT (22/51) Jul 06 2013 That would look more sensible, but I don't think that's
- Namespace (18/38) Jul 06 2013 It seems that your code works if you put the Template Type
- Manu (3/36) Jul 06 2013 Indeed, hence my point that the type deduction is the key issue here.
- Timon Gehr (4/24) Jul 06 2013 The key issue is that the syntax void foo(T)(Unqual!T a); denotes
- Manu (4/35) Jul 06 2013 Perhaps what you say is true, but does that mean something to the effect...
- TommiT (6/8) Jul 06 2013 I think Artur's solution is the cleanest one because it changes
- Manu (6/16) Jul 06 2013 Maybe so, but I just have my suspicions that it will never fly, and I th...
- TommiT (27/31) Jul 07 2013 You think it won't fly because it's a breaking change? I also
- Artur Skawina (15/29) Jul 07 2013 It's like the virtual-by-default situation - if the default isn't fixed
- Marco Leise (8/51) Jul 07 2013 If you wanted to save on template instantiations for every
- Martin Nowak (3/7) Jul 07 2013 There is a linker optimization to get rid of the duplicates.
- Dicebot (5/12) Jul 08 2013 Linker alone can't possibly know all required information to make
- TommiT (28/30) Jul 06 2013 But my syntax makes pretty much sense too:
- Artur Skawina (41/42) Jul 06 2013 I was going to reply that while template bloat is a real issue, it is th...
- Dicebot (12/13) Jul 06 2013 It is not that simple. Consider:
- Artur Skawina (9/23) Jul 06 2013 This is exactly why i did mention that this is a breaking change and tha...
- TommiT (3/4) Jul 06 2013 To me it seems highly unlikely that this would break any code.
- Dicebot (19/20) Jul 06 2013 Yes. Honestly, lot of problems. I can easily imagine template
- TommiT (7/18) Jul 06 2013 He's talking about changing the semantics only on POD types, like
- Dmitry Olshansky (5/23) Jul 06 2013 I've seen an aggressive proposal back in the day to just do a shallow
- Marco Leise (12/14) Jul 07 2013 I came to the same conclusion. If feasible, type inference
- Dicebot (7/10) Jul 07 2013 Than it does not really solve anything. Have you measured how
- Tommi (14/24) Jul 07 2013 Basically all I know about the performance issue being discussed
- Marco Leise (5/21) Jul 07 2013 Hmm, this inout stuff sounds like a possible solution...
- Dicebot (6/7) Jul 06 2013 If this is about template bloat I think much better is to address
- Marco Leise (7/15) Jul 07 2013 Less duplicate instantiations results in faster compile times.
- Dmitry Olshansky (12/27) Jul 07 2013 The thing is that if even if you somehow force your way past IFTI what
- Manu (12/30) Jul 07 2013 Hmmm, this is an interesting point.
- Jacob Carlborg (16/26) Jul 08 2013 Template arguments are part of the mangled name. Example:
- Martin Nowak (21/36) Jul 08 2013 I can't find the Bugzilla entry right now, but we discussed before why
- Manu (59/100) Jul 13 2013 I'm not sure I follow. Can you demonstrate?
Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.
Jul 05 2013
"Manu" <turkeyman gmail.com> wrote in message news:mailman.1752.1373074509.13711.digitalmars-d puremagic.com...Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.void f(T)(T _a) { Unqual!T a = _a; ... }
Jul 05 2013
On 6 July 2013 11:41, Daniel Murphy <yebblies nospamgmail.com> wrote:"Manu" <turkeyman gmail.com> wrote in message news:mailman.1752.1373074509.13711.digitalmars-d puremagic.com...That doesn't do what I want at all. The signature is still f(T) not f(Unqual!T).Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.void f(T)(T _a) { Unqual!T a = _a; ... }
Jul 05 2013
"Manu" <turkeyman gmail.com> wrote in message news:mailman.1754.1373081562.13711.digitalmars-d puremagic.com...On 6 July 2013 11:41, Daniel Murphy <yebblies nospamgmail.com> wrote:Yeah it's possible I didn't finish reading your post."Manu" <turkeyman gmail.com> wrote in message news:mailman.1752.1373074509.13711.digitalmars-d puremagic.com...That doesn't do what I want at all. The signature is still f(T) not f(Unqual!T).Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.void f(T)(T _a) { Unqual!T a = _a; ... }
Jul 05 2013
That doesn't do what I want at all. The signature is still f(T) not f(Unqual!T).For non-const, const and immutable inout would do it. void f(T)(inout T var)
Jul 06 2013
On 6 July 2013 18:23, Namespace <rswhite4 googlemail.com> wrote:That doesn't do what I want at all. The signature is still f(T) notNot if there's more than 1 argument, and 'inout T' is still not 'T'.f(Unqual!T).For non-const, const and immutable inout would do it. void f(T)(inout T var)
Jul 06 2013
Am Sat, 6 Jul 2013 20:24:41 +1000 schrieb Manu <turkeyman gmail.com>:On 6 July 2013 18:23, Namespace <rswhite4 googlemail.com> wrote:Hey, inout looks like a neat solution! And it only creates one template instance no matter how many arguments of type inout(T) you use! -- MarcoThat doesn't do what I want at all. The signature is still f(T) notNot if there's more than 1 argument, and 'inout T' is still not 'T'.f(Unqual!T).For non-const, const and immutable inout would do it. void f(T)(inout T var)
Jul 07 2013
On Sunday, 7 July 2013 at 11:07:44 UTC, Marco Leise wrote:Am Sat, 6 Jul 2013 20:24:41 +1000 schrieb Manu <turkeyman gmail.com>:const would have the same effect: void f(T)(const T var) { ... } ...but then you can't mutate var.On 6 July 2013 18:23, Namespace <rswhite4 googlemail.com> wrote:Hey, inout looks like a neat solution! And it only creates one template instance no matter how many arguments of type inout(T) you use!That doesn't do what I want at all. The signature is still f(T) notNot if there's more than 1 argument, and 'inout T' is still not 'T'.f(Unqual!T).For non-const, const and immutable inout would do it. void f(T)(inout T var)
Jul 07 2013
Am Sun, 07 Jul 2013 13:17:23 +0200 schrieb "TommiT" <tommitissari hotmail.com>:const would have the same effect: void f(T)(const T var) { ... } ...but then you can't mutate var.That doesn't handle shared. -- Marco
Jul 07 2013
On Sunday, 7 July 2013 at 11:59:36 UTC, Marco Leise wrote:Am Sun, 07 Jul 2013 13:17:23 +0200 schrieb "TommiT" <tommitissari hotmail.com>:That seems like a compiler bug to me.const would have the same effect: void f(T)(const T var) { ... } ...but then you can't mutate var.That doesn't handle shared.
Jul 07 2013
On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.this? template f(T) { void f (T x) { f_i(x); } void f_i(Unqual!T x) { writefln("%s", x); } }
Jul 05 2013
On 6 July 2013 11:42, finalpatch <fengli gmail.com> wrote:On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:And again, f(T), the signature is wrong. There is also an additional call... twice as slow when non-optimised :/Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.this? template f(T) { void f (T x) { f_i(x); } void f_i(Unqual!T x) { writefln("%s", x); } }
Jul 05 2013
On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:Okay, so I feel like this should be possible, but I can't make it work...I don't think D's IFTI allows different argument and parameter types... I think this is possible with a proxy function (a function that accepts arguments with any qualifiers, but forwards them to the real function after stripping away top-level qualifiers). Should be possible to create something that auto-generates such a proxy function. A small proxy function will likely be inlined, and would avoid template bloat. Another possible way: Unqual!T unqual(T)(T v) { return v; } Then use unqual(x) whenever calling the function (although you'd have to remember to, which sucks).
Jul 05 2013
On 07/06/2013 03:34 AM, Manu wrote:Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.void f(T)(const(T) a) {} This will strip off const, immutable and inout, but not shared. I'd have thought that void f(T)(const(shared(T)) a) {} would strip shared as well, but DMD does not match int to const(shared(int)) at all which I think is incorrect.
Jul 05 2013
On 6 July 2013 11:45, Timon Gehr <timon.gehr gmx.ch> wrote:On 07/06/2013 03:34 AM, Manu wrote:So what is the signature of f() in this case? It looks like the function receives const(T), not T. It looks like it sets T to Unqual!T, but it's not T that I'm interested in, it's the function argument being deduced to the correct type. We can do impressive stuff like this: void f(T : U[N], U, size_t N)(T x); .. but I suppose in that example, T is still the supplied type verbatim, it's just performing a lot of fancy work to decompose it. So then I wonder if my question becomes, with parameter type deduction, is it an absolute requirement that the parameter type is taken to be the supplied argument's type verbatim (and not possibly something it is implicitly castable to)? Is there ANY way to flex this rule while retaining the functionality of argument type deduction?Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.void f(T)(const(T) a) {} This will strip off const, immutable and inout, but not shared. I'd have thought that void f(T)(const(shared(T)) a) {} would strip shared as well, but DMD does not match int to const(shared(int)) at all which I think is incorrect.
Jul 05 2013
On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.You could just forward to an implementation template, passing explicitly the arguments. This is kind of like the "take" function making an explicit call to "Take!T" I guess. In any case, this is what I mean. -------- void foo(T)(T t) { fooImpl!(Unqual!T)(t); } void fooImpl(T)(T t) { static assert(is(Unqual!T == T)); //do it } void main() { const(int) x; foo(x); } -------- This should do what you want. foo should be completly inlined away I believe. It is a tiny bit hackish, but should work. You'll generate all flavors of foo, but only Unqual'd versions of fooImpl (which is what you care about). You can also add some "isPrimitivee!T" if you want or something. Not sure how this deals with "shared" ? In any case, you asked for "Unqual", so that's what you get ;)
Jul 06 2013
On Saturday, 6 July 2013 at 08:42:45 UTC, monarch_dodra wrote:On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:Hum.. just realized you explicitly wanted to do this via template deduction. I'm not sure then :/ Sorry. Hope my workaround will be useful for you? Chances are you probably already had it worked out.Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given.You could just forward to an implementation template, passing explicitly the arguments.
Jul 06 2013
On 6 July 2013 18:45, monarch_dodra <monarchdodra gmail.com> wrote:On Saturday, 6 July 2013 at 08:42:45 UTC, monarch_dodra wrote:Nope, still have no good idea of my own. My first 2 impulses were the best shot I had, but they didn't work. Forwarding to an Impl template doesn't really address my problem. I still end out with bucket loads of instantiations, and double-call performance will really suffer in debug builds, which might be okay if these were big functions, but they're many trivial functions, so the cost is very high. I'm getting the distinct feeling that what I want is not actually possible. Which I have to say, is rather surprising.On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:Hum.. just realized you explicitly wanted to do this via template deduction. I'm not sure then :/ Sorry. Hope my workaround will be useful for you? Chances are you probably already had it worked out.Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given.You could just forward to an implementation template, passing explicitly the arguments.
Jul 06 2013
On Saturday, 6 July 2013 at 10:34:04 UTC, Manu wrote:I'm getting the distinct feeling that what I want is not actually possible. Which I have to say, is rather surprising.This seems like a defect of the language. Perhaps a new language feature is needed. Something like this: void foo(inout T)(T t) { t.mutate(); } Pass a variable of type S, const(S), or mutable(S), and the function signature becomes: void foo(S); And then fails if your argument cannot be converted to mutable S.
Jul 06 2013
On Saturday, 6 July 2013 at 11:23:08 UTC, TommiT wrote:Pass a variable of type S, const(S), or mutable(S) ...That should have been immutable(S)
Jul 06 2013
On 6 July 2013 21:23, TommiT <tommitissari hotmail.com> wrote:On Saturday, 6 July 2013 at 10:34:04 UTC, Manu wrote:The way that makes the most sense to me is: void f(T)(Unqual!T t) {} Given an immutable(T) for instance that I want to call with, it would instantiate the template with the signature void f(T), and then attempt to call it with the immutable(T). If immutable(T) is not convertible to the argument T, then it would produce a compile error as if attempting to call any such function that just receives T. The point here is that I want more control over the signature of the instantiated template (reduce the number of permutations generated for various calls). Template blow-out is perhaps the biggest and most well known day-to-day problem in C++, and tools like this may be very valuable to mitigate the disaster.I'm getting the distinct feeling that what I want is not actually possible. Which I have to say, is rather surprising.This seems like a defect of the language. Perhaps a new language feature is needed. Something like this: void foo(inout T)(T t) { t.mutate(); } Pass a variable of type S, const(S), or mutable(S), and the function signature becomes: void foo(S); And then fails if your argument cannot be converted to mutable S.
Jul 06 2013
On Saturday, 6 July 2013 at 11:35:28 UTC, Manu wrote:On 6 July 2013 21:23, TommiT <tommitissari hotmail.com> wrote:That would look more sensible, but I don't think that's implementable if any template could be used in the place of 'Unqual'. For example: struct Soo(T, int v) {} template Too(T, int x) { alias Too = Soo!(T, x * x - 102); } void foo(T, n)(Too!(T, n) var) {} void main() { Soo!(int, 42) var; foo(var); } It would be pretty difficult for the compiler to figure out during the instantiation of foo that T == int and n == 12. I don't know, but I suspect it's pretty impossible to implement a compiler that could figure out those template parameters for every possible scenario.On Saturday, 6 July 2013 at 10:34:04 UTC, Manu wrote:The way that makes the most sense to me is: void f(T)(Unqual!T t) {}I'm getting the distinct feeling that what I want is not actually possible. Which I have to say, is rather surprising.This seems like a defect of the language. Perhaps a new language feature is needed. Something like this: void foo(inout T)(T t) { t.mutate(); } Pass a variable of type S, const(S), or mutable(S), and the function signature becomes: void foo(S); And then fails if your argument cannot be converted to mutable S.
Jul 06 2013
The way that makes the most sense to me is: void f(T)(Unqual!T t) {} Given an immutable(T) for instance that I want to call with, it would instantiate the template with the signature void f(T), and then attempt to call it with the immutable(T). If immutable(T) is not convertible to the argument T, then it would produce a compile error as if attempting to call any such function that just receives T. The point here is that I want more control over the signature of the instantiated template (reduce the number of permutations generated for various calls). Template blow-out is perhaps the biggest and most well known day-to-day problem in C++, and tools like this may be very valuable to mitigate the disaster.It seems that your code works if you put the Template Type explicit: ---- import std.stdio; import std.traits : Unqual; void foo(T)(Unqual!T a) { writeln(typeof(a).stringof, " <-> ", T.stringof); } void main() { int a; const int b; immutable int c; //foo(c); /// Error foo!(typeof(a))(a); foo!(typeof(b))(b); foo!(typeof(c))(c); } ----
Jul 06 2013
On 6 July 2013 22:27, Namespace <rswhite4 googlemail.com> wrote:The way that makes the most sense to me is:Indeed, hence my point that the type deduction is the key issue here. It should be possible... maybe a bit tricky though.void f(T)(Unqual!T t) {} Given an immutable(T) for instance that I want to call with, it would instantiate the template with the signature void f(T), and then attempt to call it with the immutable(T). If immutable(T) is not convertible to the argument T, then it would produce a compile error as if attempting to call any such function that just receives T. The point here is that I want more control over the signature of the instantiated template (reduce the number of permutations generated for various calls). Template blow-out is perhaps the biggest and most well known day-to-day problem in C++, and tools like this may be very valuable to mitigate the disaster.It seems that your code works if you put the Template Type explicit: ---- import std.stdio; import std.traits : Unqual; void foo(T)(Unqual!T a) { writeln(typeof(a).stringof, " <-> ", T.stringof); } void main() { int a; const int b; immutable int c; //foo(c); /// Error foo!(typeof(a))(a); foo!(typeof(b))(b); foo!(typeof(c))(c); } ----
Jul 06 2013
On 07/06/2013 04:06 PM, Manu wrote:... It seems that your code works if you put the Template Type explicit: ---- import std.stdio; import std.traits : Unqual; void foo(T)(Unqual!T a) { writeln(typeof(a).stringof, " <-> ", T.stringof); } void main() { int a; const int b; immutable int c; //foo(c); /// Error foo!(typeof(a))(a); foo!(typeof(b))(b); foo!(typeof(c))(c); } ---- Indeed, hence my point that the type deduction is the key issue here. It should be possible... maybe a bit tricky though.The key issue is that the syntax void foo(T)(Unqual!T a); denotes roughly the opposite of what you think it denotes. Basically, inference is instructed to find a T, such that Unqual!T is the argument type.
Jul 06 2013
On 7 July 2013 00:49, Timon Gehr <timon.gehr gmx.ch> wrote:On 07/06/2013 04:06 PM, Manu wrote:Perhaps what you say is true, but does that mean something to the effect of what I'm trying to demonstrate is impossible? I feel like it would be a much cleaner solution than any of the others presented in this thread...... It seems that your code works if you put the Template Type explicit: ---- import std.stdio; import std.traits : Unqual; void foo(T)(Unqual!T a) { writeln(typeof(a).stringof, " <-> ", T.stringof); } void main() { int a; const int b; immutable int c; //foo(c); /// Error foo!(typeof(a))(a); foo!(typeof(b))(b); foo!(typeof(c))(c); } ---- Indeed, hence my point that the type deduction is the key issue here. It should be possible... maybe a bit tricky though.The key issue is that the syntax void foo(T)(Unqual!T a); denotes roughly the opposite of what you think it denotes. Basically, inference is instructed to find a T, such that Unqual!T is the argument type.
Jul 06 2013
On Saturday, 6 July 2013 at 23:11:26 UTC, Manu wrote:[..] I feel like it would be a much cleaner solution than any of the others presented in this thread...I think Artur's solution is the cleanest one because it changes the default behaviour to a more sensible one: it is useful to keep the full type information only if the type isn't implicitly convertible from immutable to mutable and back again... otherwise keeping the full type information along only causes inconvenience.
Jul 06 2013
On 7 July 2013 16:02, TommiT <tommitissari hotmail.com> wrote:On Saturday, 6 July 2013 at 23:11:26 UTC, Manu wrote:Maybe so, but I just have my suspicions that it will never fly, and I think a solution here is actually pretty important. This is demonstrably one of the biggest issues with C++, and since templates are so much more convenient in D, I expect time will show it to be far worse in D than it already is in C++.[..] I feel like it would be a much cleaner solution than any of the others presented in this thread...I think Artur's solution is the cleanest one because it changes the default behaviour to a more sensible one: it is useful to keep the full type information only if the type isn't implicitly convertible from immutable to mutable and back again... otherwise keeping the full type information along only causes inconvenience.
Jul 06 2013
On Sunday, 7 July 2013 at 06:22:17 UTC, Manu wrote:On 7 July 2013 16:02, TommiT <tommitissari hotmail.com> wrote:You think it won't fly because it's a breaking change? I also think a solution is needed. Earlier you said that your suggestion either is implementable or it isn't. If you put it that way, the answer must be that it is implementable, because if a problem is computable then it is possible to write a compiler that can compute it given that there's an ambiguous solution to the problem. But since compiler writers are only human, I think there might the third possibility which is that it's just a too big of an undertaking for a small group of humans to write that compiler. For example here's an example where the compiler would need to solve the equation: cast(int) (3.5 ^^ x * x ^^ 4 + 5 * x) == 206 for x. And not only does the compiler need to figure out an answer to the problem, it needs to figure out that there aren't multiple answers to the problem. struct Soo(int n) { } template Too(int x) { alias Too = Soo!(cast(int) (3.5 ^^ x * x ^^ 4 + 5 * x)); } void foo(int n)(Too!n) { } void main() { Soo!206 s; foo(s); // should call foo!2(s) }I think Artur's solution is the cleanest one [..]Maybe so, but I just have my suspicions that it will never fly, and I think a solution here is actually pretty important.
Jul 07 2013
On 07/07/13 08:22, Manu wrote:On 7 July 2013 16:02, TommiT <tommitissari hotmail.com <mailto:tommitissari hotmail.com>> wrote: On Saturday, 6 July 2013 at 23:11:26 UTC, Manu wrote: [..] I feel like it would be a much cleaner solution than any of the others presented in this thread... I think Artur's solution is the cleanest one because it changes the default behaviour to a more sensible one: it is useful to keep the full type information only if the type isn't implicitly convertible from immutable to mutable and back again... otherwise keeping the full type information along only causes inconvenience. Maybe so, but I just have my suspicions that it will never fly, and I think a solution here is actually pretty important. This is demonstrably one of the biggest issues with C++, and since templates are so much more convenient in D, I expect time will show it to be far worse in D than it already is in C++.It's like the virtual-by-default situation - if the default isn't fixed everybody will ignoring this issue, until it actually becomes a problem. Then they (or more likely somebody else) will have tp do a clean up pass, and then the cycle will repeat. Almost nobody will start with auto f(T=Unqual!_T)(_T val) { /*...*/ } // example new syntax Fixing "just" IFTI should be relatively safe, affecting only a tiny amount of code, if any. These kind of const changes happen more or less randomly anyway, from the POV of the called function. "const a=1; f(a+a); f(a+1);" -- this already creates two different instances of 'f(T)(T)". Yeah, fixing the expression types would be an even better idea (so that "const int + const int" == "int" etc) but that could have a larger impact, as it would affect a lot of 'auto' declarations. artur
Jul 07 2013
Am Sun, 7 Jul 2013 00:06:26 +1000 schrieb Manu <turkeyman gmail.com>:On 6 July 2013 22:27, Namespace <rswhite4 googlemail.com> wrote:If you wanted to save on template instantiations for every possible attribute combination, you are doing it wrong. Those are already 3 duplicate templates with binary identical functions foo(int a) in them, which makes me cry on the inside. -- MarcoThe way that makes the most sense to me is:Indeed, hence my point that the type deduction is the key issue here. It should be possible... maybe a bit tricky though.void f(T)(Unqual!T t) {} Given an immutable(T) for instance that I want to call with, it would instantiate the template with the signature void f(T), and then attempt to call it with the immutable(T). If immutable(T) is not convertible to the argument T, then it would produce a compile error as if attempting to call any such function that just receives T. The point here is that I want more control over the signature of the instantiated template (reduce the number of permutations generated for various calls). Template blow-out is perhaps the biggest and most well known day-to-day problem in C++, and tools like this may be very valuable to mitigate the disaster.It seems that your code works if you put the Template Type explicit: ---- import std.stdio; import std.traits : Unqual; void foo(T)(Unqual!T a) { writeln(typeof(a).stringof, " <-> ", T.stringof); } void main() { int a; const int b; immutable int c; //foo(c); /// Error foo!(typeof(a))(a); foo!(typeof(b))(b); foo!(typeof(c))(c); } ----
Jul 07 2013
On 07/07/2013 01:19 PM, Marco Leise wrote:If you wanted to save on template instantiations for every possible attribute combination, you are doing it wrong. Those are already 3 duplicate templates with binary identical functions foo(int a) in them, which makes me cry on the inside.There is a linker optimization to get rid of the duplicates. http://msdn.microsoft.com/en-us/library/bxwfs976(v=vs.110).aspx
Jul 07 2013
On Monday, 8 July 2013 at 03:03:30 UTC, Martin Nowak wrote:On 07/07/2013 01:19 PM, Marco Leise wrote:Linker alone can't possibly know all required information to make a proper clean up - and duplicates are not main bloat cause. It needs to work in team with a compiler, possibly with a help of a type system.If you wanted to save on template instantiations for every possible attribute combination, you are doing it wrong. Those are already 3 duplicate templates with binary identical functions foo(int a) in them, which makes me cry on the inside.There is a linker optimization to get rid of the duplicates. http://msdn.microsoft.com/en-us/library/bxwfs976(v=vs.110).aspx
Jul 08 2013
On Saturday, 6 July 2013 at 11:35:28 UTC, Manu wrote:The way that makes the most sense to me is: void f(T)(Unqual!T t) {}But my syntax makes pretty much sense too: void foo(inout T)(T var) { var = 42; } void main() { foo!(int)(1); foo!(const int)(2); foo!(immutable int)(3); } For all those calls to foo, the type parameter T is deduced to be int, and the function template instantiates to the signature: void foo(int var); The function template foo accepts int, const(int) and immutable(int) as the type parameter, because they're all convertible to inout(int). The same kind of logic can be seen with runtime arguments, when they are qualified with something: void bar(T)(const T var) { } void main() { const int n; bar(n); } Here, similarly, the type of T is deduced to be int, even though the argument passed in is const(int).
Jul 06 2013
On Saturday, 6 July 2013 at 12:51:18 UTC, TommiT wrote:The function template foo accepts int, const(int) and immutable(int) as the type parameter, because they're all convertible to inout(int).I didn't mean _convertible_ to inout, but rather that inout _accepts_ const, immutable or nothing.
Jul 06 2013
On Saturday, 6 July 2013 at 12:51:18 UTC, TommiT wrote:On Saturday, 6 July 2013 at 11:35:28 UTC, Manu wrote:Some more details of this proposed feature: inout(T) foo(inout T)(T a, T b) { return var; } void bar(inout T)(T a, T b) { a = b; } void main() { const int con; immutable int imm; foo(con, con); // OK (returns const int) foo(con, imm); // Error: returned inout(T) is ambiguous bar(con, con); // OK bar(con, imm); // OK (no ambiguity because inout not used) }The way that makes the most sense to me is: void f(T)(Unqual!T t) {}But my syntax makes pretty much sense too: void foo(inout T)(T var) { var = 42; } void main() { foo!(int)(1); foo!(const int)(2); foo!(immutable int)(3); }
Jul 06 2013
On Saturday, 6 July 2013 at 13:30:02 UTC, TommiT wrote:Some more details of this proposed feature: inout(T) foo(inout T)(T a, T b) { return var; } void bar(inout T)(T a, T b) { a = b; } void main() { const int con; immutable int imm; foo(con, con); // OK (returns const int) foo(con, imm); // Error: returned inout(T) is ambiguous bar(con, con); // OK bar(con, imm); // OK (no ambiguity because inout not used) }Another approach, if we wanted a different syntax, would be to add a new keyword 'mutable'. Like immutable overrides const, mutable would override both const and immutable: static assert(is(mutable(const(int)) == int)); static assert(is(mutable(immutable(int)) == int)); Here's how it would look: T foo(T)(mutable(T) a, mutable(T) b) { a = b = 123; return a; } void main() { const int con; immutable int imm; foo(con, con); // OK (returns const int) foo(con, imm); // Error: T is ambiguous }
Jul 06 2013
I feel like it would be much better to do this with type deduction, than to introduce new keywords and concepts... Saying type deduction is hard is probably not accurate. It's either possible, or impossible. If it's mechanically possible, then it's easy for the compiler to do. If it's impossible (or ambiguous), then an error would be thrown in those cases. I'm sure the compiler is capable of working out if it's impossible and complaining. On 6 July 2013 23:50, TommiT <tommitissari hotmail.com> wrote:On Saturday, 6 July 2013 at 13:30:02 UTC, TommiT wrote:Some more details of this proposed feature: inout(T) foo(inout T)(T a, T b) { return var; } void bar(inout T)(T a, T b) { a = b; } void main() { const int con; immutable int imm; foo(con, con); // OK (returns const int) foo(con, imm); // Error: returned inout(T) is ambiguous bar(con, con); // OK bar(con, imm); // OK (no ambiguity because inout not used) }Another approach, if we wanted a different syntax, would be to add a new keyword 'mutable'. Like immutable overrides const, mutable would override both const and immutable: static assert(is(mutable(const(int)) == int)); static assert(is(mutable(immutable(**int)) == int)); Here's how it would look: T foo(T)(mutable(T) a, mutable(T) b) { a = b = 123; return a; } void main() { const int con; immutable int imm; foo(con, con); // OK (returns const int) foo(con, imm); // Error: T is ambiguous }
Jul 06 2013
On Saturday, 6 July 2013 at 13:30:02 UTC, TommiT wrote:Some more details of this proposed feature: inout(T) foo(inout T)(T a, T b) { return var; } void bar(inout T)(T a, T b) { a = b; } void main() { const int con; immutable int imm; foo(con, con); // OK (returns const int) foo(con, imm); // Error: returned inout(T) is ambiguous bar(con, con); // OK bar(con, imm); // OK (no ambiguity because inout not used) }Some more details of a bit more complicated use case: E foo(inout T : E[3], E)(T arr) { return arr[0]; } E bar(inout T : inout(E)[3], E)(T arr) { return arr[0]; } void main() { immutable int[3] iarr; foo(iarr); // returns an immutable int bar(iarr); // returns an int }
Jul 06 2013
On 07/06/13 13:35, Manu wrote:The point here is that I want more control over the signature of the instantiated template (reduce the number of permutations generated for various calls). Template blow-out is perhaps the biggest and most well known day-to-day problem in C++, and tools like this may be very valuable to mitigate the disaster.I was going to reply that while template bloat is a real issue, it is the compiler that should deal with this (by always inlining trivial function). Then I remembered that you mentioned that you want this only for "primitive" types, and realized what your real problem is. IFTI and value types. For example: auto f(T...)(T a) {/*...*/} int a; const int b; immutable int c; f(a); f(b); f(c); This creates three instances of 'f'. And it gets much worse with a larger number of arguments... Typically all instantiations will be identical, except in cases where the 'f' implementation tries to mutate the argument(s). (This mutation is always safe when T is passed by value and T does not contain external references) IFTI should just strip the qualifiers from all value-passed POD types which contain no refs (ie no pointers, slices and classes inside). Doing that will significantly reduce template bloat, at practically no cost. It *is* a change in behavior, but there shouldn't be much code out there that sensibly relies on the 1:1 type propagation. Explicitly specifying a type will still be possible using 'typeof'. Note that this is already done for pointers; calling 'f' with the following: immutable(void)* a; const immutable(void)* b; immutable immutable(void)* c; will not create three separate instances. This is possible in the pointer case because the compiler can get away with manipulating the type. Doing this for structs would be more complicated; fixing the primitive types- (and maybe PODs) case isn't. BTW, the way D's 'auto' works contributes to the problem: const int a; auto b = a+a; auto c = a+1; f(b); f(c); uses two different 'f' instances. artur
Jul 06 2013
On Saturday, 6 July 2013 at 15:05:51 UTC, Artur Skawina wrote:...It is not that simple. Consider: void f(T)(T t) { static if (T == Unqual!T) // one function body else // completely different one } Currently every template instance is completely independent and tied to exact type. I don't know of any tool to express "group of related types" concept in D other than built-in "inout".
Jul 06 2013
On 07/06/13 17:10, Dicebot wrote:On Saturday, 6 July 2013 at 15:05:51 UTC, Artur Skawina wrote:This is exactly why i did mention that this is a breaking change and that "there shouldn't be much code out there that sensibly relies on the 1:1 type propagation." Do you think this would be a problem /in practice/? Right now IFTI chooses an expensive alternative, which is rarely required. You have to do more work to "undo" the unnecessary type propagation. This is the wrong default. "auto" makes things even worse, by increasing the number of different-but-compatible types and causing them to propagate. artur...It is not that simple. Consider: void f(T)(T t) { static if (T == Unqual!T) // one function body else // completely different one } Currently every template instance is completely independent and tied to exact type. I don't know of any tool to express "group of related types" concept in D other than built-in "inout".
Jul 06 2013
On Saturday, 6 July 2013 at 15:38:45 UTC, Artur Skawina wrote:Do you think this would be a problem /in practice/?To me it seems highly unlikely that this would break any code. +1 from me.
Jul 06 2013
On Saturday, 6 July 2013 at 15:38:45 UTC, Artur Skawina wrote:Do you think this would be a problem /in practice/?Yes. Honestly, lot of problems. I can easily imagine template function that modifies its argument in-place for a mutable qualifier and allocates a copy for an immutable one. Templates that introspect their parameter type to format some string. Different algorithms for processing mutable and immutable versions of same container. And it is not just code breakage - you propose some magic instead of simple well-defined semantics and so far I don't even see the detailed description how new system should behave. Currently template body is defined only by its parameter set, your proposal changes that as far as I understand. I think more reasonable approach is to define "inout" in template parameter specialization to mean "I don't care what it is, make it compiler error to rely on any behavior difference between them". Don't know what to do about mangling though. Will also most likely need to prohibit and direct reference to T. It looks like extremely complex way to solve only one minor part of general problem (template bloat).
Jul 06 2013
On Saturday, 6 July 2013 at 18:07:08 UTC, Dicebot wrote:On Saturday, 6 July 2013 at 15:38:45 UTC, Artur Skawina wrote:He's talking about changing the semantics only on POD types, like int, struct of ints, static array of ints... only types that can implicitly convert from immutable to mutable.Do you think this would be a problem /in practice/?Yes. Honestly, lot of problems. I can easily imagine template function that modifies its argument in-place for a mutable qualifier and allocates a copy for an immutable one. Templates that introspect their parameter type to format some string. Different algorithms for processing mutable and immutable versions of same container.And it is not just code breakage - you propose some magic instead of simple well-defined semantics and so far I don't even see the detailed description how new system should behave.As I understand it, it would be like what C++ does, but only for POD types. Just take const, immutable, whatever away when they're passed by value.
Jul 06 2013
06-Jul-2013 22:54, TommiT пишет:On Saturday, 6 July 2013 at 18:07:08 UTC, Dicebot wrote:I've seen an aggressive proposal back in the day to just do a shallow unqual on all aggregates passed by value.On Saturday, 6 July 2013 at 15:38:45 UTC, Artur Skawina wrote:He's talking about changing the semantics only on POD types, like int, struct of ints, static array of ints... only types that can implicitly convert from immutable to mutable.Do you think this would be a problem /in practice/?Yes. Honestly, lot of problems. I can easily imagine template function that modifies its argument in-place for a mutable qualifier and allocates a copy for an immutable one. Templates that introspect their parameter type to format some string. Different algorithms for processing mutable and immutable versions of same container.-- Dmitry OlshanskyAnd it is not just code breakage - you propose some magic instead of simple well-defined semantics and so far I don't even see the detailed description how new system should behave.As I understand it, it would be like what C++ does, but only for POD types. Just take const, immutable, whatever away when they're passed by value.
Jul 06 2013
Am Sun, 07 Jul 2013 01:04:52 +0400 schrieb Dmitry Olshansky <dmitry.olsh gmail.com>:I've seen an aggressive proposal back in the day to just do a shallow unqual on all aggregates passed by value.I came to the same conclusion. If feasible, type inference should always produce tail-const versions. Well, Rebinable!T then for classes. ;) It is really annoying to have value types like ints passed in as immutable/const, just because they were at the call site. Even for array slices arguments it's sometimes neat to be able to shrink the passed slice to remove elements that you don't need to process (whitespace, zeroes, ...) -- Marco
Jul 07 2013
On Saturday, 6 July 2013 at 18:54:16 UTC, TommiT wrote:He's talking about changing the semantics only on POD types, like int, struct of ints, static array of ints... only types that can implicitly convert from immutable to mutable.Than it does not really solve anything. Have you measured how much template bloat comes from common meta-programming tools like std.algorithm and how much - from extra instances for qualified POD types? It is better to solve broad problem instead and get this special case for free, not add more special cases in a desperate attempts to contain it.
Jul 07 2013
On Sunday, 7 July 2013 at 17:48:17 UTC, Dicebot wrote:On Saturday, 6 July 2013 at 18:54:16 UTC, TommiT wrote:Basically all I know about the performance issue being discussed here is that: "Code bloat is bad, m'kay". But note that Artur's suggestion would also change language semantics to a more sensible and convenient default. For example: void foo(T)(T value) if (isIntegral!T) { value = 42; } ...I can feel free to mutate 'value' without having to worry about somebody breaking my function by calling it with an argument of type like const(int), immutable(short), shared(long) ...He's talking about changing the semantics only on POD types, like int, struct of ints, static array of ints... only types that can implicitly convert from immutable to mutable.Than it does not really solve anything. Have you measured how much template bloat comes from common meta-programming tools like std.algorithm and how much - from extra instances for qualified POD types? It is better to solve broad problem instead and get this special case for free, not add more special cases in a desperate attempts to contain it.
Jul 07 2013
Am Sat, 06 Jul 2013 17:10:24 +0200 schrieb "Dicebot" <public dicebot.lv>:On Saturday, 6 July 2013 at 15:05:51 UTC, Artur Skawina wrote:Hmm, this inout stuff sounds like a possible solution... -- Marco...It is not that simple. Consider: void f(T)(T t) { static if (T == Unqual!T) // one function body else // completely different one } Currently every template instance is completely independent and tied to exact type. I don't know of any tool to express "group of related types" concept in D other than built-in "inout".
Jul 07 2013
On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:...If this is about template bloat I think much better is to address problem in general. For example, internal linkage or strict export requirements - anything that will allow to inline and completely eliminate trivial templates leaving no traces of it in final executable.
Jul 06 2013
Am Sat, 06 Jul 2013 17:24:23 +0200 schrieb "Dicebot" <public dicebot.lv>:On Saturday, 6 July 2013 at 01:35:09 UTC, Manu wrote:Less duplicate instantiations results in faster compile times. An area where DMD is really good and should strive to keep its pole position. -- Marco...If this is about template bloat I think much better is to address problem in general. For example, internal linkage or strict export requirements - anything that will allow to inline and completely eliminate trivial templates leaving no traces of it in final executable.
Jul 07 2013
06-Jul-2013 05:34, Manu пишет:Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {}The thing is that if even if you somehow force your way past IFTI what would be generated is: f!(const int)(int arg); f!(immutable int)(int arg); f!(shared int)(int arg); f!(const shared int)(int arg); Which IMHO falls short of desired goal. Short of using a forwarding thunk (that you don't like, but if there was force_inline?) we'd have to hack the compiler.Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int).I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.-- Dmitry Olshansky
Jul 07 2013
On 7 July 2013 22:31, Dmitry Olshansky <dmitry.olsh gmail.com> wrote:06-Jul-2013 05:34, Manu =D0=BF=D0=B8=D1=88=D0=B5=D1=82:.Okay, so I feel like this should be possible, but I can't make it work..=Hmmm, this is an interesting point. I initially thought this was desirable, it could be useful. But now that you point it out, I guess the point you are making is that they will all mangle separately anyway? That seems problematic, because since all have the same return value and physical arguments, how does the compiler choose an overload to call in various circumstances? I think I (mistakenly?) presumed they would all mangle the same, since they have the same physical signature (return value + physical args), and therefore all be the same function (eliminating the duplicates).I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {}The thing is that if even if you somehow force your way past IFTI what would be generated is: f!(const int)(int arg); f!(immutable int)(int arg); f!(shared int)(int arg); f!(const shared int)(int arg); Which IMHO falls short of desired goal. Short of using a forwarding thunk (that you don't like, but if there was force_inline?) we'd have to hack the compiler.
Jul 07 2013
On 2013-07-08 04:10, Manu wrote:Hmmm, this is an interesting point. I initially thought this was desirable, it could be useful. But now that you point it out, I guess the point you are making is that they will all mangle separately anyway? That seems problematic, because since all have the same return value and physical arguments, how does the compiler choose an overload to call in various circumstances? I think I (mistakenly?) presumed they would all mangle the same, since they have the same physical signature (return value + physical args), and therefore all be the same function (eliminating the duplicates).Template arguments are part of the mangled name. Example: int foo (string a) (int b) { return b; } int bar (int b) { return b; } assert(foo!("a").mangleof == "_D4main17__T3fooVAyaa1_61Z3fooFNaNbNfiZi"); assert(foo!("abcdefg").mangleof == "_D4main29__T3fooVAyaa7_61626364656667Z3fooFNaNbNfiZi"); assert(bar.mangleof == "_D4main3barFiZi"); -- /Jacob Carlborg
Jul 08 2013
On 07/06/2013 03:34 AM, Manu wrote:Okay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int).I can't find the Bugzilla entry right now, but we discussed before why it is not generally possible to deduce A from a match of type B with Template!A. Basically you'd need the inverse of the Template and the type mapping would need to be bijectiv. What does work though and looks similar is to deduce A from a match of Template!B with Template!A.I don't want a million permutations of the template function for each combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.There is a linker optimization that would get rid of the duplicates. http://stackoverflow.com/questions/15168924/gcc-clang-merging-functions-with-identical-instructions-comdat-folding I came up with "out-of-bound" template instantiations to avoid unneeded instantiations. What you do is to forward common template code to another template that is next to the actual template. The next to is important because it allows to merge identical instantiations. For example this idiom is useful when you pass additional arguments to your template, e.g. __FILE__ and __LINE__. void _f(T)(T a) {} void f(T)(T a) { return _f!(Unqual!T)(a); } template _f(CommonArgs) { enum _f = foo!CommonArgs; } template f(CommonArgs, MoreArgs) { static assert(bar!MoreArgs); enum f = _f!CommonArgs; }
Jul 08 2013
On 8 July 2013 21:16, Martin Nowak <code dawg.eu> wrote:On 07/06/2013 03:34 AM, Manu wrote:I'm not sure I follow. Can you demonstrate? I don't want a million permutations of the template function for eachOkay, so I feel like this should be possible, but I can't make it work... I want to use template deduction to deduce the argument type, but I want the function arg to be Unqual!T of the deduced type, rather than the verbatim type of the argument given. I've tried: void f(T : Unqual!U, U)(T a) {} and: void f(T)(Unqual!T a) {} Ie, if called with: const int x; f(x); Then f() should be generated void f(int) rather than void f(const int). I can't find the Bugzilla entry right now, but we discussed before whyit is not generally possible to deduce A from a match of type B with Template!A. Basically you'd need the inverse of the Template and the type mapping would need to be bijectiv. What does work though and looks similar is to deduce A from a match of Template!B with Template!A.I really hate relying on linker optimisations to clean up mess like this, which simply shouldn't exist in the first place. Debugging is critically important. In my experience, most programmers spend 90% of their time debugging, and that means debug builds still need to be usable. Unoptimised code isn't THAT much slower/bigger by nature. But there's a big different between 10 times slower and 100 times slower. Likewise, there's also a big difference between twice as big, and 10 times as big. Depending on the optimiser to eliminate this sort of duplication tends your debug code towards the latter. At my prior company, we were very proud that our debug build still ran at ~10fps (playable/testable)... most companies debug builds run closer to 1fps or less, which means you can't practically test AND debug your code. It's very hard to reveal the bug you're chasing if you can't physically test the build. Every new person we employed was amazed, and commented on this. It was without doubt, a strategic advantage for our company. And the reason we succeeded to this end, was simply because we banned C++ (well, most of it) :/ D makes templates so convenient, one can imagine the typical situation might even be worse than typical C++. So instead, D needs to take the opportunity to offer tools to allow the programmer to express what they actually want, rather than generating copious bloat, and expecting optimisation passes to clean it up. Forwarding to secondary functions like this is really horrible. So now my 'optimisation' (it's not an optimisation, it's just what I want to do in the first place) requires that I mutilate my code. What are the sensible naming conventions for this scheme? Does this really improve readability? What about find-in-files/go-to-definition? I'm sure we can do better than this... actually, we must. I won't accept this. Bloat should be factored out by design. It's not something that should be ignored, and then attempted to clean up later. So I'm back where I started :/ Again, from the top, I want more control over the template argument deduction. I want this for example: void f(T)(Unqual!T arg); When called with: int x; const(int) cx; immutable(int) ix; shared(int) sx; f(x); f(cx); f(ix); f(sx); All calls deduce the template instantiation: void f(int)(int arg); I can see why my example syntax doesn't work. The 'T' in the argument list is actually the inverse of what I want, like you say. But I'm sure there are tweaks on the expression that could possibly make sense somehow. Basically, is it possible? Is there another approach that could produce the same outcome; that is, having more control over the template argument deduction, and consequently, the resulting template instantiation? Some creative thought can surely crack this nut. I think this is a severe issue, and worth some serious attention. I'm rather surprised how few of the heavy weights have commented on this topic :(combination of const/immutabe/shared/etc, which especially blows out when the function has 2 or more args. Note: T may only be a primitive type. Obviously const(int*) can never be passed to int*.There is a linker optimization that would get rid of the duplicates. http://stackoverflow.com/**questions/15168924/gcc-clang-** merging-functions-with-**identical-instructions-comdat-**folding<http://stackoverflow.com/questions/15168924/gcc-clang-merging-functions-with-identical-instructions-comdat-folding> I came up with "out-of-bound" template instantiations to avoid unneeded instantiations. What you do is to forward common template code to another template that is next to the actual template. The next to is important because it allows to merge identical instantiations. For example this idiom is useful when you pass additional arguments to your template, e.g. __FILE__ and __LINE__. void _f(T)(T a) {} void f(T)(T a) { return _f!(Unqual!T)(a); } template _f(CommonArgs) { enum _f = foo!CommonArgs; } template f(CommonArgs, MoreArgs) { static assert(bar!MoreArgs); enum f = _f!CommonArgs; }
Jul 13 2013