www.digitalmars.com         C & C++   DMDScript  

D - Multiple returns / was: x, y, z = 0

reply "Christian Schüler" <cschueler gmx.de> writes:
I'm moving this discussion to a new thread.
What about multiple return values, like in LUA:

--- LUA code begin ---

function foo()
    return 1, 2, 3
end

a, b, c = foo()

function bar( x, y, z )
    return x + y + z
end

sum = bar( foo() )

--- LUA code end ---
May 29 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Pavel Minayev wrote:

 BTW, out-parameters are supposed to exist for such cases =)
I keep forgetting about those! Hey Walter, is there any way to omit an out parameter? Of course, inout parameters are required...but there any way to skip an out parameter? I'd hate to make a temp variable whenever there was an out parameter I didn't use. This would probably require the compiler to declare a temporary for you on the stack, and pass that as the out parameter...but better the compiler than me, I always say! :) -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
May 29 2002
prev sibling parent reply "Christian Schüler" <cschueler gmx.de> writes:
 But I don't
 think you can easily do it in a language which compiles to
 native code.
Yes, that's the point. I was thinking about it how such a feature could be implemented. Let's suppose you can declare a function to have multiple returns in a compiled language (like C or D): int, int, int foo() { return 1, 2, 3; } If this function was to be compiled into assembler, it would leave, on the stack, three integers. There's basically nothing wrong with that, as the caller is responsible for cleaning up the stack, and, through the function prototype, it is known at compile time what return values a function leaves. (Think as an alternative that the function foo() would have returned a structure containing 3 integers, pretty legal in C++). Now suppose that the assignment operator = is a function that can take an arbitrary number of parameters, not just one. Let's suppose the assignment operator is declared as: inline operator =( ... ) That is, the assignment operator can take any number of parameters, from the stack. If there is written: int a, b, c; a, b, c = foo(); the assignment operator takes the 3 values from the stack that the function foo() has left, assigning them to a, b and c. The compiler would have to make sure, at compile time, that the number and the types of the arguments match. Of course, in the final implementation when the language is optimized, things doesn't take the detour via the physical stack, but this view is helpful in understanding whats going on. With this in mind, the forwarding of multiple return values as function parameters is a no-brainer: int bar( int x, int y, int z ) { return x + y + z; } int sum = bar( foo() ); The function bar() takes three parameters, which must be pushed onto the stack before calling the function. Instead of pushing single arguments, they could pretty much be left behind from the call to the function foo(). Again the compiler will check at compile time from the prototypes that everything matches together. Thoughts, anyone?
May 29 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
next sibling parent reply "anderson" <anderson firestar.com.au> writes:
I think it's a cool idea,
I staying away from the ASM part.

int, int, int a()
{
...
    return x,y,z;
}

interprate the same as
int a(int out c, int out d)
{
...
   return x,y,z; //for all out params
}

interprate which would interparte to
int a(int out c, int out d)
{
...
   c = y;
   d = z;
   return x;
}

or alternativly

make it the same as doing,
{int a, int b , int c} a()
{
...
    return {x,y,z};
}

If speed is a concern users can just use the single version (but they'd
probably end up using one of the later algorithms anyways) , because speed
is not an issue in all parts of a problem.

PS - Does D allow for overloading of return types?



"Pavel Minayev" <evilone omen.ru> wrote in message
news:ad4djm$8na$1 digitaldaemon.com...
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:ad5clq$1jm8$1 digitaldaemon.com...

 PS - Does D allow for overloading of return types?
No, and I hope it never will.
May 30 2002
parent reply "anderson" <anderson firestar.com.au> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:ad5kma$2bro$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:ad5clq$1jm8$1 digitaldaemon.com...

 PS - Does D allow for overloading of return types?
No, and I hope it never will.
Can you explain your POV? I apparently missed it.
May 31 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:ad9bos$12oh$1 digitaldaemon.com...

 PS - Does D allow for overloading of return types?
No, and I hope it never will.
Can you explain your POV? I apparently missed it.
Function overloading based on return type is a complex, hardly predictable feature, especially in a Cish weak type environment used by D. I think it adds more confusion than benefits. And, after all, one may always use out parameters to imitate it...
May 31 2002
parent reply "anderson" <anderson firestar.com.au> writes:
But if one can use out to imitated it, why not use the same rules for the
return type? Why do we need to use workarounds to do the same thing?

I would be inclined to agree with you if your argment was stronger.


"Pavel Minayev" <evilone omen.ru> wrote in message
news:ad9qlf$1iue$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:ad9bos$12oh$1 digitaldaemon.com...

 PS - Does D allow for overloading of return types?
No, and I hope it never will.
Can you explain your POV? I apparently missed it.
Function overloading based on return type is a complex, hardly predictable feature, especially in a Cish weak type environment used by D. I think it adds more confusion than benefits. And, after all, one may always use
out
 parameters to imitate it...
Jun 01 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:adag2i$e2q$1 digitaldaemon.com...

 But if one can use out to imitated it, why not use the same rules for the
 return type? Why do we need to use workarounds to do the same thing?
