Thread overview
Template method and type resolution of return type
Apr 19, 2014
matovitch
Apr 19, 2014
bearophile
Apr 19, 2014
matovitch
Apr 19, 2014
matovitch
Apr 19, 2014
matovitch
Apr 19, 2014
Andrej Mitrovic
Apr 20, 2014
David Held
Apr 20, 2014
matovitch
Apr 20, 2014
monarch_dodra
Apr 20, 2014
matovitch
April 19, 2014
Hi everyone !

Let's say I have a struct :

struct Test
{
	immutable (ubyte)[] data;

	T get(T)()
	{
		return *(cast(T*)(&(data[0])));
	}
}

This code will work :

import std.stdio;

void main()
{
	Test t;
	t.data = [152, 32, 64, 28, 95];
	float b = t.get!float;
	writefln("%s", b);
}

This won't compile :

import std.stdio;

void main()
{
	Test t;
	t.data = [152, 32, 64, 28, 95];
	float b = t.get;
	writefln("%s", b);
}

This neither:

import std.stdio;

void main()
{
	Test t;
	t.data = [152, 32, 64, 28, 95];
	float b = t.get!(typeof(b));
	writefln("%s", b);
}

And this will work:

import std.stdio;

void main()
{
	Test t;
	t.data = [152, 32, 64, 28, 95];
	float b;
        b = t.get!(typeof(b));
	writefln("%s", b);
}

Why can't dmd infere the type 'float' ? In fact this would allow a nicer syntax for the Json struct in vibe.d for example.
April 19, 2014
matovitch:

