www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Fun with templates

reply Manu <turkeyman gmail.com> writes:
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
next sibling parent reply "Daniel Murphy" <yebblies nospamgmail.com> writes:
"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
parent reply Manu <turkeyman gmail.com> writes:
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...
 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; ... }

That doesn't do what I want at all. The signature is still f(T) not f(Unqual!T).
Jul 05 2013
next sibling parent "Daniel Murphy" <yebblies nospamgmail.com> writes:
"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:

 "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; ... }

That doesn't do what I want at all. The signature is still f(T) not f(Unqual!T).

Yeah it's possible I didn't finish reading your post.
Jul 05 2013
prev sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
 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
parent reply Manu <turkeyman gmail.com> writes:
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) not
 f(Unqual!T).

For non-const, const and immutable inout would do it. void f(T)(inout T var)

Not if there's more than 1 argument, and 'inout T' is still not 'T'.
Jul 06 2013
parent reply Marco Leise <Marco.Leise gmx.de> writes:
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:
 
 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)

Not if there's more than 1 argument, and 'inout T' is still not 'T'.

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! -- Marco
Jul 07 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
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>:

 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) not
 f(Unqual!T).

For non-const, const and immutable inout would do it. void f(T)(inout T var)

Not if there's more than 1 argument, and 'inout T' is still not 'T'.

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!

const would have the same effect: void f(T)(const T var) { ... } ...but then you can't mutate var.
Jul 07 2013
parent reply Marco Leise <Marco.Leise gmx.de> writes:
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
parent "Tommi" <tommitissari hotmail.com> writes:
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>:

 const would have the same effect:
 
 void f(T)(const T var) { ... }
 
 ...but then you can't mutate var.

That doesn't handle shared.

That seems like a compiler bug to me.
Jul 07 2013
prev sibling next sibling parent reply "finalpatch" <fengli gmail.com> writes:
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
parent Manu <turkeyman gmail.com> writes:
On 6 July 2013 11:42, finalpatch <fengli gmail.com> wrote:

 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); } }

And again, f(T), the signature is wrong. There is also an additional call... twice as slow when non-optimised :/
Jul 05 2013
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
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
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent Manu <turkeyman gmail.com> writes:
On 6 July 2013 11:45, Timon Gehr <timon.gehr gmx.ch> wrote:

 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.

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?
Jul 05 2013
prev sibling next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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:
 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.

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.
Jul 06 2013
parent reply Manu <turkeyman gmail.com> writes:
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:

 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.

You could just forward to an implementation template, passing explicitly the arguments.

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.

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.
Jul 06 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
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
next sibling parent "TommiT" <tommitissari hotmail.com> writes:
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
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On 6 July 2013 21:23, TommiT <tommitissari hotmail.com> wrote:

 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.

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.
Jul 06 2013
next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 6 July 2013 at 11:35:28 UTC, Manu wrote:
 On 6 July 2013 21:23, TommiT <tommitissari hotmail.com> wrote:

 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.

The way that makes the most sense to me is: void f(T)(Unqual!T t) {}

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.
Jul 06 2013
prev sibling next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
 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
parent reply Manu <turkeyman gmail.com> writes:
On 6 July 2013 22:27, Namespace <rswhite4 googlemail.com> 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.

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.
Jul 06 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
parent reply Manu <turkeyman gmail.com> writes:
On 7 July 2013 00:49, Timon Gehr <timon.gehr gmx.ch> wrote:

 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.

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...
Jul 06 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
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
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 7 July 2013 16:02, TommiT <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++.
Jul 06 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Sunday, 7 July 2013 at 06:22:17 UTC, Manu wrote:
 On 7 July 2013 16:02, TommiT <tommitissari hotmail.com> wrote:
 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.

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) }
Jul 07 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
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
prev sibling parent reply Marco Leise <Marco.Leise gmx.de> writes:
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:
 
 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); } ----

Indeed, hence my point that the type deduction is the key issue here. It should be possible... maybe a bit tricky though.

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. -- Marco
Jul 07 2013
parent reply Martin Nowak <code dawg.eu> writes:
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
parent "Dicebot" <public dicebot.lv> writes:
On Monday, 8 July 2013 at 03:03:30 UTC, Martin Nowak wrote:
 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

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.
Jul 08 2013
prev sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
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
next sibling parent "TommiT" <tommitissari hotmail.com> writes:
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
prev sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 6 July 2013 at 12:51:18 UTC, TommiT wrote:
 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); }

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) }
Jul 06 2013
next sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
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
parent Manu <turkeyman gmail.com> writes:
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
prev sibling parent "TommiT" <tommitissari hotmail.com> writes:
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
prev sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
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
parent reply "Dicebot" <public dicebot.lv> writes:
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
next sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 07/06/13 17:10, Dicebot wrote:
 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".

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
Jul 06 2013
next sibling parent "TommiT" <tommitissari hotmail.com> writes:
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
prev sibling parent reply "Dicebot" <public dicebot.lv> writes:
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
parent reply "TommiT" <tommitissari hotmail.com> writes:
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:
 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.

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.
 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
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
06-Jul-2013 22:54, TommiT пишет:
 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:
 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.

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.

I've seen an aggressive proposal back in the day to just do a shallow unqual on all aggregates passed by value.
 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.

-- Dmitry Olshansky
Jul 06 2013
parent Marco Leise <Marco.Leise gmx.de> writes:
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
prev sibling parent reply "Dicebot" <public dicebot.lv> writes:
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
parent "Tommi" <tommitissari hotmail.com> writes:
On Sunday, 7 July 2013 at 17:48:17 UTC, Dicebot wrote:
 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.

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) ...
Jul 07 2013
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
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:
 ...

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".

Hmm, this inout stuff sounds like a possible solution... -- Marco
Jul 07 2013
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
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
parent Marco Leise <Marco.Leise gmx.de> writes:
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:
 ...

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.

Less duplicate instantiations results in faster compile times. An area where DMD is really good and should strive to keep its pole position. -- Marco
Jul 07 2013
prev sibling next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
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
parent reply Manu <turkeyman gmail.com> writes:
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..=


.
 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.

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).
Jul 07 2013
parent Jacob Carlborg <doob me.com> writes:
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
prev sibling parent reply Martin Nowak <code dawg.eu> writes:
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
parent Manu <turkeyman gmail.com> writes:
On 8 July 2013 21:16, Martin Nowak <code dawg.eu> wrote:

 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'm not sure I follow. Can you demonstrate? 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<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; }

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 :(
Jul 13 2013