Out arguments cannot be typecasted: void foo(out short n); void foo(out byte n); ... int n; foo(n); // error! You cannot cast it to short, or do something else like that. Function wants a short oR byte, and you have to provide it a short or byte. Ambiguity is impossible. Now, if we had overloading based on return arguments, we'd have this: short foo(); byte foo(); ... int n; n = foo(); // which version is called? A headache for both the compiler (to find out such cases), and the developer. Okay, so this case is simple, but call to foo() might have happened in a complex expression, and due to the weak nature of D type system, many type conversions could happen, so it is hard to tell the desired type for the return value of foo() at some point.
Jun 01 2002
parent reply "anderson" <anderson firestar.com.au> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:adak4l$j29$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:adag2i$e2q$1 digitaldaemon.com...

 But if one can use out to imitated it, why not use the same rules for
the
 return type? Why do we need to use workarounds to do the same thing?
Out arguments cannot be typecasted: void foo(out short n); void foo(out byte n); ... int n; foo(n); // error! You cannot cast it to short, or do something else like that. Function wants a short oR byte, and you have to provide it a short or byte. Ambiguity is impossible. Now, if we had overloading based on return arguments, we'd have this: short foo(); byte foo(); ... int n; n = foo(); // which version is called? A headache for both the compiler (to find out such cases), and the developer. Okay, so this case is simple, but call to foo() might have happened in a complex expression, and due to the weak nature of D type system, many type conversions could happen, so it is hard to tell the desired type for the return value of foo() at some point.
Ok, seems resonable. So it's D fault for having a weak type system for returns types. And you like D having weak return types?
Jun 02 2002
next sibling parent reply "anderson" <anderson firestar.com.au> writes:
Sorry I ment

Ok, seems resonable. So it's D fault for having a weak type system for
returns types. And you like D having weak typing for return?


 Ok, seems resonable. So it's D fault for having a weak type system for
 returns types. And you like D having weak return types?
Jun 02 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:adck5l$2mun$1 digitaldaemon.com...

 Ok, seems resonable. So it's D fault for having a weak type system for
 returns types. And you like D having weak typing for return?
Yes. I don't want to perform a cast each time I assign a value returned by a double function to int. BTW, this applies not only to the return statement, but to the entire system. It's not Java, and it's another reason why I like D. ... now if we only didn't need cast to assign a void* pointer to something else (like in C) ...
Jun 02 2002
parent reply "anderson" <anderson firestar.com.au> writes:
I hate to keep this subject up, but have you considered explicit
overloading?

With explicit overloading, the function used will default to the default
function, unless an explicit one has been defined specifically for that
type.

ie

double sqrt(int val) {};
explicit float sqrt(int val) {};
explicit int sqrt(int val) {};

Therefore if a short is given, it will default to the double method, but if
a float is given it will use the explicit type. The programmer can optimise
the float and int methods for better performance.


Of cause there are some disadvantages of this to true overloading (having do
explicit more types), but It kind meets halfway.


"Pavel Minayev" <evilone omen.ru> wrote in message
news:adcv46$31cl$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:adck5l$2mun$1 digitaldaemon.com...

 Ok, seems resonable. So it's D fault for having a weak type system for
 returns types. And you like D having weak typing for return?
Yes. I don't want to perform a cast each time I assign a value returned by a double function to int. BTW, this applies not only to the return statement, but to the entire system. It's not Java, and it's another reason why I like D. ... now if we only didn't need cast to assign a void* pointer to something else (like in C) ...
Jun 02 2002
next sibling parent reply "anderson" <anderson firestar.com.au> writes:
(Just fixed some grammar)

 I hate to keep this subject going, but have you considered explicit
 overloading?

 With explicit overloading, the function used will use the default
function, unless an explicit one has been defined specifically for that
type.

 ie

 double sqrt(int val) {};
 explicit float sqrt(int val) {};
 explicit int sqrt(int val) {};

Therefore if a short is given, it will default to the double method, but if
a float is given it will use the explicit type. The programmer can optimise
the float and int methods for better performance.

Of cause there are some disadvantages of this to true overloading (having do
explicit for more types), but it kind'a meets halfway.
Jun 02 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:adeqr9$8lt$1 digitaldaemon.com...

  I hate to keep this subject going, but have you considered explicit
  overloading?

  With explicit overloading, the function used will use the default
 function, unless an explicit one has been defined specifically for that
 type.
Then, this should be applied to parameter overloading, for consistency. But in this form, it is possible. I guess you've convinced me, now you should do the same to Walter... =)
Jun 03 2002
parent reply "anderson" <anderson firestar.com.au> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:adf70n$o09$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:adeqr9$8lt$1 digitaldaemon.com...
 Then, this should be applied to parameter overloading, for consistency.
I agree, that way you know instantaly that something has been overloaded and there's no ambiguity. Also I don't think it would cause many hard ackes in porting overloads because the compiler would report an error. Then the programmer would simply had explicit to the overloads, and in the odd cases where D casting coversion is differn't, they'd simply add another explicit. Perhaps a different term/keyword then "explicit" (I'm fine with explicit). Any suggestions? Here are mine: overload explic oload, (but I don't like, overl)
 But in this form, it is possible. I guess you've convinced me, now
