Skip to content

Instantly share code, notes, and snippets.

@Redchards
Created November 13, 2016 17:01
Show Gist options
  • Select an option

  • Save Redchards/1aa9b75c1f307350eab7565465dd4bef to your computer and use it in GitHub Desktop.

Select an option

Save Redchards/1aa9b75c1f307350eab7565465dd4bef to your computer and use it in GitHub Desktop.
#pragma once
/* This code tries to follow in some instance the following specification for optional type :
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html
* Of course, it's also inspired by the reference TS implementation :
* https://github.com/akrzemi1/Optional
* This implementation do not aim to be the same quality, this is only a study exercise.
* Also based on the cppreference page : http://en.cppreference.com/w/cpp/experimental/optional
* It also does not bother with compatibility, and assumes a C++14 compiler. These omissions
* leads to a somewhat cleaner code in some place.
*
* In some place, the code do not follow the proposal.
* Notably, support for initializer list initialization in a natural way. To me, the only reason to
* disallow the in place construction to happen automatically is, as it's described in the proposal,
* that it might lead to surprising results along with the implicit boolean conversion.
* However, I can't see an example in which the initializer list could be problematic, and it's a nice
* syntactic sugar to have.
*
* Please, let me now if there's some misconception, bugs, or just to suggest some improvements :
* urien.loic.cours@gmail.com
*/
/* Visual Studio NOTE : As time of speaking, visual studio 2015 update 2 is absolutely incapable of handling constexpr properly
* Turns out, all the constexpr in the original code was so confusing, he wasn't accounting for things like ref qualifier anymore. Meh.
* The only solution is to delete all constexpr and wait for a new version :/
*/
#include <cstdint>
#include <stdexcept>
#include <type_traits>
template<class ...>
using void_t = void;
#if(COMPILER == MSVC_COMPILER)
#pragma warning(push)
#pragma warning(disable : 4814)
#endif
namespace
{
template<class T>
using is__default_constructible = std::integral_constant<bool, std::is_literal_type<T>::value && std::is_default_constructible<T>::value>;
// C++17 style alias.
template<class T>
static bool is__default_constructible_v = is__default_constructible<T>::value;
// Template metafunction used to modelise the "EqualityComparable" concept
// It assumes that the comparison is a well formed relation, that said, it is well founded, as it should be, and antisymetric.
template<class T1, class T2, class = void>
struct are_equal_comparable : std::false_type
{};
template<class T1, class T2>
struct are_equal_comparable<T1, T2, void_t<decltype(std::declval<T1>() == std::declval<T2>())>> : std::true_type
{};
// C++17 style alias
template<class T1, class T2>
static bool are_equal_comparable_v = are_equal_comparable<T1, T2>::value;
template<class T1, class T2, class = void>
struct are_less_than_comparable : std::false_type
{};
template<class T1, class T2>
struct are_less_than_comparable<T1, T2, void_t<decltype(std::declval<T1>() < std::declval<T2>())>> : std::true_type
{};
class optional_access_failure : public std::logic_error
{
using std::logic_error::logic_error;
};
// Required to check if a type as overloaded operator&.
// If it does, we can't just use the "&" method to get back the address.
template<class T, class = void>
struct has_overloaded_address_operator : std::false_type
{};
template<class T>
struct has_overloaded_address_operator<T, void_t<decltype(std::declval<T>().operator&())>> : std::true_type
{};
// C++17 style alias.
//template<class T>
//static bool has_overloaded_address_operator_v = has_overloaded_address_operator<T>::value;
template<class T1, class T2>
using are_equal_and_less_than_comparable = std::integral_constant<bool, are_equal_comparable<T1, T2>::value && are_less_than_comparable<T1, T2>::value>;
// C++17 style alias.
//template<class T1, class T2>
//static bool are_equal_and_less_thant_comparable_v = are_equal_and_less_than_comparable<T1, T2>::value;
// So here are the addressof functions we will be using.
// Greatly inspired by the original functions in the reference implementation.
template<class T, std::enable_if_t<!has_overloaded_address_operator<T>::value>* = nullptr>
T* static_addressof(T& ref)
{
return &ref;
}
template<class T, std::enable_if_t<has_overloaded_address_operator<T>::value>* = nullptr>
T* static_addressof(T& ref)
{
return std::addressof(ref);
}
}
struct nullopt_t
{
public:
nullopt_t() = default;
nullopt_t(const nullopt_t&) = default;
nullopt_t(nullopt_t&&) = default;
nullopt_t& operator=(const nullopt_t&) = delete;
nullopt_t& operator=(nullopt_t&&) = delete;
};
constexpr nullopt_t nullopt{};
struct in_place_t
{
public:
in_place_t() = default;
in_place_t(const in_place_t&) = default;
in_place_t(in_place_t&&) = default;
in_place_t& operator=(const in_place_t&) = delete;
in_place_t& operator=(in_place_t&&) = delete;
};
constexpr in_place_t in_place{};
// The "dummy_" field is here to make a false initialization when the optional is disengaged,
// because we don't want to initialize the true object.
// That's quite a mess, we don't want anybody to see that ...
namespace
{
template<class T, class = void>
union basic__storage_layout
{
private:
uint8_t dummy_;
public:
T value_;
public:
basic__storage_layout() noexcept
: dummy_{}
{}
basic__storage_layout(const T& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
: value_{ other }
{}
basic__storage_layout(T&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
: value_{ std::move(other) }
{}
~basic__storage_layout() {}
};
template<class T>
union basic__storage_layout<T, void_t<std::enable_if_t<std::is_trivially_destructible<T>::value>>>
{
private:
uint8_t dummy_;
public:
T value_;
public:
basic__storage_layout() noexcept
: dummy_{}
{}
basic__storage_layout(const T& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
: value_{ other }
{}
basic__storage_layout(T&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
: value_{ std::move(other) }
{}
~basic__storage_layout() = default;
};
// Now let's build our real storage class
/*template<class T>
using basic_storage_layout = std::conditional_t<!std::is_default_constructible<T>::value,
basic_storage_layout_no_default<T>,
std::conditional_t<std::is_literal_type<T>::value,
basic__storage_layout_default<T>,
basic_storage_layout_default<T>>>;*/
// Dummy alias.
template<class T>
using basic_storage_layout = basic__storage_layout<T>;
// All constructors marked explicit becaue we don't want them to be converting constructors.
// So indicating here that we do not want is is always good to prevent errors.
// This class is for the case where our T type is not a literal type.
// Thus, the storage can't be a literal type, and we don't need constructors.
// Moreover, calling a non constructor inside a constructor can actually
// lead to subtle error messages about "constructor which will never produce constant expression".
// TODO : Hide this in an anonymous namespace.
template<class T, class = void>
struct basic_storage
{
public:
bool initialized_;
basic_storage_layout<T> storage_;
public:
basic_storage() noexcept(std::is_nothrow_default_constructible<T>::value)
: initialized_{ false },
storage_{}
{}
explicit basic_storage(const T& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
: initialized_{ true },
storage_{ other }
{}
explicit basic_storage(T&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
: initialized_{ true },
storage_{ std::move(other) }
{}
template<class ... TArgs, std::enable_if_t<std::is_constructible<T, TArgs...>::value>* = nullptr>
explicit basic_storage(TArgs&&... args) noexcept(noexcept(T{ std::forward<TArgs>(args)... }))
: initialized_{ true },
storage_{ std::forward<TArgs>(args)... }
{}
template<class U, class ... TArgs, std::enable_if_t<std::is_constructible<T, std::initializer_list<U>, TArgs...>::value>* = nullptr>
explicit basic_storage(std::initializer_list<U> list, TArgs&&... args) noexcept(noexcept(T{ list, std::forward<TArgs>(args)... }))
: initialized_{ true },
storage_{ list, std::forward<TArgs>(args)... }
{}
basic_storage& operator=(const T& other) noexcept(std::is_nothrow_copy_assignable<T>::value)
{
if (!initialized_)
{
new(&storage_.value_) T(other);
initialized_ = true;
}
else
{
storage_.value_ = other;
}
return *this;
}
basic_storage& operator=(T&& other) noexcept(std::is_nothrow_move_assignable<T>::value)
{
if (!initialized_)
{
new(&storage_.value_) T(std::move(other));
initialized_ = true;
}
else
{
storage_.value_ = std::move(other);
}
return *this;
}
~basic_storage() noexcept
{
if (initialized_)
{
storage_.value_.~T();
}
}
};
// If our T type is a literal type, then we can use constructors.
template<class T>
struct basic_storage<T, void_t<std::enable_if_t<std::is_trivially_destructible<T>::value>>>
{
public:
bool initialized_;
basic_storage_layout<T> storage_;
public:
basic_storage() noexcept(std::is_nothrow_default_constructible<T>::value)
: initialized_{ false },
storage_{}
{}
explicit basic_storage(const T& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
: initialized_{ true },
storage_{ other }
{}
explicit basic_storage(T&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
: initialized_{ true },
storage_{ std::move(other) }
{}
template<class ... TArgs, std::enable_if_t<std::is_constructible<T, TArgs...>::value>* = nullptr>
explicit basic_storage(TArgs&&... args) noexcept(noexcept(T{ std::forward<TArgs>(args)... }))
: initialized_{ true },
storage_{ std::forward<TArgs>(args)... }
{}
template<class U, class ... TArgs, std::enable_if_t<std::is_constructible<T, std::initializer_list<U>, TArgs...>::value>* = nullptr>
explicit basic_storage(std::initializer_list<U> list, TArgs&&... args) noexcept(noexcept(T{ list, std::forward<TArgs>(args)... }))
: initialized_{ true },
storage_{ list, std::forward<TArgs>(args)... }
{}
basic_storage& operator=(const T& other) noexcept(std::is_nothrow_copy_assignable<T>::value)
{
if (!initialized_)
{
new(&storage_.value_) T(other);
initialized_ = true;
}
else
{
storage_.value_ = other;
}
return *this;
}
basic_storage& operator=(T&& other) noexcept(std::is_nothrow_move_assignable<T>::value)
{
if (!initialized_)
{
new(&storage_.value_) T(std::move(other));
initialized_ = true;
}
else
{
storage_.value_ = std::move(other);
}
return *this;
}
~basic_storage() noexcept = default;
};
}
template<class T>
class optional : basic_storage<T>
{
using Base = basic_storage<T>;
private:
static_assert(!std::is_same<nullopt_t, T>::value, "Optional type is ill formed, cannot use a null option type as underlying type !");
public:
optional() noexcept(std::is_nothrow_default_constructible<T>::value) = default;
optional(nullopt_t) noexcept {};
optional(const optional& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
: Base{}
{
if (other.initialized())
{
*static_cast<Base*>(this) = other.storage_.value_;
}
}
optional(optional&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
: Base{}
{
if (other.initialized())
{
*static_cast<Base*>(this) = std::move(other.storage_.value_);
}
other.clear();
}
optional(const T& value) noexcept(std::is_nothrow_copy_constructible<T>::value)
: Base(value)
{}
optional(T&& value) noexcept(std::is_nothrow_move_constructible<T>::value)
: Base(std::move(value))
{}
template<class ... TArgs>
explicit optional(in_place_t, TArgs&& ... args)
: Base(std::forward<TArgs>(args)...)
{}
template<class U, std::enable_if_t<std::is_constructible<T, std::initializer_list<U>>::value>* = nullptr>
optional(std::initializer_list<U> list) noexcept(noexcept(T{ list }))
: Base(list)
{}
template<class U, class ... TArgs, std::enable_if_t<std::is_constructible<T, std::initializer_list<U>, TArgs...>::value>* = nullptr>
optional(std::initializer_list<U> list, TArgs&&... args) noexcept(noexcept(T{ list, std::forward<TArgs>(args)... }))
: Base(list, std::forward<TArgs>(args)...)
{}
~optional() noexcept = default;
optional& operator=(const optional& other) noexcept(std::is_nothrow_copy_constructible<T>::value && std::is_nothrow_copy_assignable<T>::value)
{
optional tmp{ other };
this->swap(tmp);
return *this;
}
optional& operator=(optional&& other) noexcept(std::is_nothrow_move_assignable<T>::value)
{
this->swap(other);
return *this;
}
/* The two following constructors are using template to disable the overload mechanism.
* If we were to write for the nullopt something like this :
* operator& operator=(nullopt_t){...}
* A line like this one :
* optional<int> opt;
* opt = {};
* Would yield an ambiguous overload error, because of the overload taking optional<T> rvalue overload
*/
template<class U>
auto operator=(U) noexcept
-> std::enable_if_t<std::is_same<U, nullopt_t>::value, optional&>
{
clear();
return *this;
}
template<class U>
auto operator=(U&& value) noexcept(std::is_nothrow_copy_assignable<T>::value)
-> std::enable_if_t<std::is_same<std::decay_t<U>, T>::value, optional&>
{
*static_cast<Base*>(this) = std::forward<U>(value);
return *this;
}
template<class ... TArgs>
auto emplace(TArgs&& ... args) noexcept(std::is_nothrow_copy_constructible<T>::value)
-> std::enable_if_t<std::is_constructible<T, TArgs...>::value, void>
{
new(&Base::storage_.value_) T{ std::forward<TArgs>(args)... };
}
template<class AltRetType>
auto value_or(AltRetType&& default_value) noexcept
-> std::enable_if_t<std::is_convertible<AltRetType, T>::value, T>
{
return initialized() ? Base::storage_.value_ : static_cast<T>(default_value);
}
operator bool() const noexcept
{
return initialized();
}
// Should think about how to make in place construction.
// Converting constructor is out of question !
// Noexcept because the destructor should not throw any exception anyway.
void clear() noexcept
{
if (initialized())
{
Base::storage_.value_.T::~T();
}
Base::initialized_ = false;
}
bool initialized() const noexcept
{
return Base::initialized_;
}
/* Initially, the noexcept specification was the following :
* noexcept(std::is_nothrow_copy_assignable<T>::value && std::is_nothrow_move_assignable<T>::value)
* but a swap function should never throw anyway, or algorithms would be compromised.
* So, we make the maybe false promise here, that it will not throw, and check by a static assert
*/
void swap(optional<T>& other) noexcept
{
// Do we really want that ? However, if the type swap is throwing, the std algorithms could fail,
// so it might be wise anyway.
//static_assert(std::is_nothrow_copy_assignable<T>::value && std::is_nothrow_move_assignable<T>::value,
// "Throwing swap function !");
if (!initialized() && other.initialized())
{
*static_cast<Base*>(this) = other.storage_.value_;
other.clear();
}
else if (initialized() && !other.initialized())
{
static_cast<Base&>(other) = std::move(this->storage_.value_);
clear();
}
else if (initialized() && other.initialized())
{
// Enabling correct ADL.
using std::swap;
swap(Base::storage_.value_, other.storage_.value_);
}
}
void ensure_initialized() const
{
if (!initialized())
{
throw optional_access_failure("Accessed uninitialized optional value");
}
}
T& value()
{
ensure_initialized();
return Base::storage_.value_;
}
const T& value() const
{
ensure_initialized();
return Base::storage_.value_;
}
// here ?!? Ref qualifier say "rvalue reference", and literal type cannot be moved
// Some use for this additional notation then ?
/* T&& value() &&
{
ensure_initialized();
return std::move(Base::storage_.value_);
*/
/* const T* operator->() const
{
ensure_initialized();
return static_addressof(Base::storage_.value_);
}*/
T* operator->()
{
ensure_initialized();
return static_addressof(Base::storage_.value_);
}
const T& operator*() const& noexcept
{
return value();
}
T& operator*() & noexcept
{
return value();
}
T&& operator*() && noexcept
{
return std::move(value());
}
};
template<class T>
class optional<T&>
{
static_assert(!std::is_same<T, nullopt_t>::value, "Optional type is ill formed, cannot use a null option type as underlying type !");
private:
// Naked pointer to simulate the reference, but allowing modification.
T* ref;
public:
optional() noexcept
: ref{ nullptr }
{}
optional(nullopt_t) noexcept
: ref{ nullptr }
{}
optional(T&& other) noexcept = delete;
optional(const optional& other) noexcept
: ref{ other.ref }
{}
optional(T& other) noexcept
: ref{ static_addressof(other) }
{}
~optional() noexcept = default;
template<class U>
auto operator=(U) noexcept
-> std::enable_if_t<std::is_same<U, nullopt_t>::value, optional&>
{
ref = nullptr;
return *this;
}
template<class U>
auto operator=(U&& refVal) noexcept
-> std::enable_if_t<std::is_same<std::decay_t<U>, T>::value, optional&>
{
ref = static_addressof(refVal);
return *this;
}
optional& operator=(const optional& other) noexcept
{
ref = other.ref;
return *this;
}
void emplace(T& refVal) noexcept
{
ref = static_addressof(refVal);
}
T& value()
{
if (initialized())
{
return *ref;
}
throw optional_access_failure("Accessed uninitialized optional value");
}
const T& value() const
{
return value();
}
const T* operator->() const noexcept
{
return ref;
}
T* operator->() noexcept
{
return ref;
}
const T& operator*() const
{
return value();
}
T& operator*()
{
return value();
}
void clear() noexcept
{
ref = nullptr;
}
bool initialized() noexcept
{
return ref != nullptr;
}
operator bool() noexcept
{
return initialized();
}
void swap(optional& other) noexcept
{
using std::swap;
swap(ref, other.ref);
}
template<class AltRetType>
auto value_or(AltRetType& default_value) noexcept
-> std::enable_if_t<std::is_same<AltRetType&, T&>::value, T&>
{
return initialized() ? value() : static_cast<T&>(default_value);
}
};
template<class T>
void swap(optional<T>& lhs, optional<T>& rhs)
{
lhs.swap(rhs);
}
/* This function is a bit contrived due to the one line.
* As the goal of this implementation is to be clear, here is the version with classical if statements
*
* if(bool{lhs} == bool{rhs})
* {
* if(bool{lhs}) return lhs == rhs;
* else return true;
* }
* else
* {
* return false;
* }
*/
// Concept : EqualityComparable
template<class T1, class T2, std::enable_if_t<are_less_than_comparable<T1, T2>::value>* = nullptr>
bool operator==(const optional<T1>& lhs, const optional<T2>& rhs)
{
return (bool{ lhs } != bool{ rhs }) ? false : (bool{ lhs } ? *lhs == *rhs : true);
}
template<class T>
bool operator==(const optional<T>& lhs, nullopt_t)
{
return !bool{ lhs };
}
template<class T>
bool operator==(nullopt_t, const optional<T>& rhs)
{
return !bool{ rhs };
}
template<class T1, class T2>
bool operator!=(const optional<T1>& lhs, const optional<T2>& rhs)
{
return !(lhs == rhs);
}
template<class T>
bool operator!=(const optional<T>& lhs, nullopt_t)
{
return !(lhs == nullopt);
}
template<class T>
bool operator!=(nullopt_t, const optional<T>& rhs)
{
return !(rhs == nullopt);
}
// Concept : LessThanComparable
template<class T1, class T2, std::enable_if_t<are_less_than_comparable<T1, T2>::value>* = nullptr>
bool operator<(const optional<T1>& lhs, const optional<T2>& rhs)
{
return !bool{ rhs } ? false : (!bool{ lhs } ? true : *lhs < *rhs);
}
template<class T>
bool operator<(const optional<T>&, nullopt_t)
{
return false;
}
template<class T>
bool operator<(nullopt_t, const optional<T>& rhs)
{
return bool{ rhs };
}
template<class T1, class T2>
bool operator>(const optional<T1>& lhs, const optional<T2>& rhs)
{
return rhs < lhs;
}
template<class T>
bool operator>(const optional<T>& lhs, nullopt_t)
{
return nullopt < lhs;
}
template<class T>
bool operator>(nullopt_t, const optional<T>& rhs)
{
return rhs < nullopt;
}
template<class T1, class T2>
bool operator<=(const optional<T1>& lhs, const optional<T2>& rhs)
{
return !(lhs > rhs);
}
template<class T>
bool operator<=(const optional<T>& lhs, nullopt_t)
{
return lhs == nullopt;
}
template<class T>
bool operator<=(nullopt_t, const optional<T>& rhs)
{
return true;
}
template<class T1, class T2>
bool operator>=(const optional<T1>& lhs, const optional<T2>& rhs)
{
return !(lhs < rhs);
}
template<class T>
bool operator>=(const optional<T>& lhs, nullopt_t)
{
return nullopt <= lhs;
}
template<class T>
bool operator>=(nullopt_t, const optional<T>& rhs)
{
return rhs <= nullopt;
}
template<class T>
optional<std::decay_t<T>> make_optional(T&& value)
{
return{ std::forward<T>(value) };
}
#if(COMPILER == MSVC_COMPILER)
#pragma warning(pop)
#endif
#pragma once
#include <Optional.hxx>
/*
* Simple class, mimicking the behaviour of a unique_ptr, generalizing it to allow new resources to be handled.
* No shared resources however, because of the fact that the implementation is actually quite involved.
* We don't support construction or assignement by handle to assure unicity, at least in a way (possible to break it with get() method).
* The interface is thus a bit less flexible, but a bit more fool proof too. Maybe the missing features will be added later.
* Here, we use optional, because our handle can be anything, from a simple integer to a more complexe structure. However, I do not want to force
* the user to define a "null handle", as it can be quite complicated, unnatural, or plain impossible.
* There is also no dereference operators, as handles do not directly point to any data in many API, and can be used to access data in multiple ways.
*
* The ressource management strategy must be implemented in a similar fashion as shown below :
* class MyStrategy
* {
* public:
* using HandleType = ...;
* static HandleType construct(...) noexcept { ... }
* // WARNING : If the optional is disengaged, the destroy should do nothing, and by all mean not throwing !!!
* static void destroy(optional<HandleType>) noexcept { ... }
* };
*/
template<class RessourceManagementStrategy>
class RessourceHandler
{
public:
using RawHandleType = typename RessourceManagementStrategy::HandleType;
using ConstructorType = decltype(RessourceManagementStrategy::construct);
using DestructorType = decltype(RessourceManagementStrategy::destroy);
public:
RessourceHandler() = default;
RessourceHandler(RessourceHandler&& other) noexcept
{
swap(other);
other.release();
}
RessourceHandler(RawHandleType handle) noexcept
{
internalHandle_ = handle;
}
RessourceHandler(const RessourceHandler&) = delete;
~RessourceHandler() noexcept
{
reset({});
}
RessourceHandler& operator=(RessourceHandler&& other) noexcept
{
swap(other);
other.release();
return *this;
}
RessourceHandler& operator=(optional<RawHandleType> handle) noexcept
{
reset(handle);
return *this;
}
RessourceHandler& operator=(const RessourceHandler&) noexcept = delete;
optional<RawHandleType> release() noexcept
{
auto oldHandle = internalHandle_;
internalHandle_ = nullopt;
return oldHandle;
}
template<class ... Args>
static RessourceHandler create(Args&&... args)
{
return { getConstructor()(std::forward<Args>(args)...) };
}
void reset(optional<RawHandleType> handle) noexcept
{
getDestructor()(internalHandle_);
internalHandle_ = handle;
}
optional<RawHandleType> get() const noexcept
{
return internalHandle_;
}
static ConstructorType* getConstructor() noexcept
{
return RessourceManagementStrategy::construct;
}
static DestructorType* getDestructor() noexcept
{
return RessourceManagementStrategy::destroy;
}
void swap(RessourceHandler& other) noexcept
{
using std::swap;
swap(internalHandle_, other.internalHandle_);
}
operator bool() const noexcept
{
return internalHandle_;
}
template<class T>
friend bool operator<(RessourceHandler<T>& lhs, RessourceHandler<T>& rhs);
template<class T>
friend bool operator>(RessourceHandler<T>& lhs, RessourceHandler<T>& rhs);
template<class T>
friend bool operator<=(RessourceHandler<T>& lhs, RessourceHandler<T>& rhs);
template<class T>
friend bool operator>=(RessourceHandler<T>& lhs, RessourceHandler<T>& rhs);
template<class T>
friend bool operator==(RessourceHandler<T>& lhs, RessourceHandler<T>& rhs);
template<class T>
friend bool operator!=(RessourceHandler<T>& lhs, RessourceHandler<T>& rhs);
private:
optional<RawHandleType> internalHandle_;
};
/* Explicitly defaultabled comparison operators ? Pretty please C++ !
* Here I'm not bothering defining comparison in terms of other. It's more simple that way for such
* simple comparisons operators.
*/
template<class T>
void swap(RessourceHandler<T>& lhs, RessourceHandler<T>& rhs)
{
lhs.swap(rhs);
}
template<class T>
bool operator<(const RessourceHandler<T>& lhs, const RessourceHandler<T>& rhs)
{
lhs.internalHandle_ < rhs.internalHandle_;
}
template<class T>
bool operator>(const RessourceHandler<T>& lhs, const RessourceHandler<T>& rhs)
{
lhs.internalHandle_ > rhs.internalHandle_;
}
template<class T>
bool operator<=(const RessourceHandler<T>& lhs, const RessourceHandler<T>& rhs)
{
lhs.internalHandle_ <= rhs.internalHandle_;
}
template<class T>
bool operator>=(const RessourceHandler<T>& lhs, const RessourceHandler<T>& rhs)
{
lhs.internalHandle_ >= rhs.internalHandle_;
}
template<class T>
bool operator==(const RessourceHandler<T>& lhs, const RessourceHandler<T>& rhs)
{
lhs.internalHandle_ == rhs.internalHandle_;
}
template<class T>
bool operator!=(const RessourceHandler<T>& lhs, const RessourceHandler<T>& rhs)
{
lhs.internalHandle_ != rhs.internalHandle_;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment