Changing the return type of a function from void to int

C

C++Liliput

Hi,
I have a set of APIs that currently have a void return type. I want
to change this to int because I am wrapping around these APIs in JNI
and want to throw exceptions under certain error conditions. Since
that is possible only if I return an int (that denotes an error
condition), my question is this - does changing the return type of a
function from void to int break any backward compatibility with the
older clients of the API? It doesn't look like it since the older
clients can continue to use the new APIs in the void context. Are
there any other pitfalls to this change?

Thanks
Aveek
 
S

Shao Miller

I have a set of APIs that currently have a void return type. I want
to change this to int...
...
...my question is this - does changing the return type of a
function from void to int break any backward compatibility with the
older clients of the API?

Do you mean if the older clients are recompiled to use modified headers?
Or do you mean that the older clients remain the way they are but will
be calling into a modified library where these functions now return
'int' instead of 'void'?

For the latter, I might be worried a bit about n1256.pdf's 6.5.2.2p9.
The older clients would be calling the functions using a function type
other than how the functions are defined in the newly-compiled library.
If that results in undefined behaviour, and outside of the scope of C,
if a stack is used to return function values, and if the caller is
expected to clean up that stack after a function call, the older clients
would not be cleaning very well by assuming a 'void' return type.

Or maybe I'm wrong and in:

typedef void f_ret_void(int);
typedef int f_ret_int(int);

the types 'f_ret_void' and 'f_ret_int' are compatible. That doesn't
seem quite right, though. :)

Perhaps this is a non-issue in your environment, though. Does the
product/do the products need to be utterly portable in a standard sense?
 
K

Keith Thompson

C++Liliput said:
I have a set of APIs that currently have a void return type. I want
to change this to int because I am wrapping around these APIs in JNI
and want to throw exceptions under certain error conditions. Since
that is possible only if I return an int (that denotes an error
condition), my question is this - does changing the return type of a
function from void to int break any backward compatibility with the
older clients of the API? It doesn't look like it since the older
clients can continue to use the new APIs in the void context. Are
there any other pitfalls to this change?

As far as the C language is concerned, a function returning int is
simply incompatible with a function returning void, and the behavior of
a program that calls one such function as if it were the other is
undefined.

It may be that you can get away with it on your platform, but that's a
question about your platform, not about C.
 
F

Fred

As far as the C language is concerned, a function returning int is
simply incompatible with a function returning void, and the behavior of
a program that calls one such function as if it were the other is
undefined.

So you are saying that the statement
printf( "Hello, World!\n" );
provokes undefined behavior, since I am calling it
as if it were a function returning void rather than
int n;
n = printf( "Hello, World!\n" );
where I am calling it as a function returning an int?
 
K

Keith Thompson

Fred said:
So you are saying that the statement
printf( "Hello, World!\n" );
provokes undefined behavior, since I am calling it
as if it were a function returning void rather than
int n;
n = printf( "Hello, World!\n" );
where I am calling it as a function returning an int?

No, not at all.

If you have the required #include <stdio.h>, then the correct
declaration of printf (returning int) is visible to the compiler
when it compiles the call. It generates code that calls printf()
with the specified arguments, then discards the result.

On the other hand, if you omit the #include, and instead try to
declare printf yourself as a void function, then the behavior
is undefined:

extern void printf(const char *format, ...);

int main(void)
{
printf("Hello, World!\n");
return 0;
}

And if you omit the declaration altogether, then a C90 compiler
will implicitly assume that the unknown function "printf" returns
int -- but it will also assume that it takes a fixed number of
arguments based on the call, whereas printf is actually variadic.
Since fixed-argument and variadic functions can use different calling
conventions, again, the behavior is undefined. In C99, calling a
function without a visible declaration is a constraint violation;
if the compiler chooses to generate an executable after issuing
a warning, again, the language doesn't define the behavior of the
resulting program. A sufficiently clever compiler might recognize
the name "printf" and warn you that you're doing something wrong,
but that's not required.

It's common for funtions returning int and functions returning void
to be "compatible" in practice. For example, an int function might
store its result in a specified CPU register; calling it as if it
were a void function merely ignores whatever is in that register.
But other calling schemes are possible.

Basically, if you lie to the compiler, all bets are off.
 
S

Shao Miller

So you are saying that the statement
printf( "Hello, World!\n" );
provokes undefined behavior, since I am calling it
as if it were a function returning void rather than
int n;
n = printf( "Hello, World!\n" );
where I am calling it as a function returning an int?

I don't think he's saying that.

I believe that the expression for the called function, 'printf' in this
instance, is the same regardless of what you are doing with the result
of the function call. And in your former case, I think that you simply
have a "void expression," which is evaluated.

The same as:

int n = 3;
n;

You might chop it up thusly:

[ [ [printf] [ [(] ["Hello, world!\n"] [)] ] ] [;] ]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

and:

[ [ [n] [=] [ [printf] [ [(] ["Hello, world!\n"] [)] ] ] ] [;] ]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The "outer" expression for the assignment of 'n' should not influence
the "inner" expression for the function call; that expression is a
sub-expression in its own right, if I understand correctly. :)
 
K

Keith Thompson

Shao Miller said:
I don't think he's saying that.

I believe that the expression for the called function, 'printf' in this
instance, is the same regardless of what you are doing with the result
of the function call. And in your former case, I think that you simply
have a "void expression," which is evaluated.
[...]

Not quite. A void expression would be an expression whose type is void.
There is no void expression in the statement
printf( "Hello, World!\n" );

[consulting the standard]

Hang on, maybe there is. C99 6.8.3 describes statement expressions (an
expression with a semicolon added to the end of it can be used as a
statement). The semantics are:

The expression in an expression statement is evaluated as a void
expression for its side effects.

Frankly, I find that wording a bit misleading. I would have said that
the expression is evaluated for its side effects, and any result is
discarded.

Given the current wording, I suppose one could argue that evaluating a
printf call "as a void expression" means calling printf *as if* it
returned void, and therefore the behavior is undefined. But I'm certain
that that's not the intent, since it would break most C programs.

Looks like another question for comp.std.c.
 
S

Shao Miller

On 5/9/2011 1:51 PM, Keith Thompson wrote:
....C99 6.8.3 describes statement expressions (an
expression with a semicolon added to the end of it can be used as a
statement). The semantics are:

The expression in an expression statement is evaluated as a void
expression for its side effects.

...

Given the current wording, I suppose one could argue that evaluating a
printf call "as a void expression" means calling printf *as if* it
returned void, and therefore the behavior is undefined. But I'm certain
that that's not the intent, since it would break most C programs.

Looks like another question for comp.std.c.

I'm certain of the same, but not so certain one could argue that
argument. Since the outermost expression is a function call, and that
expression has a result type, then I think 6.3.2.2p1 would allow for the
value to be discarded just as you suggest it ought.
 
M

Martin Ambuhl

So you are saying that the statement
printf( "Hello, World!\n" );
provokes undefined behavior, since I am calling it
as if it were a function returning void rather than
int n;
n = printf( "Hello, World!\n" );
where I am calling it as a function returning an int?

Please don't presume to tell others what they are saying. Your
assertion beginning with "So you are saying" is simply false. It is not
true that Keith was saying what follows, and for a simple reason: what
follows is completely false. You are _not_ calling it as a function
returning void; you are simply discarding the return value.
So, in addition to making false claims about what Keith wrote, you are
showing a lack of familiarity with elementary aspects of the C language.
 
S

Shao Miller

On 5/9/2011 1:51 PM, Keith Thompson wrote:
....C99 6.8.3 describes statement expressions (an
expression with a semicolon added to the end of it can be used as a
statement). The semantics are:

The expression in an expression statement is evaluated as a void
expression for its side effects.

...

Given the current wording, I suppose one could argue that evaluating a
printf call "as a void expression" means calling printf *as if* it
returned void, and therefore the behavior is undefined. But I'm certain
that that's not the intent, since it would break most C programs.

Looks like another question for comp.std.c.

I'm certain of the same, but not so certain one could argue that
argument. Since the outermost expression is a function call, and that
expression has a result type, then I think 6.3.2.2p1 would allow for the
value to be discarded just as you suggest it ought.
 