Thanks, I do think overloading can be a rather powerful tool in reducting libary complexity.
 you should do the same to Walter... =)
You seem to be the best at that! ;) (Not that I'm asking you to do anything.)
Jun 03 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
Just a thought to extend the idea further.

What if it would be possible to tell the compiler, arguments of which
type does this function expect. The syntax could be something like this:

    long abs(long cast(byte, short, int) n);
    extended abs(extended cast(float, double) n);

These two lines declare _two_ abs() functions, one taking a long argument,
another takes extended. However, for types byte, short and int, first
version is called (and argument is cast to long), and for float and
double, the second version is called. This is essentially the same as
if there was a set of overloaded functions:

    long abs(long n);
    long abs(int n) { return abs(cast(long) n); }
    long abs(short n) { return abs(cast(long) n); }
    long abs(byte n) { return abs(cast(long) n); }

Only shorter, and the compiler might decide not to use inline wrappers,
but to resolve ambiguity just by looking at the list of possible
types for each argument.

What do you think?

P.S. The same could be applied to return type as well...
Jun 03 2002
parent reply "anderson" <anderson firestar.com.au> writes:
I was thinking along the same lines, but I couldn't get it into words and
you brought it up. I think it's an excellent idea.

The only problem I can see with this is that we don't want to make the
programmer go to have to type to much to overload.

What about these problems to solve,

Declared in module 1
long abs(long cast(byte, short, int) n);

Declared in module 2
extended abs(extended cast(int ,float, double) n);

So I suppose in this situtation a complier error message would be triggered,
if two overloads shared the same type. But a better Idea would be to use
module 2 for the int in it's local space.

It'd also be good to have an alternative method to save on typing

extended abs(extended allbut(int) n); //Would do everything except int
values

Extending some of my orignal ideas.
If a function is deleared without a in built cast it becomes default. You
can overwrite (not overload) a default function for local use (like you can
already). You cannot however overwrite a default function twice in the same
local space.

If a function contains the cast word then it is explictly saying to overload
those values. You can overwrite (not overload) a cast function for local
use. You cannot however overwrite a cast for the same types function twice
in the same local space.

ie 1 -Wrong
Module 1
long abs(long n); (default function) //OK works in local space
long abs(int n); (default function) //ERROR, cannot overwrite must cast

ie 2 -Correct
Module 1
long abs(long n); //(default function) //OK works in local space

Module 2
long abs(int n); //(default function) //OK works in local space

ie 3
Module 1
long abs(long n); //(default function) //OK works in local space

Module 2
long abs(int n); //(default function)
long abs(int cast(int, long) n); //(cast function)
long abs(int cast(int, double) n); //ERROR, ambiguus choice available

ie 3
Module 1
long abs(long n); //(default function) //OK works in local space
long abs(int cast(int, double) n); //OK works in local space

Module 2
long abs(int n); //(default function)
long abs(int cast(int, long) n); //(cast function)

You get the idea though.
That's my solution to that problem, you may have already though of that far
ahead of me.

So of course using your method (which I happen to like), does make things
more tricky.

"Pavel Minayev" <evilone omen.ru> wrote in message
news:adfleq$175n$1 digitaldaemon.com...
 Just a thought to extend the idea further.

 What if it would be possible to tell the compiler, arguments of which
 type does this function expect. The syntax could be something like this:

     long abs(long cast(byte, short, int) n);
     extended abs(extended cast(float, double) n);

 These two lines declare _two_ abs() functions, one taking a long argument,
 another takes extended. However, for types byte, short and int, first
 version is called (and argument is cast to long), and for float and
 double, the second version is called. This is essentially the same as
 if there was a set of overloaded functions:

     long abs(long n);
     long abs(int n) { return abs(cast(long) n); }
     long abs(short n) { return abs(cast(long) n); }
     long abs(byte n) { return abs(cast(long) n); }

 Only shorter, and the compiler might decide not to use inline wrappers,
 but to resolve ambiguity just by looking at the list of possible
 types for each argument.

 What do you think?

 P.S. The same could be applied to return type as well...
Jun 03 2002
next sibling parent "anderson" <anderson firestar.com.au> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:adg33m$1m4h$1 digitaldaemon.com...
 I was thinking along the same lines, but I couldn't get it into words and
 you brought it up. I think it's an excellent idea.
Fix The only problem I can see with this is that we don't want to give the programmer a typing overload.
 What about these problems to solve,

 Declared in module 1
 long abs(long cast(byte, short, int) n);

 Declared in module 2
 extended abs(extended cast(int ,float, double) n);

 So I suppose in this situtation a complier error message would be
triggered,
 if two overloads shared the same type. But a better Idea would be to use
 module 2 for the int in it's local space.

 It'd also be good to have an alternative method to save on typing

 extended abs(extended allbut(int) n); //Would do everything except int
 values

 Extending some of my orignal ideas.
 If a function is deleared without a in built cast it becomes default. You
 can overwrite (not overload) a default function for local use (like you
can
 already). You cannot however overwrite a default function twice in the
same
 local space.

 If a function contains the cast word then it is explictly saying to
overload
 those values. You can overwrite (not overload) a cast function for local
 use. You cannot however overwrite a cast for the same types function twice
 in the same local space.

 ie 1 -Wrong
 Module 1
 long abs(long n); (default function) //OK works in local space
 long abs(int n); (default function) //ERROR, cannot overwrite must cast

 ie 2 -Correct
 Module 1
 long abs(long n); //(default function) //OK works in local space

 Module 2
 long abs(int n); //(default function) //OK works in local space

 ie 3
 Module 1
 long abs(long n); //(default function) //OK works in local space

 Module 2
 long abs(int n); //(default function)
 long abs(int cast(int, long) n); //(cast function)
 long abs(int cast(int, double) n); //ERROR, ambiguus choice available

 ie 3
 Module 1
 long abs(long n); //(default function) //OK works in local space
 long abs(int cast(int, double) n); //OK works in local space

 Module 2
 long abs(int n); //(default function)
 long abs(int cast(int, long) n); //(cast function)

 You get the idea though.
 That's my solution to that problem, you may have already though of that
far
 ahead of me.

 So of course using your method (which I happen to like), does make things
 more tricky.

 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:adfleq$175n$1 digitaldaemon.com...
 Just a thought to extend the idea further.

 What if it would be possible to tell the compiler, arguments of which
 type does this function expect. The syntax could be something like this:

     long abs(long cast(byte, short, int) n);
     extended abs(extended cast(float, double) n);

 These two lines declare _two_ abs() functions, one taking a long
argument,
 another takes extended. However, for types byte, short and int, first
 version is called (and argument is cast to long), and for float and
 double, the second version is called. This is essentially the same as
 if there was a set of overloaded functions:

     long abs(long n);
     long abs(int n) { return abs(cast(long) n); }
     long abs(short n) { return abs(cast(long) n); }
     long abs(byte n) { return abs(cast(long) n); }

 Only shorter, and the compiler might decide not to use inline wrappers,
 but to resolve ambiguity just by looking at the list of possible
 types for each argument.

 What do you think?

 P.S. The same could be applied to return type as well...
Jun 03 2002
prev sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:adg33m$1m4h$1 digitaldaemon.com...

 What about these problems to solve,

 Declared in module 1
 long abs(long cast(byte, short, int) n);

 Declared in module 2
 extended abs(extended cast(int ,float, double) n);

 So I suppose in this situtation a complier error message would be
triggered,
 if two overloads shared the same type. But a better Idea would be to use
 module 2 for the int in it's local space.
If we import both modules into a third one, that we'd have to resolve the naming conflict, using full names anyhow: int a; module1.abs(a); module2.abs(a); If we define AND call abs() from module2, then local declaration shadows out the one from module1. So you won't be able to call abs(byte), for example, unless you provide the full name - module1.abs(byte).
 It'd also be good to have an alternative method to save on typing

 extended abs(extended allbut(int) n); //Would do everything except int
 values
Or it could be: extended abs(extended !cast(int) n);
 Extending some of my orignal ideas.
 If a function is deleared without a in built cast it becomes default. You
 can overwrite (not overload) a default function for local use (like you
can
 already). You cannot however overwrite a default function twice in the
same
 local space.

 If a function contains the cast word then it is explictly saying to
overload
 those values. You can overwrite (not overload) a cast function for local
 use. You cannot however overwrite a cast for the same types function twice
 in the same local space.

 ie 1 -Wrong
 Module 1
 long abs(long n); (default function) //OK works in local space
 long abs(int n); (default function) //ERROR, cannot overwrite must cast
I'd let it be there, just for compatibility, and preserve the current semantics. IMO, it's better to think of cast() as of addition to rather than replacement of current schema of function overloading.
 Module 2
 long abs(int n); //(default function)
 long abs(int cast(int, long) n); //(cast function)
I think this should be an error. IMO, the best idea is to consider such declaration semantically equal to this: long abs(int); long abs(long); Since we already have abs(int) in this module, it should be forbidden. By the way, since n is already int, why list it in the cast clause? It should be implicit.
Jun 03 2002
parent reply "anderson" <anderson firestar.com.au> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:adg79p$1r3c$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:adg33m$1m4h$1 digitaldaemon.com...

 What about these problems to solve,

 Declared in module 1
 long abs(long cast(byte, short, int) n);

 Declared in module 2
 extended abs(extended cast(int ,float, double) n);

 So I suppose in this situtation a complier error message would be
triggered,
 if two overloads shared the same type. But a better Idea would be to use
 module 2 for the int in it's local space.
If we import both modules into a third one, that we'd have to resolve the naming conflict, using full names anyhow: int a; module1.abs(a); module2.abs(a); If we define AND call abs() from module2, then local declaration shadows out the one from module1. So you won't be able to call abs(byte), for example, unless you provide the full name - module1.abs(byte).
 It'd also be good to have an alternative method to save on typing

 extended abs(extended allbut(int) n); //Would do everything except int
 values
Or it could be: extended abs(extended !cast(int) n);
Good Idea
 Extending some of my orignal ideas.
 If a function is deleared without a in built cast it becomes default.
You
 can overwrite (not overload) a default function for local use (like you
can
 already). You cannot however overwrite a default function twice in the
same
 local space.

 If a function contains the cast word then it is explictly saying to
overload
 those values. You can overwrite (not overload) a cast function for local
 use. You cannot however overwrite a cast for the same types function
twice
 in the same local space.

 ie 1 -Wrong
 Module 1
 long abs(long n); (default function) //OK works in local space
 long abs(int n); (default function) //ERROR, cannot overwrite must cast
I'd let it be there, just for compatibility, and preserve the current semantics. IMO, it's better to think of cast() as of addition to rather than replacement of current schema of function overloading.
 Module 2
 long abs(int n); //(default function)
 long abs(int cast(int, long) n); //(cast function)
I think this should be an error. IMO, the best idea is to consider such declaration semantically equal to this: long abs(int); long abs(long);
You right, I did mean, long abs(int n); //(default function) long abs(int cast(double, long) n); //(cast function) I which case there shouldn't be any problems. So as a general example, the following should occure M1 (effected only by 1) 1 int abs(int); M2 (effected only by 2,3) 2 int abs(int); 3 int abs(double cast(double)); M3 (effected only by 4,5) 4 int abs(int); 5 int abs(int cast(long)); M4 (effected only by 4,5,3) import M1 import M2 import M3 Because It's good to encourage low coupling and high coheiesion. Also with scoping, sometimes it's nessary to overload a function that's outside the scope of the present model. For example a sorting algoithm that uses ">". This is my suggestion, which is not very neat and you may have a better idea. M1 (effected only by 1) 1 int abs(int); M2 (effected only by 2,3) 2 int abs(int); 3 int abs(double cast(double)); M3 (effected only by 4,5) 4 int abs(int); 5 int abs(int cast(long)); M4 (effected only by 4,5,3) import M1 import M2 6 int abs(int cast(long)) //Overwrites 5 import M3 (all function called from this one that use abs(long) are effected by 6)
 Since we already have abs(int) in this module, it should be
 forbidden.

 By the way, since n is already int, why list it in the cast clause?
 It should be implicit.
Jun 03 2002
parent "anderson" <anderson firestar.com.au> writes:
But that does not seem like a good idea to me.

 M1 (effected only by 1)
 1 int abs(int);

 M2  (effected only by 2,3)
 2 int abs(int);
 3 int abs(double cast(double));

 M3 (effected only by 4,5)
 4 int abs(int);
 5 int abs(int cast(long));

 M4 (effected only by 4,5,3)
 import M1
 import M2
 6 int abs(int cast(long)) //Overwrites 5
 import M3 (all function called from this one that use abs(long) are
effected
 by 6)
Jun 05 2002
prev sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
I kind of like this use of explicit.  Would it be better than this?

default double sqrt(double val) {}
float sqrt(float val) {}
extended sqrt(extended val) {}
int sqrt(int val) {}

Something I always wanted in C++ was the ability to mark operator
typeconvert as explicit.

Has anyone tried to do something like an iterator in D yet?  I suppose you
could just return slices but that has the potential to copy lots of data
around.  And the data structure might not be an array.

Sean

"anderson" <anderson firestar.com.au> wrote in message
news:adelqm$37h$1 digitaldaemon.com...
 I hate to keep this subject up, but have you considered explicit
 overloading?

 With explicit overloading, the function used will default to the default
 function, unless an explicit one has been defined specifically for that
 type.

 ie

 double sqrt(int val) {};
 explicit float sqrt(int val) {};
 explicit int sqrt(int val) {};

 Therefore if a short is given, it will default to the double method, but
if
 a float is given it will use the explicit type. The programmer can
optimise
 the float and int methods for better performance.


 Of cause there are some disadvantages of this to true overloading (having
do
 explicit more types), but It kind meets halfway.
Jun 03 2002
parent reply "anderson" <anderson firestar.com.au> writes:
Yes, for many reasons.

Portablily, non-overloaded functions are decleared the same way and can be
easily made overloaded without a change to the base function. In the case of
sqrt, you may not even have access to the default function.

Also you could for example define a double sqrt function and later decide
that it's not optimal for int. It's easier to then add an explicit function
rather then modify the orignal.

Although I'm open to ideas I'd vote against a default like that.

"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:adf73u$o1k$1 digitaldaemon.com...
 I kind of like this use of explicit.  Would it be better than this?

 default double sqrt(double val) {}
 float sqrt(float val) {}
 extended sqrt(extended val) {}
 int sqrt(int val) {}

 Something I always wanted in C++ was the ability to mark operator
 typeconvert as explicit.

 Has anyone tried to do something like an iterator in D yet?  I suppose you
 could just return slices but that has the potential to copy lots of data
 around.  And the data structure might not be an array.

 Sean

 "anderson" <anderson firestar.com.au> wrote in message
 news:adelqm$37h$1 digitaldaemon.com...
 I hate to keep this subject up, but have you considered explicit
 overloading?

 With explicit overloading, the function used will default to the default
 function, unless an explicit one has been defined specifically for that
 type.

 ie

 double sqrt(int val) {};
 explicit float sqrt(int val) {};
 explicit int sqrt(int val) {};

 Therefore if a short is given, it will default to the double method, but
if
 a float is given it will use the explicit type. The programmer can
optimise
 the float and int methods for better performance.


 Of cause there are some disadvantages of this to true overloading
(having
 do
 explicit more types), but It kind meets halfway.
Jun 03 2002
parent "anderson" <anderson firestar.com.au> writes:
Unless D supported both of coarse, because the "default" method would
require less typing in some situations. And the default method could require
prior planning, otherwise everthing automaticly becomes default. And having
two methods may be confusing ie

default double sqrt();
explicit int sqrt();
float sqrt(); //What is this? Explicit I suppose.

So I still think explicit method is better even though It requires more
typing.

"anderson" <anderson firestar.com.au> wrote in message
news:adf8op$pnp$1 digitaldaemon.com...
 Yes, for many reasons.

 Portablily, non-overloaded functions are decleared the same way and can be
 easily made overloaded without a change to the base function. In the case
of
 sqrt, you may not even have access to the default function.

 Also you could for example define a double sqrt function and later decide
 that it's not optimal for int. It's easier to then add an explicit
function
 rather then modify the orignal.

 Although I'm open to ideas I'd vote against a default like that.

 "Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
 news:adf73u$o1k$1 digitaldaemon.com...
 I kind of like this use of explicit.  Would it be better than this?

 default double sqrt(double val) {}
 float sqrt(float val) {}
 extended sqrt(extended val) {}
 int sqrt(int val) {}

 Something I always wanted in C++ was the ability to mark operator
 typeconvert as explicit.

 Has anyone tried to do something like an iterator in D yet?  I suppose
you
 could just return slices but that has the potential to copy lots of data
 around.  And the data structure might not be an array.

 Sean

 "anderson" <anderson firestar.com.au> wrote in message
 news:adelqm$37h$1 digitaldaemon.com...
 I hate to keep this subject up, but have you considered explicit
 overloading?

 With explicit overloading, the function used will default to the
default
 function, unless an explicit one has been defined specifically for
that
 type.

 ie

 double sqrt(int val) {};
 explicit float sqrt(int val) {};
 explicit int sqrt(int val) {};

 Therefore if a short is given, it will default to the double method,
but
 if
 a float is given it will use the explicit type. The programmer can
optimise
 the float and int methods for better performance.


 Of cause there are some disadvantages of this to true overloading
(having
 do
 explicit more types), but It kind meets halfway.
Jun 03 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:adcimp$2li4$1 digitaldaemon.com...
 "Pavel Minayev" <evilone omen.ru> wrote in message
 A headache for both the compiler (to find out such cases), and the
 developer. Okay, so this case is simple, but call to foo() might have
 happened in a complex expression, and due to the weak nature of D
 type system, many type conversions could happen, so it is hard to
 tell the desired type for the return value of foo() at some point.
Ok, seems resonable. So it's D fault for having a weak type system for returns types. And you like D having weak return types?
What Pavel's referring to are the default integral promotion rules, which in D are just like in C. Without them, there is too much casting necessary. Also, if function return types were overloadable, in order to compile an expression with multiple overloaded function calls in it, the compiler will have to try every combinatoric assortment of those functions trying to find the "best" match. I don't think the result of that will be pleasing <g>.
Jun 12 2002
parent reply "anderson" <anderson firestar.com.au> writes:
Have you read the rest of the suggestions? Explicit which changed into
Pavel's Cast like so,

    long abs(double n);
    long abs(long cast(byte, short, int) n);
    extended abs(extended cast(float) n);

That way there is no ambiguity and no extra casting nessary.
The initial one is the default, while other are explicit. By explicit I mean
must be one of the things in cast to be accepted. Of coarse there would
still be some problems programming the complier, but that's your field not
mine.


"Walter" <walter digitalmars.com> wrote in message
news:ae7v8h$a9d$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:adcimp$2li4$1 digitaldaemon.com...
 "Pavel Minayev" <evilone omen.ru> wrote in message
 A headache for both the compiler (to find out such cases), and the
 developer. Okay, so this case is simple, but call to foo() might have
 happened in a complex expression, and due to the weak nature of D
 type system, many type conversions could happen, so it is hard to
 tell the desired type for the return value of foo() at some point.
Ok, seems resonable. So it's D fault for having a weak type system for returns types. And you like D having weak return types?
What Pavel's referring to are the default integral promotion rules, which
in
 D are just like in C. Without them, there is too much casting necessary.

 Also, if function return types were overloadable, in order to compile an
 expression with multiple overloaded function calls in it, the compiler
will
 have to try every combinatoric assortment of those functions trying to
find
 the "best" match. I don't think the result of that will be pleasing <g>.
Jun 12 2002
parent reply "Walter" <walter digitalmars.com> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:ae96c2$24ol$1 digitaldaemon.com...
 Have you read the rest of the suggestions? Explicit which changed into
 Pavel's Cast like so,

     long abs(double n);
     long abs(long cast(byte, short, int) n);
     extended abs(extended cast(float) n);

 That way there is no ambiguity and no extra casting nessary.
 The initial one is the default, while other are explicit. By explicit I
mean
 must be one of the things in cast to be accepted. Of coarse there would
 still be some problems programming the complier, but that's your field not
 mine.
Yes, that handles overloading by argument, but not by function return value.
Jun 12 2002
parent "anderson" <anderson firestar.com.au> writes:
The same can be applied,

double abs(long n);
long cast(byte, short, int) abs(long  n);
extended cast(float) abs(long n);

I think Paval mentioned that.

"Walter" <walter digitalmars.com> wrote in message
news:ae9h0g$2hal$1 digitaldaemon.com...
 "anderson" <anderson firestar.com.au> wrote in message
 news:ae96c2$24ol$1 digitaldaemon.com...
 Have you read the rest of the suggestions? Explicit which changed into
 Pavel's Cast like so,

     long abs(double n);
     long abs(long cast(byte, short, int) n);
     extended abs(extended cast(float) n);

 That way there is no ambiguity and no extra casting nessary.
 The initial one is the default, while other are explicit. By explicit I
mean
 must be one of the things in cast to be accepted. Of coarse there would
 still be some problems programming the complier, but that's your field
not
 mine.
Yes, that handles overloading by argument, but not by function return
value.

Jun 13 2002
prev sibling next sibling parent reply Karl Bochert <kbochert ix.netcom.com> writes:
 D does not use the C calling convention (not always, at least). So
 it's wrong to assume that the caller will clean up the stack.
It is quite easy for the compiler to force an appropriate calling convention on a multiple-return function.
 Also, how exactly do you want to pass something on the stack in
 such a way that callee can read it? Don't forget that you also
 have the PUSHed value of EBP, and then the return address. You
 can POP EBP first, of course, and then push the return values,
 but:
 
 1) You then don't have access to local variables (since EBP
 is restored to its old value), and registers might not be enough
 (what if function returns 10 values?)
 
 2) RET expects return address to be on top. This can be done
 by POPping it into the register, then pushing all the return
 values, then PUSHing the return address again. Kinda messy.
