tags:

views:

193

answers:

4

In D main fnc is defined:

void main(/*perhaps some args but I do not remember*/)
{

}  

My question is that I know for sure that this fnc returns zero on success and non-zero on failure and yet it is defined as to not returning anything. What is the logic behind it?

+1  A: 

If you define the function as

int main(...);

then the return value, that you can get (in bash) using

echo $?

will be whatever you return from the function. If you don't return anything, or you define your main function as

void main(...);

then the exit status of the command is undefined. For some reason (I can't find any documentation on this) it's always 200 on my system.

Delan Azabani
@Delan but if function is defined with void as a return type it shouldn't return anything, not even 200. The problem I have with this is that even though void is as a return type this function always returns something and that to me is illogical, would you agree with me?
There is nothing we can do
The *function* doesn't return anything if you use `void main`. Once `main` returns, the *D runtime support* gives a process exit code to the operating system, regardless of which form you use. If you use `int main`, the value you return is the one passed back to the OS. The OS doesn't know or care what language you used; the existence of an exit code does not depend on that.
shambulator
@shambulator disagree, Andrei Alexandrescu says: "In D, void main() returns zero (success) to the operating system on normal return, and nonzero upon exception." and that to me is highly illogical, do you agree with me?
There is nothing we can do
@there... What's illogical about it? zero is success (and void means that it's always success if no exception was thrown), and an exception is obviously failure, so that would be non-zero. It seems perfectly logical.
Jonathan M Davis
It seems logical for me. A program *must* have a return value. Even when its `main` is `void`.
Delan Azabani
Added an answer; ran out of comment space :)
shambulator
+5  A: 

There are several possible signatures for main():

void main()
void main(string[] args)
void main(char[][] args)
void main(wstring[] args)
void main(wchar[][] args)
void main(dstring[] args)
void main(dchar[][] args)
int main()
int main(string[] args)
int main(char[][] args)
int main(wstring[] args)
int main(wchar[][] args)
int main(dstring[] args)
int main(dchar[][] args)

If int is the return type, then it's pretty much the same is in C or C++. The value that you return is what the OS/shell sees. If an exception is thrown, then a stack trace is printed, and the OS/shell sees a non-zero value. I don't know what it is. It may vary by exception type.

If void is the return type, then the OS/shell sees 0. If an exception is thrown, then a stack trace is printed, and the OS sees a non-zero value. Again, I don't know what it is.

Essentially, having void main allows you to not worry about returning a value to the OS/shell. Many programs are not in the least bit concerned with returning success or failure to the OS/shell. So, with void, the OS/shell always gets 0 unless an exception is thrown - which makes sense, since the only program failure at that point is if an exception escapes main(). If you do care about returning success or failure to the OS/shell, then you simply use one of the versions that returns int.

The plethora of signatures due to different string types is that you can use pretty much any of the possible string types as the input to main(). main() and main(string[] args) are probably the most commonly used though.

Jonathan M Davis
+3  A: 

function with void return type does not return any value. There is nothing illogical when you consider call stack looks like this:

OS -> D runtime -> main

The main function is invoked by D runtime system, which recognized that the main function returns nothing - and in this case return success to the OS. In case the main function is defined with return type int, then the D runtime return to OS value returned by main function.

