19

I have a class that looks like this

struct A {
     double a1;
     int b1;
     double a2;
     int b2;
};

I have to read off of a file values for a1, b1, a2, and b2. Most of the time all four numbers are on the file, but sometimes there are only two numbers.

When there are two numbers, I want to store the values in a1, and b1 and I want to store "nothing" in a2 and b2. If a2 and b2 were pointers, I could just assign them to be nullptr, but they are not pointers.

Is there something I can store in double and int variables to indicate that 'nothing' is stored?

I know Boost.Optional is available, but I'm trying to avoid that library.

jlconlin
  • 14,206
  • 22
  • 72
  • 105
  • Pick a value you know your variables will never take and name it `NULL`. Then compare the variables to `NULL`. First choices are often 0 or -1 – scohe001 Aug 28 '14 at 17:52
  • 2
    That's the trouble, I don't know values my variables will never have. – jlconlin Aug 28 '14 at 17:53
  • 2
    @Josh: No, don't name it `NULL`. Give it a unique name that doesn't conflict with the standard library. – Keith Thompson Aug 28 '14 at 17:56
  • If you really don't want to use the boost library for some reason, it's quite simple to write your own `optional` for simple types - just wrap the value and a `bool` in a little class with a suitable interface. If you want to support non-trivial types (in particular, types that can't be default-constructed), then using a library will save you a world of pain. – Mike Seymour Aug 28 '14 at 17:57
  • 4
    For floating-point, you might be able to use `NaN`, but as far as I know support for floating-point `NaN` is not universal. For type `int`, `INT_MIN` *might* be reasonable, but you said you don't know of any values that your variables will never have. Unlike for pointers, there is no distinct "nulL" value for integers. Given that, your only choice is to store some extra information indicating whether a meaningful value is stored or not. Whether you do that with `Boost.Optional`, with your own class similar to `Boost.Optional`, or just by adding `bool` members is up to you. – Keith Thompson Aug 28 '14 at 17:59

8 Answers8

14

You could assign NAN to the double a2, which would also indicate that the int b2 is invalid.

This page for NAN usage.

Community
  • 1
  • 1
jonas25007
  • 231
  • 1
  • 3
  • 2
    That seems like a reasonable solution. What is different between `std::numeric_limits::quiet_NaN()` and `std::nan("1")`? – jlconlin Aug 28 '14 at 18:17
  • 1
    I think quiet_NaN() is implementation-specific, std::nan("1") produces a specific set of bits. In terms of the information you've provided, it shouldn't matter because you should use is_nan() to check for NaN anyway. – jonas25007 Aug 28 '14 at 18:29
  • Just a warning, you want to use "quiet" NaNs, otherwise setting it to NaN could raise a floating point exception. If you don't know if you have quiet NaNs or not, then you should not use NaNs. – Mark Lakata Jun 22 '16 at 18:29
6

You cannot. I can think of two alternative ways:

  1. use int *; or
  2. Use a value that for sure invalid in your context. For example, if it can never be negative, then use -1 to indicate null. But I still prefer the first way, since its correctness is not depended on requirement or context.
Peter Pei Guo
  • 7,770
  • 18
  • 35
  • 54
  • 2
    @matsjoyce: Introducing dynamic allocation where you don't otherwise need it is probably a bad idea, at least for performance. – Mike Seymour Aug 28 '14 at 17:59
  • True, but to use `int*`, you would have to maintain a list of all the numbers used, to keep alive the pointer. – matsjoyce Aug 28 '14 at 18:01
4

Either you have a value that's not legal or you don't. If you have a value that's not legal (like -1), use it as a sentinel. If not, then no. Use Boost.Optional or roll your own "value plus boolean" class.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • If you use an illegal value as sentinel, you should add an assert that checks if any value provided was legal. – Walter Aug 28 '14 at 18:30
2

Looks like you are going to have problems further down the road. The need to know how many values are valid will be sprinkled through the code base.

I suggest having a factory and base class. Essentially, you will have at least two classes:

struct two_values
{
  double a1;
  int    b1;
};

struct four_values : public two_values
{
  double a2;
  int    b2;
};

When a function explicitly requires the four values, use the four_values structure in the declaration. Otherwise use the two_values structure in function declaration.

This relationship states that a four_values instance can be used in any function requiring a two_values structure.

Alternative
An alternative is to use std::vector for your items:

struct Container
{
  std::vector<double> a_values;
  std::vector<int>    b_values;
};

A benefit with this design is that the vectors can tell you how many items there are and the concept is expandable, in case you need 6 items.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • While the original question was about "nullable" floats and ints, I think this response is the best advice. You can probably bake the handling of 2 or 4 values into the object model and make it clean. If you allow nullable values, now you open yourself up to 16 possible combinations (2**4) when in reality, you only want 2 possible combinations. What do you do if `a1` is nulled, or `a2` is nulled but not `b2`? – Mark Lakata Jun 22 '16 at 18:34
1
  1. You can pick a value that cannot be in the text files (an illegal value), such as 0, -1, std::numeric_limits<int>::max(). When you process the data, only use the value if it does not equal the illegal value (or sentinel).
  2. Include a bool indicating how many values there are:

    struct A {
        double a1;
        int b1;
        double a2;
        int b2;
        bool has_4_nums;
    };
    
  3. Use a pointer (int* or std::unique_ptr<int> as per @Peter Pei Guo), and assign nullptr when they do not exist.

matsjoyce
  • 5,744
  • 6
  • 31
  • 38
0

You have a two options to avoid boost::optional and hence the dependency to Boost:

  • Use the compiler's std::experimental::optional, which is available from GCC 4.9+ (and Clang in recent versions IIRC) with -std=c++14.

  • Use the "reference implementation" from Andrzej Krzemieński for std::experimental::optional from GitHub. It's header only, so you can just copy the header to your project (of course paying attention to the license)

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
0

Alternatively, you may add extra member to know if normal members have been affected, something like:

struct A {
     double a1;
     int b1;
     double a2;
     int b2;
     bool is_assigned[4];
};

or

struct A {
     double a1;
     int b1;
     double a2;
     int b2;
     double* p_a1; // point to a1 when initialized, else nullptr
     int* p_b1;    // point to b1 when initialized, else nullptr
     double* p_a2; // point to a2 when initialized, else nullptr
     int* p_b1;    // point to b2 when initialized, else nullptr
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
-1
struct A {
     double a1;
     int b1;
     double a2;
     int b2;

};

for this You can just use any type of variable to indicating whether two or four value are assigned to these variable.

Like take a variable name assigned = -1

So if Your input value are positive then if two values are inside file then just make

a2 = b2 = assigend

So when You are checking then just check whether the value of a2 and b2 are -1 or something else,

Robin Halder
  • 253
  • 2
  • 14