Check for value definedness in C++
Asked Answered
B

9

7

I'm working in C++ and I need to know if a scalar value (for instance a double) is "defined" or not. I also need to be able to "undef" it if needed:

class Foo {
public:
    double get_bar();

private:
    double bar;
    void calculate_bar() {
        bar = something();
    }
};

double Foo::get_bar() {
    if ( undefined(bar) )
        calculate_bar();
    return bar;
}

Is it possible in C++?

Thanks

Bawl answered 23/1, 2009 at 15:11 Comment(0)
A
12

As the other answers says, C++ doesn't have this concept. You can easily work around it though.

Either you can have an undefined value which you initialize bar to in the constructor, typically -1.0 or something similar.

If you know that calculate_bar never returns negative values you can implement the undefined function as a check for < 0.0.

A more general solution is having a bool saying whether bar is defined yet that you initialized to false in the constructor and when you first set it you change it to true. boost::optional does this in an elegant templated way.

This is what the code example you have would look like.

class Foo {
public:
    double get_bar();
    Foo() : barDefined(false) {}
private:
    double bar;
    bool barDefined;
    void calculate_bar() {
        bar = something();
    }
};

double Foo::get_bar() {
    if ( barDefined == false ) {
        calculate_bar();
        barDefined = true;
    }
    return bar;
}
Adverb answered 23/1, 2009 at 15:17 Comment(7)
boost::optional is truly interestingMonotype
What does everyone have against good old -1. Can't get no respect I sayJosefjosefa
Robert Gould: it's a sentinel value. I'll leave it up to you to research why that's often considered a bad thing.Giverin
I think -1 is fine if your assumptions on calculate_bar holds so you never get a correct value that's the same as your undefined value. I also think it's good practice to do something like: const double UNDEFINED = -1.0. Also checking for equality on floating point types can be treacherous.Adverb
If you can specify a given value as never happening, you can use it as a sentinel value, although I'd rather name it as Laserallan says. A quick check of the Standard shows no guarantee that "double f(2.3); return f == 2.3;" will return true; anybody know of problems in that area?Mut
Should use Lazy initialization. Let the compiler do the work for you. Doing the work yourself is error prone (as it requires the programmer to think). Compiler will always do it correctly.Astonishment
For this example, your static based solution would be good, in a general situation where there can be many instances of Foo with different values for bar you need something more flexible. I think that there should at most one copy of the line in the "if(barDefined == false) {" in the class though :)Adverb
N
6

As others pointed out, there is nothing like an "undefined" state. But you may want to look into boost.optional

Nonsectarian answered 23/1, 2009 at 15:18 Comment(0)
A
3

If you mean at run-time, there is no such thing. If bar is never initialized, it will have whatever random bits happen to be there, depending on how the object is allocated (some allocators will initialize new memory to all-zero).

edit: it's up to the programmer to handle object state in constructors and/or manual initialization methods like init()

Axum answered 23/1, 2009 at 15:13 Comment(0)
M
2

C++ does not have an "undefined" state for primitive types. The closest available for float/double would be NAN, but that really has a different meaning.

Met answered 23/1, 2009 at 15:14 Comment(2)
I've used a quiet NaN (qNaN) for this purpose myself: en.wikipedia.org/wiki/NaN#Quiet_NaNs. The IEEE encoding leaves lots of undefined bits in a NaN, allowing you to distinguish between NaN as a calculation result versus an explicitly assigned value.Unholy
i can't understant why you are the only one to talk about it..Ancel
R
2

Why not maintain a separate flag that gets initialized to false and then gets set to true when bar is calculated. It can then be 'undefed' by setting the flag to false again.

if(!isBarValid)
{
    calculateBar();
    isBarValid = true;
}
return bar;
Rocky answered 23/1, 2009 at 15:17 Comment(0)
B
1

This is not possible in C/C++, primitives will always a value assigned (mostly garbage, whatever was on that spot in memory before it, unless explicitly assigned at declaration). I's common to have a placeholder value (i.e. 0 for pointers) which denotes not-used, however these have to be explicitly assigned as well. If your double can take on any value, then I suggest you put a boolean next to it, assigned to false initially, and test/set that one when you want to do your calculation.

Brooklime answered 23/1, 2009 at 15:15 Comment(0)
H
0

Initialize bar to some value which can never occur when you call the something() function in the constructor.

For example:

Foo(): bar(-1)
{
}

Then check for the value -1 in the get_bar function.

(hmmm Laserallan also posted that answer 1 minute before :-( ;-) )

Healion answered 23/1, 2009 at 15:17 Comment(1)
Unfortunately my double can be any number.Bawl
T
0

You must do it by using an extra boolean.

To implement using an extra boolean, you could try logic like the following template:

template<typename T>
struct Defined
{
 bool defined;
 T value;
 Defined() : defined(false) {}
 Defined(const T& value_) : defined(true), value(value_) {}
 ... and perhaps other operators here ...
 ... to make this behave even more like a T ...
};
Toenail answered 23/1, 2009 at 15:19 Comment(2)
This is actually what boost::optional if you have access to boost in your program. Note that I learned about it in this thread :) #413111Adverb
It's how C#'s Nullable<T> template is implemented too.Toenail
R
0

You could try the Construct on first use idiom and write get_bar() this way:

double & get_bar()
{
    static double *bar = new double(something());
    return *bar;
}

When you call get_bar() it will make bar for you if no one has asked for it yet. Any subsequent calls will just return bar. As the linked page says, this doesn't technically leak memory because the OS will reclaim it when the program exits.

UPDATE:

Changed the return value to double & to allow you to modify bar.

Roaring answered 23/1, 2009 at 15:28 Comment(2)
I've seen & used the technique before -- but why allocate memory, why not just "static double bar"? (even for classes)Axum
The next FAQ in the list answers that very question. :) parashift.com/c++-faq-lite/ctors.html#faq-10.14Roaring

© 2022 - 2024 — McMap. All rights reserved.