Michal Minich
@Michal I've accepted your answer because is the clearest one. But I think that if you see main with void it is at best confusing if someone says that it returns something. Not the best choice on D part in my opinion.
There is nothing we can do
It is a bit misleading, but no more so than something like "`main` returns a value to the operating system". Which is what the documentation for most languages states, in pretty much those terms :) There's always an intermediate layer to 1) handle OS-specific interactions and 2) enforce language semantics.
shambulator
@shambulator but the value returned by main (in C++) is the value returned to the system so for me as for "a user of a language" it doesn't matter really what other layers will be there - I'm clearly stating that from the last point I have the control over (main) my program returns what main returns and having void as a return type and yet returning something "implicitly" it's just a bad idea. For me anyway. And when you're saying that it is not true that main returns value to the operating system it is not true because a main is the first point (or the last) depend how you look at this -
There is nothing we can do
which you have control over and exactly the value returned by main is returned to the system. It is bit like saying that letter which I've received is not from sender but from postman because he was the person (the intermediate layer) who gave me the letter. This is obviously not true. He was just a transporter (intermediate layer) of something (returning value) that sender (main) sent to me (OS). So in fact is the main which returnes value to the system using n-intermediate layers.
There is nothing we can do
At the risk of flogging a dead horse (we're clearly on the same page as to what's actually happening behind the scenes), isn't it simpler to say "`void main` (conceptually) returns 0 to the OS" even if it's not literally true, rather than having to haul out something like my answer? Technically, the letter is from the postman :) As you point out, it's a difference of interpretation. Interpret "return" more broadly where `main` is concerned, because it's treated specially in all languages. The reason is programmer convenience, as pointed out by @Jonathan M Davis.
shambulator
At the risk of *further* flogging a dead horse, the stumbling block in your original question is "I know for sure that this fnc **returns** zero on success and non-zero on failure". It *doesn't*, because a `void` function can't return a value. The phrase "returns 0" is overloaded for `main`, to mean "exits with a process exit code of 0". And this is far from specific to D.
shambulator
@shambulator I really do not see point of having two functions being different just by return type (and by the way, how does it works with overloading rules? Are you allowed to have two identical fnc with only return type different?) instead of one specifically declaring return type and if nothing else is stated then zero is assumed. Things like void main and int main in one language only cause confusion. Those are original words of Andrei Alexandrescu (in his email to me):
There is nothing we can do
Andrei Alexandrescu says: "In D, void main() returns zero (success) to the operating system on normal return, and nonzero upon exception." To me it is at best confusing and at worse illogical.
There is nothing we can do
I really don't know what else to tell you. You're taking the word "return" much too literally. `void main` returns (in the D programming language keyword sense) **no value** (as expected). If you tried compiling `int x = main();` you'd get the obvious compiler error. Instead, when the execution of `main` ends, if its return type is `void`, then the runtime code generated by the compiler "returns" (in the language-agnostic sense of tearing the process down, *not* the D-specific keyword sense) an exit code of 0 to the OS. I think the pseudocode in my answer explains it better than descriptions.
shambulator
@shambulator and what about having two fnc which differ only by return type?
There is nothing we can do
Overloading works as normal. You choose which version of `main` you want, and write only one of them, otherwise the compiler will complain. Depending on which version you choose, the compiler generates different startup/shutdown code (the `static if` part of my pseudocode), which sets the process exit code to 0 in the case of a `void main` (what you refer to as "returning 0" to the OS), or the return value of an `int main`. The `void` version is supported purely for convenience, so you get the common values for "success" or "generic failure" without having to write code to explicitly set them.
shambulator
Just occurred to me that you might not have seen `static if` before, and that's causing some confusion about overloading. In my example, `static if` is a conditional compilation construct to compile only one of the lines `exitCode = main(cmdLineArgs);` and `main(cmdLineArgs);` depending on the return type of `main`. It's the D-like substitute for C++'s `#ifdef`. You don't actually have both versions of `main` defined.
shambulator
+7  A: 

What Alexandrescu is saying is simply shorthand for the exit code I described. The zero or nonzero returned to the OS is a (language-agnostic) process exit code, not a function return value. The OS doesn't call main directly, and main doesn't return directly to the OS. The D compiler inserts startup and shutdown code in your program to handle these interactions with the OS, as do pretty much all other compilers for other languages. On startup, for instance, this boilerplate code uses some OS-dependent mechanism to get the command-line arguments, and puts them into a D string[] array to pass to main. On shutdown, it uses the return value from int main for the exit code, or, for void main, uses its own value (0 for success, nonzero for unhandled exception).

In pseudocode:

// Generated by compiler
void _realMain()
{
    // OS-dependent; probably calls GetCommandLineW
    // and CommandLineToArgvW on Windows, for example
    string[] cmdLineArgs = createArgArray();

    int exitCode = 0;    // Assume success
    try
    {
        // static if selects only one call to main for compilation,
        // depending on main's return type.

        // If main has been written to return int, use its value for the exit code
        static if (main returns int)
            exitCode = main(cmdLineArgs);
        // If main has been declared void, just run it and keep the exit code of 0
        else
            // void main: *doesn't return anything*
            main(cmdLineArgs);
    }
    catch
    {
        // Unhandled exception results in non-zero exit code
        exitCode = 1;
        printStackTrace();
    }

    // OS-dependent process shutdown function.
    // This is where the exit code is "returned" to the OS.
    // Note it *does not* use the return keyword to do so.
    // Calling the OS's function to kill the current process
    // does not return, because the process is dead by the
    // time the function has finished!
    exitProcess(exitCode);
    // In particular, no code *after* the exitProcess line will run.
}
shambulator