> struct Test
> {
> 	immutable (ubyte)[] data;
>
> 	T get(T)()
> 	{
> 		return *(cast(T*)(&(data[0])));
> 	}

It's better to return const/immutable data. Otherwise the program gives undefined results. In alternative use mutable ubytes for data.

Also &data[0] is probably data.ptr.


> This won't compile :
>
> import std.stdio;
>
> void main()
> {
> 	Test t;
> 	t.data = [152, 32, 64, 28, 95];
> 	float b = t.get;
> 	writefln("%s", b);

D doesn't perform inference on the return type.


> This neither:
>...
> 	Test t;
> 	t.data = [152, 32, 64, 28, 95];
> 	float b = t.get!(typeof(b));
> 	writefln("%s", b);

I don't know. Perhaps b is not yet defined. This in theory could work.


> In fact this would allow a nicer syntax for the Json struct in vibe.d for example.

I don't think in future D will behave differently on this code.

Bye,
bearophile
April 19, 2014
On Saturday, 19 April 2014 at 17:49:54 UTC, bearophile wrote:
> matovitch:
>
>> struct Test
>> {
>> 	immutable (ubyte)[] data;
>>
>> 	T get(T)()
>> 	{
>> 		return *(cast(T*)(&(data[0])));
>> 	}
>
> It's better to return const/immutable data. Otherwise the program gives undefined results. In alternative use mutable ubytes for data.
>
> Also &data[0] is probably data.ptr.

I did'nt know that one.

>
>
>> This won't compile :
>>
>> import std.stdio;
>>
>> void main()
>> {
>> 	Test t;
>> 	t.data = [152, 32, 64, 28, 95];
>> 	float b = t.get;
>> 	writefln("%s", b);
>
> D doesn't perform inference on the return type.

I see. But why ?

It's seems it exits a workaround overloading cast operators in c++ :
    http://programmaticallyspeaking.blogspot.se/2012/09/infer-return-type-for-templated.html

However I think it would only work with opImplicitCast in D. The code given in vibe.d doc could became :

void manipulateJson(Json j)
{
	j = Json.emptyObject;
	j.name = "Example";
	j.id = 1;

	// retrieving the values is done using get()
	assert(j["name"] == "Example");
	assert(j["id"] == 1);

	// print out as JSON: {"name": "Example", "id": 1}
	writefln("JSON: %s", j.toString());
}


ps : Thank you for all your great examples on Rosetta code (or the last one about the rubik's cube).
April 19, 2014
On Saturday, 19 April 2014 at 18:46:28 UTC, matovitch wrote:
> On Saturday, 19 April 2014 at 17:49:54 UTC, bearophile wrote:
>> matovitch:

>
> I did'nt know that one.
>

This last sentence is misleading, let be clear : I know almost nothing when it comes to D...but I'm willing to learn ! ;-)

April 19, 2014
On Saturday, 19 April 2014 at 18:46:28 UTC, matovitch wrote:
> On Saturday, 19 April 2014 at 17:49:54 UTC, bearophile wrote:
>> matovitch:
>>
>>> struct Test
>>> {
>>> 	immutable (ubyte)[] data;
>>>
>>> 	T get(T)()
>>> 	{
>>> 		return *(cast(T*)(&(data[0])));
>>> 	}
>>
>> It's better to return const/immutable data. Otherwise the program gives undefined results. In alternative use mutable ubytes for data.
>>
>> Also &data[0] is probably data.ptr.
>
> I did'nt know that one.
>
>>
>>
>>> This won't compile :
>>>
>>> import std.stdio;
>>>
>>> void main()
>>> {
>>> 	Test t;
>>> 	t.data = [152, 32, 64, 28, 95];
>>> 	float b = t.get;
>>> 	writefln("%s", b);
>>
>> D doesn't perform inference on the return type.
>
> I see. But why ?
>
> It's seems it exits a workaround overloading cast operators in c++ :
>     http://programmaticallyspeaking.blogspot.se/2012/09/infer-return-type-for-templated.html
>
> However I think it would only work with opImplicitCast in D. The code given in vibe.d doc could became :
>
> void manipulateJson(Json j)
> {
> 	j = Json.emptyObject;
> 	j.name = "Example";
> 	j.id = 1;
>
> 	// retrieving the values is done using get()
> 	assert(j["name"] == "Example");
> 	assert(j["id"] == 1);
>
> 	// print out as JSON: {"name": "Example", "id": 1}
> 	writefln("JSON: %s", j.toString());
> }
>
>
> ps : Thank you for all your great examples on Rosetta code (or the last one about the rubik's cube).

Or even :

void manipulateJson(Json j)
{
 	j = Json.emptyObject;
 	j.name = "Example";
 	j.id = 1;

 	assert(j.name == "Example");
 	assert(j.id == 1);

 	// print out as JSON: {"name": "Example", "id": 1}
 	writefln("JSON: %s", j.toString());
}

Or at least this will work :

void manipulateJson(Json j)
{
 	j = Json.emptyObject;
 	j.name = "Example";
 	j.id = 1;

 	string s = j.name;
        assert(s == "Example");


 	// print out as JSON: {"name": "Example", "id": 1}
 	writefln("JSON: %s", j.toString());
}

Ok I stop fooding.
April 19, 2014
On 4/19/14, matovitch via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
> This won't compile :
>
> import std.stdio;
>
> void main()
> {
> 	Test t;
> 	t.data = [152, 32, 64, 28, 95];
> 	float b = t.get;
> 	writefln("%s", b);
> }

Because it's probably overkill. At some point it becomes too much magic. The following currently works but it might be considered an ambiguity with return type inference:

-----
struct S
{
    int get()  { return 0; }
    T get(T)() { return T.init; }
}

void main()
{
    S s;
    float x = s.get();  // which overload? (currently int get())
}
-----
April 20, 2014
On 4/19/2014 3:31 PM, Andrej Mitrovic via Digitalmars-d-learn wrote:
> [...]
> struct S
> {
>      int get()  { return 0; }
>      T get(T)() { return T.init; }
> }
>
> void main()
> {
>      S s;
>      float x = s.get();  // which overload? (currently int get())
> }

Isn't this just because concrete methods are better overload candidates than method templates?

Dave

April 20, 2014
On Sunday, 20 April 2014 at 00:55:31 UTC, David Held wrote:
> On 4/19/2014 3:31 PM, Andrej Mitrovic via Digitalmars-d-learn wrote:
>> [...]
>> struct S
>> {
>>     int get()  { return 0; }
>>     T get(T)() { return T.init; }
>> }
>>
>> void main()
>> {
>>     S s;
>>     float x = s.get();  // which overload? (currently int get())
>> }
>
> Isn't this just because concrete methods are better overload candidates than method templates?
>
> Dave

struct S
{
   ubyte get()  { return 0 ; }
   float get()  { return 0.; }
}

void main()
{
   S s;
   float x = s.get();  // does'nt know which overload, does'nt compile.
}
April 20, 2014
On Sunday, 20 April 2014 at 07:52:08 UTC, matovitch wrote:
>
> struct S
> {
>    ubyte get()  { return 0 ; }
>    float get()  { return 0.; }
> }
>
> void main()
> {
>    S s;
>    float x = s.get();  // does'nt know which overload, does'nt compile.
> }

What I do find interesting though, is that you are allowed to write the overload, whereas C++ would outright block you for ambiguity "at the source".

This means that with proper meta magic eg `__traits(getOverloadSet, S, "get")`, you could, *manually* resolve the ambiguity yourself.
April 20, 2014
On Sunday, 20 April 2014 at 08:28:07 UTC, monarch_dodra wrote:
> On Sunday, 20 April 2014 at 07:52:08 UTC, matovitch wrote:
>>
>> struct S
>> {
>>   ubyte get()  { return 0 ; }
>>   float get()  { return 0.; }
>> }
>>
>> void main()
>> {
>>   S s;
>>   float x = s.get();  // does'nt know which overload, does'nt compile.
>> }
>
> What I do find interesting though, is that you are allowed to write the overload, whereas C++ would outright block you for ambiguity "at the source".
>
> This means that with proper meta magic eg `__traits(getOverloadSet, S, "get")`, you could, *manually* resolve the ambiguity yourself.

You mean getOverloads ? (yes it's interesting)

How about this :

class S
{
	public
	{
		this() {}

		union other {
			SFloat u_float;
			SUbyte u_ubyte;
		}

		alias other this;
	}
}

struct SFloat
{
	float data;
	alias data this;
}

struct SUbyte
{
	ubyte data;
	alias data this;
}

void main()
{
   S s;
   s.other.u_float.data = 0.5;
   //float x = s;
}

This gives :

main.d(31): Error: need 'this' for 'data' of type 'float'