www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Implicit type conversion of an argument when a function is called

reply "Carl Sturtivant" <sturtivant gmail.com> writes:
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?
Feb 10 2014
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Feb 10 2014
parent "Carl Sturtivant" <sturtivant gmail.com> writes:
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.
Feb 11 2014
prev sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Feb 11 2014
parent reply "Carl Sturtivant" <sturtivant gmail.com> writes:
 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?
Feb 11 2014
parent "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Feb 11 2014