D - Multiple returns / was: x, y, z = 0
- "Christian Schüler" <cschueler gmx.de> May 29 2002
- "Pavel Minayev" <evilone omen.ru> May 29 2002
- Russ Lewis <spamhole-2001-07-16 deming-os.org> May 29 2002
- "Christian Schüler" <cschueler gmx.de> May 29 2002
- "Pavel Minayev" <evilone omen.ru> May 29 2002
- "anderson" <anderson firestar.com.au> May 30 2002
- "Pavel Minayev" <evilone omen.ru> May 30 2002
- "anderson" <anderson firestar.com.au> May 31 2002
- "Pavel Minayev" <evilone omen.ru> May 31 2002
- "anderson" <anderson firestar.com.au> Jun 01 2002
- "Pavel Minayev" <evilone omen.ru> Jun 01 2002
- "anderson" <anderson firestar.com.au> Jun 02 2002
- "anderson" <anderson firestar.com.au> Jun 02 2002
- "Pavel Minayev" <evilone omen.ru> Jun 02 2002
- "anderson" <anderson firestar.com.au> Jun 02 2002
- "anderson" <anderson firestar.com.au> Jun 02 2002
- "Pavel Minayev" <evilone omen.ru> Jun 03 2002
- "anderson" <anderson firestar.com.au> Jun 03 2002
- "Pavel Minayev" <evilone omen.ru> Jun 03 2002
- "anderson" <anderson firestar.com.au> Jun 03 2002
- "anderson" <anderson firestar.com.au> Jun 03 2002
- "Pavel Minayev" <evilone omen.ru> Jun 03 2002
- "anderson" <anderson firestar.com.au> Jun 03 2002
- "anderson" <anderson firestar.com.au> Jun 05 2002
- "Sean L. Palmer" <seanpalmer earthlink.net> Jun 03 2002
- "anderson" <anderson firestar.com.au> Jun 03 2002
- "anderson" <anderson firestar.com.au> Jun 03 2002
- "Walter" <walter digitalmars.com> Jun 12 2002
- "anderson" <anderson firestar.com.au> Jun 12 2002
- "Walter" <walter digitalmars.com> Jun 12 2002
- "anderson" <anderson firestar.com.au> Jun 13 2002
- Karl Bochert <kbochert ix.netcom.com> May 30 2002
- "Pavel Minayev" <evilone omen.ru> May 30 2002
- "Christian Schüler" <cschueler gmx.de> May 30 2002
- "Pavel Minayev" <evilone omen.ru> May 31 2002
- "Martin M. Pedersen" <mmp www.moeller-pedersen.dk> May 30 2002
- "Pavel Minayev" <evilone omen.ru> May 30 2002
- "Walter" <walter digitalmars.com> Jun 13 2002
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
"Christian Schüler" <cschueler gmx.de> wrote in message news:ad36rt$16n9$1 digitaldaemon.com...I'm moving this discussion to a new thread. What about multiple return values, like in LUA:
And how do you want this to be implemented? Lua is a bytecode interpreter, it has its own stack-based VM, and return values are passed on the stack... it's all easily handled. But I don't think you can easily do it in a language which compiles to native code. BTW, out-parameters are supposed to exist for such cases =)
May 29 2002
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
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
"Christian Schüler" <cschueler gmx.de> wrote in message news:ad3cnv$1dr6$1 digitaldaemon.com...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
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. 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.prototype, it is known at compile time what return values a function
(Think as an alternative that the function foo() would have returned a structure containing 3 integers, pretty legal in C++).
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.
May 29 2002
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...
"Christian Schüler" <cschueler gmx.de> wrote in message
news:ad3cnv$1dr6$1 digitaldaemon.com...
Yes, that's the point. I was thinking about it how such a feature could
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
stack, three integers. There's basically nothing wrong with that, as the
caller is responsible for cleaning up the stack, and, through the
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.
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.
prototype, it is known at compile time what return values a function
(Think as an alternative that the function foo() would have returned a
structure containing 3 integers, pretty legal in C++).
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.
May 30 2002
"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
"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
"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
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
parameters to imitate it...
Jun 01 2002
"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
"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
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
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
"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
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
(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
"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
"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... =)
anything.)
Jun 03 2002
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
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
"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.
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
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
already). You cannot however overwrite a default function twice in the
local space. If a function contains the cast word then it is explictly saying to
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
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
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
"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
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
already). You cannot however overwrite a default function twice in the
local space. If a function contains the cast word then it is explictly saying to
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
"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
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 IdeaExtending some of my orignal ideas. If a function is deleared without a in built cast it becomes default.
can overwrite (not overload) a default function for local use (like you
already). You cannot however overwrite a default function twice in the
local space. If a function contains the cast word then it is explictly saying to
those values. You can overwrite (not overload) a cast function for local use. You cannot however overwrite a cast for the same types function
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
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
by 6)
Jun 05 2002
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
a float is given it will use the explicit type. The programmer can
the float and int methods for better performance.
Of cause there are some disadvantages of this to true overloading (having
explicit more types), but It kind meets halfway.
Jun 03 2002
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
a float is given it will use the explicit type. The programmer can
the float and int methods for better performance. Of cause there are some disadvantages of this to true overloading
doexplicit more types), but It kind meets halfway.
Jun 03 2002
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
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
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
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
function, unless an explicit one has been defined specifically for
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,
ifa float is given it will use the explicit type. The programmer can
the float and int methods for better performance. Of cause there are some disadvantages of this to true overloading
doexplicit more types), but It kind meets halfway.
Jun 03 2002
"anderson" <anderson firestar.com.au> wrote in message news:adcimp$2li4$1 digitaldaemon.com..."Pavel Minayev" <evilone omen.ru> wrote in messageA 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.
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
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.
returns types. And you like D having weak return types?
What Pavel's referring to are the default integral promotion rules, which
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
have to try every combinatoric assortment of those functions trying to
the "best" match. I don't think the result of that will be pleasing <g>.
Jun 12 2002
"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
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
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
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
mine.
Yes, that handles overloading by argument, but not by function return
Jun 13 2002
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.
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.
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 trimBy 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
"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
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.
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.
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
"Christian Schüler" <cschueler gmx.de> wrote in message news:ad61eg$242$1 digitaldaemon.com...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?
Yes, you've convinced me... it's all simplier than I thought. Practically, it's the same out-parameters, only hidden. Oh well. I guess it would be a nice bit of syntactic sugar.
May 31 2002
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
"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
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
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
"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
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
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









Russ Lewis <spamhole-2001-07-16 deming-os.org> 