Re: How to specify a template that uses unqualified type, like any normal function

2017-08-16 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn
On Tuesday, 15 August 2017 at 14:24:57 UTC, Steven Schveighoffer 
wrote:


What IFTI would need is a mechanism to change the parameter 
types to mutable similar to how you can do this:


foo(T)(const(T) t);

This now generates one function for int, const(int), 
immutable(int), and t is const within the function.


What we need is something like:

foo(T)(mutable(T) t) // fictitious type constructor, doesn't 
work.

In fact, that was the first thing I tried, but it doesn't exist.
Would be a pretty useful addition anyway, because it would allow 
(some time in the far future) to move from mutable by default to 
immutable by default.


Or a more general mechanism to modify IFTI when it is deciding 
the parameters to use based on the call.


In my case, I've run into this when I'm trying to use short or 
ubyte, and someone uses literals:


void foo(short s) { ...}

void fooT(T)(T t) { foo(s); }

foo(1); // ok
fooT(1); // error.

It would be nice if there was some way to tell IFTI to infer T 
as short in this case.

Yes, that would also be very nice. And would be easy to solve:
A literal should always be assumed to be the smallest type that 
can represent it, not int. May be, if compatibilitpy to old bad C 
is really still so important, integer propagation can be done 
later on if neccessary, but don't start out with int. That's just 
so oldschool and most of the time just annoying and there's no 
technical reason to do so.




Re: How to specify a template that uses unqualified type, like any normal function

2017-08-15 Thread Steven Schveighoffer via Digitalmars-d-learn

On 8/14/17 6:27 PM, Jonathan M Davis via Digitalmars-d-learn wrote:



As I understand it, sufficiently smart C++ compilers will figure out that
the code for two template instantiations can be the same even if the types
aren't, and they'll merge the definitions in the generated code, so you only
really get one.

This isn't that problem.

I can define a function foo(int), which accepts all forms of int, and 
then *inside the function* I can mutate the parameter.


If I define foo as taking a template parameter T, then the 
implementations that accept just a straight int can modify the local 
copy of the parameter, but implementations that take a const(int) 
cannot. This means your function can't be the same for multiple 
instantiations. But there's no true limitation -- const(int) implicitly 
casts to int.


What IFTI would need is a mechanism to change the parameter types to 
mutable similar to how you can do this:


foo(T)(const(T) t);

This now generates one function for int, const(int), immutable(int), and 
t is const within the function.


What we need is something like:

foo(T)(mutable(T) t) // fictitious type constructor, doesn't work.

Or a more general mechanism to modify IFTI when it is deciding the 
parameters to use based on the call.


In my case, I've run into this when I'm trying to use short or ubyte, 
and someone uses literals:


void foo(short s) { ...}

void fooT(T)(T t) { foo(s); }

foo(1); // ok
fooT(1); // error.

It would be nice if there was some way to tell IFTI to infer T as short 
in this case.


-Steve


Re: How to specify a template that uses unqualified type, like any normal function

2017-08-14 Thread ag0aep6g via Digitalmars-d-learn

On 08/15/2017 12:14 AM, Dominikus Dittes Scherkl wrote:

T foo(T)(T n)
{
static if(!is(Unqual!T == T)) return foo!(Unqual!T)(n);
else
{
// normal implementation
}
}

So it's basically 2 lines of overhead. That's acceptable. The check for 
isImplicitlyConvertible is not necessary, because if it's not it will 
error out anyway.


I'm probably missing something, but copying the parameter to a local 
variable seems simpler than doing it with overloads:



T foo(T)(T n)
{
Unqual!T m = n;
++m;
return m;
}


As this in fact leads to some decrease in code size (as the instances 
for const or shared parameters are now much smaller or in fact 
completely removed by the inliner, much less template code duplication 
happens), I will add this to a lot of templates - seems this will become 
some sort of standard D boilerplate code.


Regarding shared, be aware that dmd lets you copy a value type from 
shared to unshared, but it doesn't emit an atomic load for the copy. So 
it's generally not thread-safe to do it.


Re: How to specify a template that uses unqualified type, like any normal function

2017-08-14 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, August 14, 2017 17:43:44 Dominikus Dittes Scherkl via 
Digitalmars-d-learn wrote:
> On Monday, 14 August 2017 at 15:20:28 UTC, Steven Schveighoffer
>
> wrote:
> > On 8/14/17 9:48 AM, Dominikus Dittes Scherkl wrote:
> > > uint foo(T)(Unqual!T n) // first try
> > > {
> > >
> > > ++n; // modify should be possible
> > > return 42;
> > >
> > > }
> > > Any ideas what I need to do to make this work?
> >
> > This isn't exactly supported. Implicit Function Template
> > Instantiation (IFTI) will deduce the parameters to be the types
> > that you pass in. You can't deduce them and then change the
> > parameter types. This is a limitation of IFTI that I have
> > struggled with in the past.
>
> A little unfortunate, because I would consider this the standard
> usecase.
> You only overload functions if they do something special with
> const or shared or immutable parameters, but for templates you
> get a different implementation for these all the time, and you
> didn't even have a chance to avoid that useless code-bloat? What
> a pitty.

As I understand it, sufficiently smart C++ compilers will figure out that
the code for two template instantiations can be the same even if the types
aren't, and they'll merge the definitions in the generated code, so you only
really get one. And that's the typical solution for this particular problem.
Unfortunately, dmd doesn't have that yet. I don't know what ldc's backend is
able to do on this front, but I suspect that for this to fully work, the
frontend has to be doing it, in which case, ldc will have it when dmd has
it. The other major, related problem is that normally, all of the templates
that get instantiated end up in the final code even if that makes no sense
(most notably, stuff like isInputRange). So, there's definitely work to be
done towards reducing template bloat in the D compilers.

- Jonathan M Davis



Re: How to specify a template that uses unqualified type, like any normal function

2017-08-14 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn
On Monday, 14 August 2017 at 17:43:44 UTC, Dominikus Dittes 
Scherkl wrote:
On Monday, 14 August 2017 at 15:20:28 UTC, Steven Schveighoffer 
wrote:

What you can do, is:

auto foo(T)(T n) if (is(T == Unqual!T))
{
   // normal implementation
}

auto foo(T)(T n) if (!is(T == Unqual!T) && 
isImplicitlyConvertible!(T, Unqual!T))

{
   return foo!(Unqual!T)(n);
}


Ok, I'll try that out.


Yeah, works fine. I've improved this to

T foo(T)(T n)
{
   static if(!is(Unqual!T == T)) return foo!(Unqual!T)(n);
   else
   {
   // normal implementation
   }
}

So it's basically 2 lines of overhead. That's acceptable. The 
check for isImplicitlyConvertible is not necessary, because if 
it's not it will error out anyway.


As this in fact leads to some decrease in code size (as the 
instances for const or shared parameters are now much smaller or 
in fact completely removed by the inliner, much less template 
code duplication happens), I will add this to a lot of templates 
- seems this will become some sort of standard D boilerplate code.


Thanks for the help!


Re: How to specify a template that uses unqualified type, like any normal function

2017-08-14 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn
On Monday, 14 August 2017 at 15:20:28 UTC, Steven Schveighoffer 
wrote:

On 8/14/17 9:48 AM, Dominikus Dittes Scherkl wrote:
> uint foo(T)(Unqual!T n) // first try
> {
> ++n; // modify should be possible
> return 42;
> }
> Any ideas what I need to do to make this work?

This isn't exactly supported. Implicit Function Template 
Instantiation (IFTI) will deduce the parameters to be the types 
that you pass in. You can't deduce them and then change the 
parameter types. This is a limitation of IFTI that I have 
struggled with in the past.
A little unfortunate, because I would consider this the standard 
usecase.

You only overload functions if they do something special with
const or shared or immutable parameters, but for templates you 
get a different implementation for these all the time, and you 
didn't even have a chance to avoid that useless code-bloat? What 
a pitty.




What you can do, is:

auto foo(T)(T n) if (is(T == Unqual!T))
{
   // normal implementation
}

auto foo(T)(T n) if (!is(T == Unqual!T) && 
isImplicitlyConvertible!(T, Unqual!T))

{
   return foo!(Unqual!T)(n);
}


Ok, I'll try that out.


Re: How to specify a template that uses unqualified type, like any normal function

2017-08-14 Thread Steven Schveighoffer via Digitalmars-d-learn

On 8/14/17 9:48 AM, Dominikus Dittes Scherkl wrote:

if I use fixed-type functions, I can do the following:

