Allowing function to return double and/or NULL 
Author Message
 Allowing function to return double and/or NULL

[This is really a C language issue, not a C++ one, and it's
certainly not a Linux one.  I've set followups appropriately.]



| double foo() {
|       ...
|       if (something)
|               return NULL;    
|       ...
| }
|
| The IRIX CC compiler allows this wheras gcc/g++ does not.
| I NEED TO BE ABLE TO DO THIS ... or else rewrite major parts
| of the code.

SUMMARY
=======

I'm afraid you're in for a rewrite, though it can probably be
arranged to be a fairly minor one.  You shouldn't be able to do what
you tried above in C, or (even more so) in C++.  You only got away with
it on Irix via a C typing loophole (which C++ inherits), and there may
well have been a {*filter*} bug hidden there in your Irix version.

You need to find some other way to communicate the
case-that-used-to-return-NULL information back to foo()'s callers.
For example, you might try any one of the following:
- Set a flag somewhere.
- Return a structure containing a double-precision floating point
  number and a flag.
- Return a special (double-precision floating point) value which
  foo()'s callers can recognize as being distinct from normal values.
The last of these is probably the easiest and cleanest route.

DETAILS
=======

The basic concept of a "null pointer" is that it's a special value,
sort of a pointer, but distinct from all non-null *pointers*.  But
there's no guarantee at all (and it typically won't be true) that
whatever bit pattern a compiler uses to represent a null pointer,
will be distinct from all floating point numbers, and C (and C++)
has (have) no concept of a "null floating point number".

When you write  double foo()  you're asserting that  foo() is a
function which returns a  double  .  C is a fairly strongly typed
language, and C++ strengthens that strong typing.  So any   return X
statement within  foo()  actually means  "convert the value of X to
the type double  and return that (converted) value using the compiler's
protocol for returning values of type   double  ".  On most computers
there's no good (reasonable) way to convert pointers to double-precision
floating point numbers.  So yes, the compiler is right to squalk, just
as it would if you tried to return (say) a character string from foo().

"But it worked on Irix", you say.  Well, only through a loophole,
and there may well have been a {*filter*} bug hidden there.  You see, for
historical reasons C doesn't have a special way to represent a null
pointer in source code, instead it treats the _integer_constant_0_
as a null pointer.

Many programmers (or header files), and I infer Irix, say
        #define NULL    0
(which is perfectly legitimate) and proceed to use NULL in their
source code.  So after it got through the preprocessor, your example
code above actually looked like
| double foo() {
|       ...
|       if (something)
|               return 0;
|       ...
| }
In other words, you were just returning the double-precision floating
point value 0.0.  If your function can never "normally" return 0.0,
then there's no problem.  But if your application is such that your
function can "normally" (without having  something  true) return 0.0,
then your Irix version has a {*filter*} -- any time the function "normally"
returns 0.0, its callers will see that
        if (foo() == NULL)
is true, i.e. they'll think that  something  is true, even though it
really isn't.

Why didn't your code work under Linux gcc/g++?  Well, many programmers
(or header files) and I infer Linux gcc/g++, instead define NULL as
        #define NULL    ((void *) 0)
