Thread overview
Implicit type conversion of an argument when a function is called
Feb 11, 2014
Carl Sturtivant
Feb 11, 2014
Adam D. Ruppe
Feb 11, 2014
Carl Sturtivant
Feb 11, 2014
John Colvin
Feb 11, 2014
Carl Sturtivant
Feb 11, 2014
John Colvin
February 11, 2014
If I define a struct, and I want to pass a string to a function with a parameter of type that very struct, is there any way to arrange that an implicit conversion occurs, assuming a function that maps a string to that struct type exists?

struct data { ........ }

void f( data x) { .... }

void main() {
    data( "hello"); //convert argument implicitly to type `data`.
}

It's possible to arrange for such an implicit conversion from an existing type to a newly defined type during assignment, but what about when passing an argument to a function?

February 11, 2014
On Tuesday, 11 February 2014 at 03:45:30 UTC, Carl Sturtivant wrote:
> If I define a struct, and I want to pass a string to a function with a parameter of type that very struct, is there any way to arrange that an implicit conversion occurs, assuming a function that maps a string to that struct type exists?

Nope, you have to do it explicitly.

f(data("hello"));

You could write a wrapper function do do the conversions for you, but of course, you are still explicitly calling the wrapper at the usage site.


I kinda wish D did have this. The mistake C++ made was having the implicit conversions be the default, not having it be an option. But it isn't in and as far as I know, there are no plans to add it either.
February 11, 2014
On Tuesday, 11 February 2014 at 03:45:30 UTC, Carl Sturtivant wrote:
>
> If I define a struct, and I want to pass a string to a function with a parameter of type that very struct, is there any way to arrange that an implicit conversion occurs, assuming a function that maps a string to that struct type exists?
>
> struct data { ........ }
>
> void f( data x) { .... }
>
> void main() {
>     data( "hello"); //convert argument implicitly to type `data`.
> }
>
> It's possible to arrange for such an implicit conversion from an existing type to a newly defined type during assignment, but what about when passing an argument to a function?

With a class you can do this:
class Data
{
	string s;
	this(string s)
	{
		this.s = s;
	}
}

void f(Data x ...)
{
	import std.stdio;
	writeln(x.s);
}

void main()
{
    f("hello"); //convert argument implicitly to type `data`.
}

See Typesafe Variadic Functions at http://dlang.org/function.html

I don't know why you can't do it with a struct.

As a workaround, you can do this:

class Construct(T)
{
	T t;
	this(Q)(Q q)
	{
		static if(is(Q : T))
		{
			t = q;
		}
		else
		{
			this.t = T(q);
		}
	}
}

struct Data
{
	string s;
}

void f(Construct!Data xC ...) //construct the wrapper class
{
	auto x = xC.t; //get the contents.
	
	import std.stdio;
	writeln(x.s);
}

void main()
{
	f("hello");
	f(Data("world"));
}


Overall it's probably best to define f as:

void f(Data x)
{
	import std.stdio;
	writeln(x.s);
}

void f(Construct!Data xC ...)
{
	.f(xC.t);
}

To avoid any overhead when calling normally as well as separating the definition of the real function from the concern of automatic construction/conversion.
February 11, 2014
On Tuesday, 11 February 2014 at 03:50:16 UTC, Adam D. Ruppe wrote:
> On Tuesday, 11 February 2014 at 03:45:30 UTC, Carl Sturtivant wrote:
>> If I define a struct, and I want to pass a string to a function with a parameter of type that very struct, is there any way to arrange that an implicit conversion occurs, assuming a function that maps a string to that struct type exists?
>
> Nope, you have to do it explicitly.
>
> f(data("hello"));
[...]
> I kinda wish D did have this. The mistake C++ made was having the implicit conversions be the default, not having it be an option. But it isn't in and as far as I know, there are no plans to add it either.