Compared to what else the compiler must do, this seems trivial. Another approach would be to use an 'on-stack' calling convention. (The D calling convention??) Roughly (I think): int a, int b foo ( int c ) { ... Compiler creates 'pseudo-parameters' for returns-- pop EAX push 0 push 0 push EAX And then the normal entry code -- push EBP mov EBP, ESP Now: a is at [ebp + 8] b is at [ebp + 12] c is at [ebp + 16] Return is just a normal return: mov ESP, EBP ret and the caller does: call foo pop EAX ; get 'a' mov [EBP - 4], eax ; put it somewhere pop EAX ; get 'b' add ESP, 8 ; standard parameter trim
 By the way, in C, it was even worse: the structure was allocated once,
 and only one copy of it existed for one function... so each call rewrote
 data generated by previous one. C++ provides a workaround for this, at
 a cost of efficiency.
I suppose the 'C calling convention' was invented prior to recursion or threading!! :-) Karl Bochert
May 30 2002
next sibling parent "Pavel Minayev" <evilone omen.ru> writes:
"Karl Bochert" <kbochert ix.netcom.com> wrote in message
news:1103_1022775181 bose...

 I suppose the 'C calling convention'  was invented prior to recursion
 or threading!! :-)
Not really. I guess those guys were just too lazy to cover this case properly, bearing in mind that C was a tool made for private use at first...
May 30 2002
prev sibling parent reply "Christian Schüler" <cschueler gmx.de> writes:
I thought that the caller would leave the space on the stack to hold the
return values. Basically that's equivalent to your version just that the
first part (before the push EBP instruction) is done by the caller.

