views:

290

answers:

2

I'm creating a database access layer in native C++, and I'm looking at ways to support NULL values. Here is what I have so far:

class CNullValue
{
public:
    static CNullValue Null()
    {
        static CNullValue nv;

        return nv;
    }
};

template<class T>
class CNullableT
{
public:
    CNullableT(CNullValue &v) : m_Value(T()), m_IsNull(true)
    {
    }

    CNullableT(T value) : m_Value(value), m_IsNull(false)
    {
    }

    bool IsNull()
    {
        return m_IsNull;
    }

    T GetValue()
    {
        return m_Value;
    }

private:
    T m_Value;
    bool m_IsNull;
};

This is how I'll have to define functions:

void StoredProc(int i, CNullableT<int> j)
{
    ...connect to database
    ...if j.IsNull pass null to database etc
}

And I call it like this:

sp.StoredProc(1, 2);

or

sp.StoredProc(3, CNullValue::Null());

I was just wondering if there was a better way than this. In particular I don't like the singleton-like object of CNullValue with the statics. I'd prefer to just do

sp.StoredProc(3, CNullValue);

or something similar. How do others solve this problem?

A: 

Replace IsNull with HasValue and you've got the .NET Nullable type.

Of course.. this is C++. Why not just use a pointer to a "primitive" type?

Randolpho
Pointers have different copy semantics. If you copy a non-null pointer, then the copy refers to the same object. If you copy this CNullableT, the copy has its own instance of the value. In some situations you want one and in some the other, but that's an independent issue from whether you want the range of values to be "any T, or none". So using a pointer for an optional value brings some baggage.
Steve Jessop
I was just trying to make the interface cleaner for the majority of cases, so I can do StoredProcedcure(1, 2)instead of int p = 2; StoredProcedcure(1, Yes, the .NET Nullable type was used as inspiration :)
DanDan
@Steve Jessop: excellent point I hadn't considered.
Randolpho
I should also have added: in many cases you don't care one way or the other, because although you make a copy of the optional, nobody gets a chance to modify the actual value. If you're just writing the value to DB and returning, a pointer is fine for that reason. It's when you're storing it in your ORM object for a while first, that copying the value from a maybe-null-pointer is more code than you can be bothered with, and you want some kind of wrapper.
Steve Jessop
+10  A: 

Boost.Optional probably does what you need.

boost::none takes the place of your CNullValue::Null(). Since it's a value rather than a member function call, you can do using boost::none; if you like, for brevity. It has a conversion to bool instead of IsNull, and operator* instead of GetValue, so you'd do:

void writeToDB(boost::optional<int> optional_int) {
    if (optional_int) {
        pass *optional_int to database;
    } else {
        pass null to database;
    }
}

But what you've come up with is essentially the same design, I think.

Steve Jessop
Especially considering that it's equivalent to the embedded value performance-wise as they do not use heap-allocation.
Matthieu M.
Thank you, looking into this library now...
DanDan
Just tested it. It is perfect.
DanDan