Disappointing. It could be kept decoupled from constructors and assignment defined conversions by having an analogue of opAssign that's solely for argument passing.
February 11, 2014
> With a class you can do this:
> class Data
> {
> 	string s;
> 	this(string s)
> 	{
> 		this.s = s;
> 	}
> }
>
> void f(Data x ...)
> {
> 	import std.stdio;
> 	writeln(x.s);
> }
>
> void main()
> {
>     f("hello"); //convert argument implicitly to type `data`.
> }
>
> See Typesafe Variadic Functions at http://dlang.org/function.html
>
> I don't know why you can't do it with a struct.
>
> As a workaround, you can do this:
>
> class Construct(T)
> {
> 	T t;
> 	this(Q)(Q q)
> 	{
> 		static if(is(Q : T))
> 		{
> 			t = q;
> 		}
> 		else
> 		{
> 			this.t = T(q);
> 		}
> 	}
> }
>
> struct Data
> {
> 	string s;
> }
>
> void f(Construct!Data xC ...) //construct the wrapper class
> {
> 	auto x = xC.t; //get the contents.
> 	
> 	import std.stdio;
> 	writeln(x.s);
> }
>
> void main()
> {
> 	f("hello");
> 	f(Data("world"));
> }
>
>
> Overall it's probably best to define f as:
>
> void f(Data x)
> {
> 	import std.stdio;
> 	writeln(x.s);
> }
>
> void f(Construct!Data xC ...)
> {
> 	.f(xC.t);
> }
>
> To avoid any overhead when calling normally as well as separating the definition of the real function from the concern of automatic construction/conversion.

Nice technique, I'll remember that. Presumably this can't be extended to functions with more than one Data parameter.

What a curious corner of the language! I am somewhat mystified as to the reasoning/intuition behind the presence of this feature for classes (and not structs). Anybody?

February 11, 2014
On Tuesday, 11 February 2014 at 14:47:31 UTC, Carl Sturtivant wrote:
>> With a class you can do this:
>> class Data
>> {
>> 	string s;
>> 	this(string s)
>> 	{
>> 		this.s = s;
>> 	}
>> }
>>
>> void f(Data x ...)
>> {
>> 	import std.stdio;
>> 	writeln(x.s);
>> }
>>
>> void main()
>> {
>>    f("hello"); //convert argument implicitly to type `data`.
>> }
>>
>> See Typesafe Variadic Functions at http://dlang.org/function.html
>>
>> I don't know why you can't do it with a struct.
>>
>> As a workaround, you can do this:
>>
>> class Construct(T)
>> {
>> 	T t;
>> 	this(Q)(Q q)
>> 	{
>> 		static if(is(Q : T))
>> 		{
>> 			t = q;
>> 		}
>> 		else
>> 		{
>> 			this.t = T(q);
>> 		}
>> 	}
>> }
>>
>> struct Data
>> {
>> 	string s;
>> }
>>
>> void f(Construct!Data xC ...) //construct the wrapper class
>> {
>> 	auto x = xC.t; //get the contents.
>> 	
>> 	import std.stdio;
>> 	writeln(x.s);
>> }
>>
>> void main()
>> {
>> 	f("hello");
>> 	f(Data("world"));
>> }
>>
>>
>> Overall it's probably best to define f as:
>>
>> void f(Data x)
>> {
>> 	import std.stdio;
>> 	writeln(x.s);
>> }
>>
>> void f(Construct!Data xC ...)
>> {
>> 	.f(xC.t);
>> }
>>
>> To avoid any overhead when calling normally as well as separating the definition of the real function from the concern of automatic construction/conversion.
>
> Nice technique, I'll remember that. Presumably this can't be extended to functions with more than one Data parameter.

You are correct, although you can of course just create a normal D-style variadic function overload that does any conversions necessary and then forwards to the original function, but you get less of a helping hand from the language. In general it's harder as you could have multiple constructors that take different numbers of arguments. My example above should work for any number of args, but if you restrict yourself to 1 to 1 conversions:

void g(A a, B b) {}

void g(TL ...)(TL args)
    if(TL.length == 2)
{
    //do conversions if possible and pass the converted args to
    //g(A,B).
}


In all of this I haven't taken any care over ref-ness. In order to get that correct you likely need to use auto ref and maybe std.algorithm.forward, I haven't thought much about it.