int, int, int foo()
{    
    return 1, 2, 3;
}

compiles to:

    push    EBP
    mov    EBP, ESP
    mov    [EBP+16], 1
    mov    [EBP+12], 2
    mov    [EBP+8], 3
    mov    ESP, EBP
    pop    EBP
    ret

This function does not necessarily need the frame pointer EBP (In GCC, you can
compile with option -fomit-frame-pointer), in this case the function could be
even smaller:

    mov    [ESP+12], 1
    mov    [ESP+8], 2
    mov    [ESP+4], 3
    ret

But this is an optimisation which is not necessary at the moment.
Now let's look at some calling code:

int a, b, c;
a, b, c = foo();

In my model, the caller leaves room on the stack for the return values to
accumulate. So the example would compile to:

    sub    ESP, 12        ; make room for 3 int on the stack
    call    _foo              ; call foo
    mov    _a, [ESP+8]    ; store results
    mov    _b, [ESP+4]
    mov    _c, [ESP]
    add    ESP, 12        ; clean up stack

Then, my second example function bar(), which is an ordinary function that
takes 3 parameters and returns 1 int.

int bar( int x, int y, int z )
{
    return x + y + z;
}

I assume that a single integer value is not returned on the stack but in
register EAX, as for instance GCC does. Then the function might compile into
this code (again with full frame pointer enabled):

    push    EBP
    mov    EBP, ESP
    mov    EAX, [EBP+16]
    add    EAX, [EBP+12]
    add    EAX, [EBP+8]
    mov    ESP, EBP
    pop    EBP
    ret
    
