Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a null object?

Tags:

c++

Details

I found some information about the Null Object Pattern here (https://softwareengineering.stackexchange.com/questions/152094/null-pointers-vs-null-object-pattern) and here (http://en.wikipedia.org/wiki/Null_Object_pattern#C.2B.2B).

However, the C++ implementation doesn't illustrate my use case.

I also saw the related link for Nullable Type (http://en.wikipedia.org/wiki/Nullable_type).

Use Case

I have an object that is not part of a hierarchy and wouldn't normally be allocated on the heap. Furthermore, there isn't a convenient value that can be used as a sentinel to indicate null. Hopefully, the following code makes the use case clear.

class ContrivedType
{
public:
    ContrivedType() :
        mValue(0)
    {
        // Do nothing
    }

    bool operator==(const ContrivedType& other) const
    {
        return mValue == other.mValue;
    }

    void setValue(std::uint16_t value)
    {
        mValue = value;
    }

private:
    // All values in the range [0, 65535] are valid for use
    std::uint16_t mValue;
};

class Foo
{
public:
    const ContrivedType getValue() const
    {
        return mValue;
    }

    void setValue(const ContrivedType &value)
    {
        mValue = value;
    }

private:
    ContrivedType mValue;
};

int main()
{
    Foo f;

    if (f.getValue() == ContrivedType())
    {
        // Ambiguous case
        // -    Was this value explicitly set to be the same value
        //      as when it's default constructed
        // OR
        // -    Was the value never set
    }

    return 0;
}

Possible Solution 1

Force users of the ContrivedType that need to disambiguate between the default state and unset to use pointers and dynamically allocate the ContrivedType. Perhaps something like this?

class Foo
{
public:
    Foo() :
        mValue(nullptr)
    {
        // Do nothing
    }

    const ContrivedType* getValue() const
    {
        return mValue.get();
    }

    void setValue(const ContrivedType &value)
    {
        if (!mValue)
        {
            mValue.reset(new ContrivedType(value));
        }
        else
        {
            *mValue = value;
        }
    }

private:
    std::unique_ptr<ContrivedType> mValue;
};

Now it's very clear whether the ContrivedType was set or not.

Possible Solution 2

Update the implementation of the ContrivedType to support the concept of null.

class ContrivedType
{
public:
    ContrivedType() :
        mState(nullptr)
    {
        // Do nothing
    }

    explicit ContrivedType(std::uint16_t value) :
        mState(&mStorage)
    {
        mStorage.mValue = value;
    }

    bool isNull() const
    {
        return mState == nullptr;
    }

    bool operator==(const ContrivedType& other) const
    {
        if (!isNull())
        {
            return mStorage.mValue == other.mStorage.mValue;
        }
        else
        {
            return other.isNull();
        }
    }

    void setValue(std::uint16_t value)
    {
        mStorage.mValue = value;

        if (!mState)
        {
            mState = &mStorage;
        }
    }

private:
    struct State
    {
        // All values in the range [0, 65535] are valid for use
        std::uint16_t mValue;
    };

    State mStorage;

    // This will point to the storage when a value actually set
    State* mState;
};

Question

Is there an established pattern or idiom for this concept? If not are there any recommendations for implementing it?

Rationale

In the real code there are classes with 1 ore more members which are optional in some contexts. These classes are being serialized over a socket using a protocol that supports fields that are missing (i.e., the optional fields). Instead of wasting bytes serializing a default constructed object that wasn't explicitly set the serialization could skip over the optional fields. For example, an updateFoo(const Foo&) function. If only a subset of an existingFoo instance is being updated then only those fields need to be serialized.

Edit

It looks like std::experimental::optional (brought to my attention by @myaut) is what I would want to use but I don't have access to it.

For now I would need to use a solution that would work with Visual Studio 2013 (2015 is probably okay) and g++ 4.8.

like image 576
James Adkison Avatar asked Apr 05 '15 18:04

James Adkison


1 Answers

From this question (think to upvote it ;):

std::experimental::optional originates from the Boost.Optional library, and this implementation works well in Visual C++ 12.0 (though it differs a little). Reference single-header implementation, based on the N3793 proposal paper, can be found here.

The latest list of supported C++11/14/1z core and library features that are shipped with Visual Studio can be found from the Visual C++ Team blog, from this post in particular. A set of header files of the Standard Library implementation (and some extensions) from Microsoft can be viewed here.

I had it a taste recently, with a bit of effort for building it, I manage to use it and was happy with it. Hope it helps.

like image 100
YSC Avatar answered Oct 10 '22 16:10

YSC