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).
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;
}
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.
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;
};
Is there an established pattern or idiom for this concept? If not are there any recommendations for implementing it?
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With