uint foo(uint n)
{
++n; // modify n - as this function has received a copy of n, this 
is always possible

return 42;
}

uint bar(const uint n)
{
assert(foo(n)==42);
return 17;
}

void main()
{
bar(3);
}


But if I try the same with a template parameter, it doesn't work:

import std.traits; // Unqual

uint foo(T)(Unqual!T n) // first try
{
++n; // modify should be possible
return 42;
}

uint foo2(T)(T n) // second try
{
++n; // modify fails, as T is const
return 42;
}

uint bar(T)(const T n)
{
assert(foo(n)==42u); // cannot deduce arguments - why?!?
assert(foo2(n)==42u); // here it can deduce the arguments, but the 
function cannot modify n

return 17;
}

void main()
{
bar(3);
}

Any ideas what I need to do to make this work?


This isn't exactly supported. Implicit Function Template Instantiation 
(IFTI) will deduce the parameters to be the types that you pass in. You 
can't deduce them and then change the parameter types. This is a 
limitation of IFTI that I have struggled with in the past.


What you can do, is:

auto foo(T)(T n) if (is(T == Unqual!T))
{
   // normal implementation
}

auto foo(T)(T n) if (!is(T == Unqual!T) && isImplicitlyConvertible!(T, 
Unqual!T))

{
   return foo!(Unqual!T)(n);
}

Hopefully the inliner cuts out the extra call.

-Steve


Re: How to specify a template that uses unqualified type, like any normal function

2017-08-14 Thread Mengu via Digitalmars-d-learn
On Monday, 14 August 2017 at 13:48:36 UTC, Dominikus Dittes 
Scherkl wrote:

if I use fixed-type functions, I can do the following:

uint foo(uint n)
{
   ++n; // modify n - as this function has received a copy of 
n, this is always possible

   return 42;
}

uint bar(const uint n)
{
   assert(foo(n)==42);
   return 17;
}

void main()
{
   bar(3);
}


But if I try the same with a template parameter, it doesn't 
work:


import std.traits; // Unqual

uint foo(T)(Unqual!T n) // first try
{
   ++n; // modify should be possible
   return 42;
}

uint foo2(T)(T n) // second try
{
   ++n; // modify fails, as T is const
   return 42;
}

uint bar(T)(const T n)
{
   assert(foo(n)==42u); // cannot deduce arguments - why?!?
   assert(foo2(n)==42u); // here it can deduce the arguments, 
but the function cannot modify n

   return 17;
}

void main()
{
   bar(3);
}

Any ideas what I need to do to make this work?


hi dominikus

you can call functions as func(arg) when compiler can infer the 
types for your functions but when it's not you'll get an "cannot 
deduce arguments" error.


when you call bar template function, you won't be able to modify 
the argument n. ++n will not work and will throw an error at 
compile time.


import std.traits : Unqual;
import std.stdio : writeln;

uint normalFoo(int n) {
++n;
return n;
}

uint constNormalFoo(const int n) {
++n; // will raise error
return n;
}

uint templateFoo(T)(Unqual!T n) {
++n;
return n;
}

uint constTemplateFoo(T)(const Unqual!T n) {
++n; // will raise error
return n;
}

void main() {
writeln(normalFoo(42));
// writeln(constNormalFoo(42));
writeln(templateFoo!int(42));
// writeln(constTemplateFoo!int(42));

}

more info is available at:


How to specify a template that uses unqualified type, like any normal function

2017-08-14 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn

if I use fixed-type functions, I can do the following:

uint foo(uint n)
{
   ++n; // modify n - as this function has received a copy of n, 
this is always possible

   return 42;
}

uint bar(const uint n)
{
   assert(foo(n)==42);
   return 17;
}

void main()
{
   bar(3);
}


But if I try the same with a template parameter, it doesn't work:

import std.traits; // Unqual

uint foo(T)(Unqual!T n) // first try
{
   ++n; // modify should be possible
   return 42;
}

uint foo2(T)(T n) // second try
{
   ++n; // modify fails, as T is const
   return 42;
}

uint bar(T)(const T n)
{
   assert(foo(n)==42u); // cannot deduce arguments - why?!?
   assert(foo2(n)==42u); // here it can deduce the arguments, but 
the function cannot modify n

   return 17;
}

void main()
{
   bar(3);
}

Any ideas what I need to do to make this work?