As you can see, the first return value of function foo() is addressed as
[EBP+16] within foo(), which is the same location as the first parameter of
function bar(). So you can substitute the return of function foo() for any 3
int parameters for a function call. I follow my example:

int sum =
 bar( foo() );

which should compile to:

    sub    ESP, 12    ; prepare to call function foo
    call    _foo        ; will leave 3 return values on the stack
    call    _bar        ; same as if we had pushed 3 single parameters
    add    ESP, 12    ; clean up stack
    mov    _sum, EAX

So what do you think is this easy to implement in the compiler?


Karl Bochert <kbochert ix.netcom.com> schrieb in im Newsbeitrag:
1103_1022775181 bose...
 D does not use the C calling convention (not always, at least). So
 it's wrong to assume that the caller will clean up the stack.
It is quite easy for the compiler to force an appropriate calling convention on a multiple-return function.
 Also, how exactly do you want to pass something on the stack in
 such a way that callee can read it? Don't forget that you also
 have the PUSHed value of EBP, and then the return address. You
 can POP EBP first, of course, and then push the return values,
 but:

 1) You then don't have access to local variables (since EBP
 is restored to its old value), and registers might not be enough
 (what if function returns 10 values?)

 2) RET expects return address to be on top. This can be done
 by POPping it into the register, then pushing all the return
 values, then PUSHing the return address again. Kinda messy.
