tags:

views:

471

answers:

9

Hello, this wierd question just came to my mind.

Why isnt it possible to call a function which takes no arguments with a function call as argument which does not return any value (which imho is equivalent to calling a function which takes no arguments with no arguments).

f.e.

void foo(void) {...}
void bar(void) {...}

foo(bar())

Dont get me wrong, i know void is not a value and that it can not be treated like one.

With my logic it would make sense and it should be possible to do that. I mean, why not? Any argument why that should not be possible?

Let me hear your opinions about this.

A: 

Because void doesn't allow you to specify an argument name, which would be required in order to capture the return value of the first function, regardless of what it actually returned.

Ignacio Vazquez-Abrams
You can construct a void object. `void(3);` is a way to do nothing, and would match the semantics of argument-passing values were allowed to be passed to `void` functions.
Potatoswatter
+1  A: 

it does make kind of sense (bar produces nothing, foo consumes nothing, therefore foo(bar()) should be allowed). OTOH, it would only be good to confuse the reader. if you want to be l33t, there's always ,, && and || operators to emulate semicolons.

just somebody
A: 

Let's pretend it works. Can you think of a single hypothetical case where

bar();
foo();

which does precisely the same thing isn't vastly more readable and sensible?

Edited. Yes, yes. :)

ezod
I think you'll find that `bar(); foo()` is actually the correct order of execution ;)
Anon.
Actually, it's `bar(); foo();` that does precisely the same thing. Arguments are fully evaluated before the function is called.
caf
At the moment i have something like this in my code:#define EVAL(f) f(++evalcounter)Dont ask me why i use it, i just have to.Normally f does not take any values. I just added this (global) integer just to count something. Wouldnt it be much better if i had something like #define EVAL(f) f( dummy(++evalcounter) ) ?
George B.
It doesn't matter if it'sfoo();bar();orbar();foo();if your functions don't have side-effects. They don't, do they?
Duncan
`#define EVAL(f) (++evalcounter,f())`
Steve Jessop
Check the comments over this post :)
George B.
@Duncan: if you're going to claim that the design of C and C++ is motivated by an assumption that void functions never have side-effects, then you've got a *lot* of explaining to do ;-) The question isn't why *you* wouldn't allow this language change, it's why the C committee didn't. The C committee has no problem with side effects, indeed goes to some lengths to define when their order can and can't be known (sequence points).
Steve Jessop
@Steve Jessop: I'm not making that claim - I'm claiming that if your functions don't have side-effects the order doesn't matter. I can't speak for what motivates the C committee. It's a committee after all. :) Obviously in the original case of foo(bar()) the argument must be evaluated first so order matters. This is all beside the point of the question though.
Duncan
+7  A: 

I'm not convinced that any of the reasons I've heard are good ones.

See, in C++, you can return a void function's result:

void foo() {
    // ...
}

void bar() {
    // ...
    return foo();
}

Yes, it's exactly the same as:

foo();
return;

but is much more consistent with generic programming, so that you can make a forwarding function work without having to worry about whether the function being forwarded has void return.

So, if a similar system applied so that a void return constituted a nullary call in a function composition scenario, that could make function composition more generic too.

Chris Jester-Young
Nice answer! I really didnt know it. So if that is possible, why shouldnt be what i wrote possible. Its about the same thing.
George B.
indeed it would, but `return` is a special case by 6.6.3/4: "A return statement with an expression of type “cv void” can be used only in functions with a return type of cv void; the expression is evaluated just before the function returns to its caller."
Potatoswatter
Exactly my point. :-)
Chris Jester-Young
@Potatoswatter: Sure. So just make cv void usable for composition only if the receiving function is nullary. (I know C++ doesn't currently work that way, but it's something to think about.)
Chris Jester-Young
In fact, if the language had built-in support for tuples (as opposed to `std::tr1::tuple`), C++ could then allow compositions with multiple-value returns, and it'd call the receiving function with multiple arguments.Oh, did I just describe `call-with-values`? I sure did! :-P
Chris Jester-Young
You may have answered your own question there - the reason there's a special case for `return` is that it always takes either 0 operands or 1. It's easy to allow for some generic-ness between the two. Function calls can take any number of parameters. To support generic-ness you'd need multiple value returns, so that we can do `void foo(int, int); X bar(void); foo(bar());` for some X. Unless you can support "any possible number", there's not much value in special-casing 0.
Steve Jessop
@Steve: I totally agree (+1); this is totally purely food-for-thought material.
Chris Jester-Young
A: 