K

Keith Thompson

Martin Ambuhl said:
Please don't presume to tell others what they are saying. Your
assertion beginning with "So you are saying" is simply false. It is not
true that Keith was saying what follows, and for a simple reason: what
follows is completely false. You are _not_ calling it as a function
returning void; you are simply discarding the return value.
So, in addition to making false claims about what Keith wrote, you are
showing a lack of familiarity with elementary aspects of the C language.

Given the wording of C99 6.8.3p2, which says that the call "is
evaluated as a void expression", it's not an unreasonable question.
(And yes, it was a question, not an assertion; note the question
mark at the end. "are you" might have been better than "you are",
but I'm not going to worry about it.)

Fred, was your question based on a (mis)reading of C99 6.8.3p2?
I'm arguing in comp.std.c that it should be re-worded; showing that
someone was actually led astray by the current wording would help
my argument.
 
F

Fred

Given the wording of C99 6.8.3p2, which says that the call "is
evaluated as a void expression", it's not an unreasonable question.
(And yes, it was a question, not an assertion; note the question
mark at the end.  "are you" might have been better than "you are",
but I'm not going to worry about it.)

Fred, was your question based on a (mis)reading of C99 6.8.3p2?
I'm arguing in comp.std.c that it should be re-worded; showing that
someone was actually led astray by the current wording would help
my argument.

It all goes back to Shao Miller's first response to the OP, where he
lists two scenarios - one recompiling with new headers, and one just
linking to the new libraries without recompiling.

The former is fine, the latter is not.

But in the earlier responses no one explained to the OP the difference
between calling a void function and calling a non-void function and
discarding the results.

Also, some programmers may not understand what is meant by "discarding
the result"; i.e., that
printf( ...);
discards the result of printf, whereas there is nothing to discard
from a call to a void function, such as
qsort(...);
 
J

James Kuyper

On 05/09/2011 02:14 PM, Fred wrote:
....
So you are saying that the statement
printf( "Hello, World!\n" );
provokes undefined behavior, since I am calling it
as if it were a function returning void rather than
int n;
n = printf( "Hello, World!\n" );
where I am calling it as a function returning an int?

The expression

printf("Hello, World!\n")

results in a value of type int. The expression

n = printf( "Hello, World!\n" )

also results in a value of type int. If the type of the first expression
would render the behavior of the corresponding expression statement
undefined, why wouldn't that also be true of the second expression,
which has the same type?
 
K

Keith Thompson

And it turns out that the phrase "evaluated as a void expression" is
defined by the standard (C99 6.3.2.2p1), so my objection to the current
wording of 6.8.3p2 is much weaker than I thought it was.
It all goes back to Shao Miller's first response to the OP, where he
lists two scenarios - one recompiling with new headers, and one just
linking to the new libraries without recompiling.

The former is fine, the latter is not.

The compiler needs to have a consistent view of how a function is
defined/declared. Suppose you have a function defined in foo.c, and
declared in foo.h, as returning void; bar.c has a #include "foo.h" and
calls the function. If you change both foo.c and foo.h so the function
returns int, and recompile everything, you're fine. If you change foo.h
and then recompile bar.c *without* recompiling the function definition,
you're lying to the compiler, and the behavior is undefined.
But in the earlier responses no one explained to the OP the difference
between calling a void function and calling a non-void function and
discarding the results.

Also, some programmers may not understand what is meant by "discarding
the result"; i.e., that
printf( ...);
discards the result of printf, whereas there is nothing to discard
from a call to a void function, such as
qsort(...);

Right. A better way to phrase it might be "discarding the result,
if any".
 
B

Borophyll

Please don't presume to tell others what they are saying.  Your
assertion beginning with "So you are saying" is simply false. It is not
true that Keith was saying what follows, and for a simple reason:  what
follows is completely false.  You are _not_ calling it as a function
returning void; you are simply discarding the return value.
So, in addition to making false claims about what Keith wrote, you are
showing a lack of familiarity with elementary aspects of the C language.

No one is making any assertions. Don't be confused by interrogative
sentences that appear to have a declarative syntax. (Hint: the
question mark at the end gives it away...)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top