Compared to what else the compiler must do, this seems trivial. Another approach would be to use an 'on-stack' calling convention. (The D calling convention??) Roughly (I think): int a, int b foo ( int c ) { ... Compiler creates 'pseudo-parameters' for returns-- pop EAX push 0 push 0 push EAX And then the normal entry code -- push EBP mov EBP, ESP Now: a is at [ebp + 8] b is at [ebp + 12] c is at [ebp + 16] Return is just a normal return: mov ESP, EBP ret and the caller does: call foo pop EAX ; get 'a' mov [EBP - 4], eax ; put it somewhere pop EAX ; get 'b' add ESP, 8 ; standard parameter trim
 By the way, in C, it was even worse: the structure was allocated once,
 and only one copy of it existed for one function... so each call rewrote
 data generated by previous one. C++ provides a workaround for this, at
 a cost of efficiency.
I suppose the 'C calling convention' was invented prior to recursion or threading!! :-) Karl Bochert
May 30 2002
parent "Pavel Minayev" <evilone omen.ru> writes:
prev sibling parent reply "Martin M. Pedersen" <mmp www.moeller-pedersen.dk> writes:
Hi,

"Pavel Minayev" <evilone omen.ru> wrote in message
news:ad4djm$8na$1 digitaldaemon.com...
 But the structure isn't pushed on the stack! It is allocated somewhere,
 and pointer to it is passed in EAX.

 By the way, in C, it was even worse: the structure was allocated once,
 and only one copy of it existed for one function... so each call rewrote
 data generated by previous one. C++ provides a workaround for this, at
 a cost of efficiency.