At the moment i have something like this in my code: #define EVAL(f) f(++evalcounter) Dont ask me why i use it, i just have to. Normally f does not take any values. I just added this (global) integer just to count something. Wouldnt it be much better if i had something like #define EVAL(f) f( dummy(++evalcounter) ) ?

Have you tried the comma operator:

#define EVAL(f)   (++evalcounter, f())
R Samuel Klatchko
The problem is that before or after EVAL there may be other variables which are connected to f, so adding this would totally destroy this connection.
George B.
What do you mean by 'connected to f'? If you do `a = EVAL(f)`, my suggestion will correctly assign the return value of f to a.
R Samuel Klatchko
@George: I think you should open a new question for that with practical examples of where you got problems.
Georg Fritzsche
Thanks for trying to help me but it really is much more complicated. Just a few words to explain what im doing: its an "embedded" functional language in c++ just by using defines and a few classes (and tricks like operator overloading etc) to "parse" the file using the preprocessor and create whatever is necessary (syntax tree, other things).
George B.
+3  A: 

Because, according to paragraph 3.9.1/9 of the standard,

An expression of type void shall be used only as an expression statement (6.2), as an operand of a comma expression (5.18), as a second or third operand of ?: (5.16), as the operand of typeid, or as the expression in a return statement (6.6.3) for a function with the return type void.

C/C++ just isn't designed to be that generic. You do get return returns_void(); for tail-call optimization, that's functional-ish, right? :vP

Edit: The above rule would still allow you to call takes_void(3) with 3 converted to void. This is forbidden by 8.3.5/2:

If the parameter-declaration-clause is empty, the function takes no arguments. The parameter list (void) is equivalent to the empty parameter list. Except for this spe- cial case, void shall not be a parameter type (though types derived from void, such as void*, can).

Potatoswatter
Hehe well at least something we can do :) Btw i understand that c/c++ is not designed for that. I just wanted to hear opinions about what i thought about. I still believe it would be totally logical if this was possible.
George B.
@George: I think the remarkable part is how many rules are required to forbid it. Perhaps it was possible on older C compilers and the C++ committee felt it to be poor style. Having more generic-ness today gives a different perspective.
Potatoswatter
+2  A: 

If the (void) parameter list was treated uniformly with all other parameter lists in C/C++, the semantic meaning if such parameter declaration would be "a single parameter of type void". If that was the case, it is quite possible that for the purposes of uniformity the language would allow to "chain" the calls to such functions as shown in your example. (At least C++ probably would, since it allows this kind of uniformity in return statements).

However, in C++ language as well as in C, parameter list that has the form (void) is not treated uniformly with other forms of parameter lists. Instead, it has a special meaning. It means that the function has no parameters at all. (Equivalent to empty () in C++).

In other words, the function declared with (void) parameter list takes zero parameters. You are supplying one. This is what makes it illegal. Considering the special meaning of (void) parameter list in C/C++, allowing

foo(bar());

would not be much different from allowing

foo(bar(), bar());
foo(bar(), bar(), bar());

as well. Qualitatively, 2 and 3 are as much different from 0, as 1 is.

AndreyT
+2  A: 

Don't be misled by the notation. Rather annoyingly, C uses the f(void) prototype to mean "f expects no parameters" rather than "f expects a single parameter of type void". The notation f() was kept to mean "the number of parameters that f expects is not known, so you can call it with any number of parameters you like, and good luck to you". Before ANSI Standard C (aka C89), there was no such thing as a function prototype, and you needed a tool like lint to protect you against even the most mundane sorts of errors with parameters, such as passing the wrong number of parameters or passing a parameter with a wildly incompatible type.

As to why you can't use the value of a function return void as a value, or why you can't pass a parameter to a function that expects no parameters, those restrictions are in place to protect you from making the most mundane sorts of errors with parameters.

Norman Ramsey
A: 

I'll add one extra declaration: void baz(int);

Now obviously if an argument with type void is equal to no arguments, then two arguments, one of which has type void would be equal to one argument (as 0 = 1*x <=> 1 = 1+1*x)

So, obviously this is legal: baz(1, bar());. But also (since void is "nothing") baz(bar(),1);.

And we can continue in this sense: baz(bar(), 1, bar());. After all, "void is nothing".

Either you ban it outright, put in arbitrary restrictions, or you end up allowing ridiculous constructs. I agree with the choice for the first resolution

MSalters
Well it would be easy to make what i said possible and legal and not allow what you said. Dont forget the above expressions are all comma separated, which indicates that both have to be something. To explain myself better: If "void would be nothing", the above would equal to baz(1 , "nothing"), which isnt legal because "something" is expected.
George B.