Why?  Because C permits this definition *or* the plain-integer-zero
definition (or any number of other definitions, so long as one is used
consistently throughout the system), and the void-star-zero definition
catches your bug and many (though not all) similar ones.

        [To be precise, C has no concept of NULL, only
        that of "a null pointer".  NULL is just a common
        macro defined as a null pointer -- and there's
        more than one valid way to define such a critter.]

        [Section 5 of the comp.lang.C FAQ discusses null
        pointers in some detail (and very clearly, too).
        It's well worth reading.]

HOW TO FIX IT
=============

Ok, how to fix things up?  Well, presumably what you're trying to do
in foo() is to, depending on some computations in foo(), return either
a double-precision floating point number *or* a special distinguished
value.  That's reasonable enough, you just picked something (NULL)
which won't work as a "special distinguished value" when it's lumped
together with arbitrary double-precision floating point numbers.

I outlined several possible ways to do this earlier.  Basically, the
easiest way is to pick some special double-precision floating point
number that foo() never "normally" (i.e. with  something  false) returns.
Depending on your application, this number might be 0.0, -1.0, 6.02e23,
-1.0e38, an IEEE-floating-point not-a-number, or whatever.  The exact
choice doesn't matter, so long as it's a value of type  double  (and
hence IEEE "not a number" values are ok) and so long as it's never
"normally" returned by foo().

Then just stick
        #define FOO_SPECIAL_RETURN_VALUE        -1.0e38 /* or whatever */
in a suitable header file, probably the same header file that contains
foo()'s ANSI-C prototype,
        [you do use prototypes, I trust; if not, START USING
        THEM RIGHT NOW, THEY CATCH A LOT OF BUGS (and they're
        required in C++)]
and then change foo() to
| double foo() {
|       ...
|       if (something)
|               return FOO_SPECIAL_RETURN_VALUE;
|       ...
| }
and change foo()'s callers from
        if (foo() == NULL)
                {
                /* hmm, looks like foo() returned a "special" value */
                ...
                }
to
        if (foo() == FOO_SPECIAL_RETURN_VALUE)
                {
                /* hmm, looks like foo() retrurned a "special" value */
                ...
                }

Since you've carefully chosen FOO_SPECIAL_RETURN_VALUE to be a
legitimate value of type  double  (which a null pointer isn't, which
NULL-defined-as-plain-integer-zero is-but-really-shouldn't-be, and
which NULL-defined-as-void-star-zero isn't), there's no problem here:
foo()'s callers can unambiguously detect FOO_SPECIAL_RETURN_VALUE
with no danger of confusing it with "normal"  double  values.

--


   "Washing one's hands of the conflict between the powerful and the powerless
    means to side with the powerful, not to be neutral." - Freire / OXFAM



Mon, 26 Jul 1999 03:00:00 GMT  
 Allowing function to return double and/or NULL

Jonathan Thornburg wrote a very nice response to Dave Topper's query
about the unportable code:

Quote:
> | double foo() {
> |  ...
> |  if (something)
> |          return NULL;    
> |  ...
> | }

But I have to pick a few nits in it.  Um, well, I'm *going* to, anyway.

Quote:
> The basic concept of a "null pointer" is that it's a special value,
> sort of a pointer, but distinct from all non-null *pointers*.  But
> there's no guarantee at all (and it typically won't be true) that
> whatever bit pattern a compiler uses to represent a null pointer,
> will be distinct from all floating point numbers, and C (and C++)
> has (have) no concept of a "null floating point number".

All perfectly correct.  However, let me also point out that at no point
in the article was there any code that dealt with the *bit pattern* of
a null pointer, because...

Quote:
> ... for historical reasons C doesn't have a special way to represent a
> null pointer in source code, instead it treats the _integer_constant_0_
> as a null pointer.

C does have a special way to represent a null pointer unambiguously in
source code: ((void *) 0).  However, for historical reasons it *also*
treats the integer constant 0 as indicating a null pointer, when in a
context where a pointer is expected.  (In other contexts, of course, 0
represents an int.)  These expressions are "null pointer constants".
In both forms, the 0 may also be replaced by any constant expression of
integral type that evaluates to 0, like 0L or (1-1).

An expression of the form ((any_type_here *) 0) is another source code
representation of a null pointer, though in this expression only the 0,
not the whole expression, is a null pointer constant.

Quote:
>    [To be precise, C has no concept of NULL, only
>    that of "a null pointer".  NULL is just a common
>    macro defined as a null pointer -- and there's
>    more than one valid way to define such a critter.]

To be still more precise, NULL is not a keyword in C; it's a macro that
is guaranteed to be defined in <stddef.h>, <stdio.h>, and several other
headers, expanding to a null pointer constant.

For historical reasons, people sometimes speak as though C did not really
include anything to do with macros and other "preprocessing" features,
nor any library functions.  The standard has established that all these
things really are part of the language; hence, NULL is.

Quote:

>    [Section 5 of the comp.lang.C FAQ discusses null
>    pointers in some detail (and very clearly, too).
>    It's well worth reading.]

Absolutely.
--

SoftQuad Inc., Toronto        is always right"           -- Michael DeCorte

My text in this article is in the public domain.



Wed, 28 Jul 1999 03:00:00 GMT  
 Allowing function to return double and/or NULL


Quote:

>[ - a very nice description of why the old code doesn't (in general)
> work, and made several good suggestions for how to do right, but
> I'm going to pick on it a little bit]
> [BIG SNIP]
>I outlined several possible ways to do this earlier.  Basically, the
>easiest way is to pick some special double-precision floating point
>number that foo() never "normally" (i.e. with  something  false) returns.
>Depending on your application, this number might be 0.0, -1.0, 6.02e23,
>-1.0e38, an IEEE-floating-point not-a-number, or whatever.  The exact
>choice doesn't matter, so long as it's a value of type  double  (and
>hence IEEE "not a number" values are ok) and so long as it's never
>"normally" returned by foo().

>Then just stick
>    #define FOO_SPECIAL_RETURN_VALUE        -1.0e38 /* or whatever */
> [small snip, and slight edit]
>and change foo()'s callers to:
>    if (foo() == FOO_SPECIAL_RETURN_VALUE)
>            {
>            /* hmm, looks like foo() retrurned a "special" value */
>            ...
>            }

There's a few potential problems with this:

1. I seem to recall that IEEE math requires that NaN != NaN, so the
specified test won't work, if you use NaN as your special value. Most
implementations provide a function to test for NaN, but they all have
different names and slightly different semantics, so you'll probably
have to write a wrapper function or macro, with conditional compiles for
all the different implementation. Assuming, of course, that you need to
be able to run this under multiple implementations.

2. Equality compares of floating point numbers make me *real*
nervous. Careful choice of values might make this OK.

3. IMO&E, returning special values as error returns leads to long term
problems. The Standard C Library is full of functions where this causes
problems (or at least inconvenience). Sooner or later you want to be
able to distinguish different errors, or the special value turns out to
be not so special.

My recomendation: You're going to have to touch every place foo() is
called anyway; bite the bullet, add a separate error flag, and check
that.

Steve Greenland
--
The Mole - I think, therefore I scream

                        "His super power is to turn into a scotch terrier."
[Flaming Carrot]



Wed, 28 Jul 1999 03:00:00 GMT  
 Allowing function to return double and/or NULL

: C does have a special way to represent a null pointer unambiguously in
: source code: ((void *) 0).  However, for historical reasons it *also*

And of course this is not a special case because...

: treats the integer constant 0 as indicating a null pointer, when in a
: context where a pointer is expected.  (In other contexts, of course, 0

So the zero above, because it is casted, is in fact in a pointer context
and there is one rule and all is right with the world.

--
D'Arcy J.M. Cain                           |  Democracy is three wolves

+1 416 424 2871     (DoD#0082)    (eNTP)   |  what's for dinner.
                --  http://www.druid.net/darcy  --



Sun, 01 Aug 1999 03:00:00 GMT  
 
 [ 4 post ] 

 Relevant Pages 

1. Passing char* to function returning null

2. imported function returning struct or null pointer.

3. function is not returning a NULL

4. how to write a function that returns a string of N nulls

5. Problem returning doubles from a function

6. lint warning "function actually returns double"

7. Function declared float returns double ????

8. ItemHasChildren returns TRUE, then GetChildItem returns NULL

9. is strcpy(buf,NULL) allowed?

10. Pointer to function returning pointer to function returning...

11. NULL as a null function pointer

12. Allowed way of returning a string?

 

 
Powered by phpBB® Forum Software