Optional?
Agenda• Type traits intro• Interface & usage• Caveats
Why?• Uninitialized variables cause lots of bugs• Removes much need for 2-stage initialization of objects• A cheap 0 or 1 element container• Internal storage. Much preferable over scoped_ptr + heap
allocation• Much less bloat & faster compilation than boost::optional
Type traits are meta functions!
is_integer<int>::value => trueis_floating_point<int>::value => falsealignment_of<Vec3>::value => 128remove_reference<T&>::type => Tis_same<int, float>::value => falseis_convertible<int, uint>::value => true
• Very useful when writing generic code
Implemented with template specialization// Defaulttemplate<class T>struct is_floating_point{ enum { value = false; }};
template<>struct is_floating_point<float>{ enum { value = true; }};
template<>struct is_floating_point<double>{ enum { value = true; }};
Useful for static dispatch, among other things..template<class T>T my_cool_algorithm(T t){ return my_cool_algorithm_impl<T, is_floating_point<T>::value>(t);}
template<class T, bool>T my_cool_algorithm_impl(T t);
template<class T, true>T my_cool_algorithm_impl(T t){ // int optimized version}
template<class T, false>T my_cool_algorithm_impl(T t){ // float optimized version}
Optional interfacestruct Nothing {};
template <class T>class Optional {
Optional();Optional(Nothing); // Note implicitOptional(const T& value); // Note implicitOptional(const Optional<T>& copy);
Optional<T>& operator=(Nothing);Optional<T>& operator=(const T& value);Optional<T>& operator=(const Optional<T>& rhs);
bool isSet() const;
/// @pre isSet()T& get(); const T& get() const;
};
// Override placement new to allow constructing elements without copyingtemplate<class T> inline void* operator new(size_t, fb::Optional<T>& optional);
// Convenience functiontemplate<class T>const T& getValueOr(const Optional<T>& optional, const T& defaultValue) const; template<class T>T& getValueOr(Optional<T>& optional, T& defaultValue) const;
Basic usage
Optional<int> i;ASSERT(!i.isSet());
i = 23;ASSERT(i.isSet() && i.get()==23);
i = Nothing();ASSERT(!i.isSet());
Return from functions
// C stylebool sqrt(double n, double& result); Optional<double> sqrt(double n);
• Client code won’t forget to check the bool• All-or-nothing guarantee, can’t break output variables
Member variables not always validclass X { Optional<uint> m_resultCache;};
• Clearer than using -1 or other "invalid values“
Single item container
class X { Optional<Weapon> m_activeWeapon;};
• Does not require DefaultConstructible objects• No heap allocations! Uses placement new internally• Useful to store RAII objects
Expressive interface
class X { void setLookForEnemyAroundPosition( const Optional<Vec3>& position);
const Optional<Vec3>& getLookForEnemyAroundPosition();};
Simplifying code
class Target { // Remove, use optional instead! bool isValid() const;};
void foo(Optional<Target> t);void bar(Target t);void baz(Target t);
• Often little code that need complexity of invalid objects• Helps maintenance – expressive client code
Caveats in current implementation• Aligned types double in size• No implicit bool conversion• Unnecessary branch for some types
Most of them can be fixed simply
Aligned types double in size
sizeof(Optional<Vec3>) == 2 * sizeof(Vec3)
For Vec3, the pad data can be used
No implicit bool conversion• Missing safe implicit type conversion to bool if (Optional<double> xRoot = sqrt(x))
Unnecessary branch for some types
~Optional() {if (!HasTrivialDestructor<T>::Value &&
m_initialized) m_storage.destruct();
}
Will get C++0x features in all of our compilers soonUntil then types can be registered as having a trivial, no-
op destructor
Further reading
Unit tests in OptionalTest.cpp
Static tests in HasTrivialDestructor.cpp, AlignedStorage.cpp and Optional.cpp
Design rationale for boost.optionalhttp://www.boost.org/doc/libs/1_38_0/libs/optional/doc/html/boost_optional/development.html
Nullable i C# http://msdn.microsoft.com/en-us/library/1t3y8s4s.aspx