This is new to me. I just checked the code generated by Microsoft C/C++ in C mode. It allocates an anonymous stack variable for the return value in the calling code for each function call (not each invocation), which is perfectly reentrant. Small structs are returned in registers. I checked GCC too, and it also allocates the structure on the stack in the calling code. The behaviour you describe above sounds like a compiler bug to me. Regards, Martin M. Pedersen
May 30 2002
next sibling parent "Pavel Minayev" <evilone omen.ru> writes:
"Martin M. Pedersen" <mmp www.moeller-pedersen.dk> wrote in message
news:ad5iq8$1ri2$1 digitaldaemon.com...

 This is new to me. I just checked the code generated by Microsoft C/C++ in
C
 mode. It allocates an anonymous stack variable for the return value in the
 calling code for each function call (not each invocation), which is
 perfectly reentrant. Small structs are returned in registers. I checked
GCC
 too, and it also allocates the structure on the stack in the calling code.
 The behaviour you describe above sounds like a compiler bug to me.
It doesn't. The C++ standard does not specify how it is done, AFAIK, it just says that function must be reentrant regardless of what it returns. I never checked output of most used C++ compilers you've mentioned, and the way I described (callee allocates struct on heap, caller deallocates), I've seen in one incomplete C++ compiler a friend of mine was trying to write. At least it worked...
May 30 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Martin M. Pedersen" <mmp www.moeller-pedersen.dk> wrote in message
news:ad5iq8$1ri2$1 digitaldaemon.com...
 This is new to me. I just checked the code generated by Microsoft C/C++ in
C
 mode. It allocates an anonymous stack variable for the return value in the
 calling code for each function call (not each invocation), which is
 perfectly reentrant. Small structs are returned in registers. I checked
GCC
 too, and it also allocates the structure on the stack in the calling code.
 The behaviour you describe above sounds like a compiler bug to me.
It's the way C compilers used to work. -Walter
Jun 13 2002