From 66a0fa20003e93a09d1430476dc1ff541601a4cc Mon Sep 17 00:00:00 2001 From: Sam Yates <halfflat@gmail.com> Date: Thu, 16 Jun 2016 14:20:17 +0200 Subject: [PATCH] Experiments in code formatting continued. --- src/util/meta.hpp | 36 +++++ src/util/optional.hpp | 267 +++++++++++++++++-------------------- src/util/uninitialized.hpp | 93 ++++++------- 3 files changed, 202 insertions(+), 194 deletions(-) create mode 100644 src/util/meta.hpp diff --git a/src/util/meta.hpp b/src/util/meta.hpp new file mode 100644 index 00000000..2e22e31c --- /dev/null +++ b/src/util/meta.hpp @@ -0,0 +1,36 @@ +#pragma once + +/* Type utilities and convenience expressions. */ + +#include <type_traits> + +namespace nest { +namespace mc { +namespace util { + +// Until C++14 ... + +template <typename T> +using result_of_t = typename std::result_of<T>::type; + +template <bool V> +using enable_if_t = typename std::enable_if<V>::type; + +// Convenience short cuts + +template <typename T> +using enable_if_copy_constructible_t = + enable_if_t<std::is_copy_constructible<T>::value>; + +template <typename T> +using enable_if_move_constructible_t = + enable_if_t<std::is_move_constructible<T>::value>; + +template <typename... T> +using enable_if_constructible_t = + enable_if_t<std::is_constructible<T...>::value>; + + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/src/util/optional.hpp b/src/util/optional.hpp index d23b5b8b..1935a6b3 100644 --- a/src/util/optional.hpp +++ b/src/util/optional.hpp @@ -1,25 +1,25 @@ #pragma once -/*! \file optional.h - * \brief An option class with a monadic interface. +/* An option class with a monadic interface. * - * The std::option<T> class was proposed for inclusion into C++14, but was - * ultimately rejected. (See N3672 proposal for details.) This class offers - * similar functionality, namely a class that can represent a value (or - * reference), or nothing at all. + * The std::option<T> class was proposed for inclusion into C++14, but was + * ultimately rejected. (See N3672 proposal for details.) This class offers + * similar functionality, namely a class that can represent a value (or + * reference), or nothing at all. * - * In addition, this class offers monadic and monoidal bindings, allowing - * the chaining of operations any one of which might represent failure with - * an unset optional value. + * In addition, this class offers monadic and monoidal bindings, allowing + * the chaining of operations any one of which might represent failure with + * an unset optional value. * - * One point of difference between the proposal N3672 and this implementation - * is the lack of constexpr versions of the methods and constructors. + * One point of difference between the proposal N3672 and this implementation + * is the lack of constexpr versions of the methods and constructors. */ #include <type_traits> #include <stdexcept> #include <utility> +#include "util/meta.hpp" #include "util/uninitialized.hpp" namespace nest { @@ -43,7 +43,7 @@ struct optional_invalid_dereference: std::runtime_error { : std::runtime_error(what_str) {} - optional_invalid_dereference()\ + optional_invalid_dereference() : std::runtime_error("derefernce of optional<void> value") {} }; @@ -52,69 +52,61 @@ struct nothing_t {}; constexpr nothing_t nothing{}; namespace detail { - // the only change I make is to add an underscore after lift_type_ template <typename Y> - struct lift_type_ - { using type=optional<Y>; }; + struct lift_type { + using type = optional<Y>; + }; template <typename Y> - struct lift_type_<optional<Y>> - { using type=optional<Y>; }; + struct lift_type<optional<Y>> { + using type = optional<Y>; + }; - // ... then use an alias template to remove the need for a ::type in user code - // this is in the same vain as the metafunctions like std::decay_t that were introduced in C++14 template <typename Y> - using lift_type = typename lift_type_<Y>::type; + using lift_type_t = typename lift_type<Y>::type; struct optional_tag {}; - // constexpr function instead template <typename X> - constexpr bool is_optional() { - return std::is_base_of<optional_tag, typename std::decay<X>::type>::value; - } + using is_optional = std::is_base_of<optional_tag, typename std::decay<X>::type>; - // you were kind of using the same pattern here... template <typename D,typename X> - struct wrapped_type_ - { using type=X; }; + struct wrapped_type_impl { + using type = X; + }; template <typename D,typename X> - struct wrapped_type_<optional<D>,X> - { using type=D; }; + struct wrapped_type_impl<optional<D>,X> { + using type = D; + }; + + template <typename X> + struct wrapped_type { + using type = typename wrapped_type_impl<typename std::decay<X>::type,X>::type; + }; - // but we can simplify the following with an alias template - //template <typename X> struct wrapped_type { using type=typename wrapped_type_<typename std::decay<X>::type,X>::type; }; template <typename X> - using wrapped_type = - typename wrapped_type_<typename std::decay<X>::type, X>::type; + using wrapped_type_t = typename wrapped_type<X>::type; template <typename X> struct optional_base: detail::optional_tag { template <typename Y> friend struct optional; protected: - // D is used throughout, so maybe give it a more descriptive name like unitinitialized_type ? - // whatever, I have the style of using CamelCase for template parameters, and lower case with underscore - // for "typedefed" types inside the class - using D=util::uninitialized<X>; + using data_type = util::uninitialized<X>; public: - // sorry, I like spaces around = signs and spaces after commas - using reference_type = typename D::reference_type; - using const_reference_type = typename D::const_reference_type; - using pointer_type = typename D::pointer_type; - using const_pointer_type = typename D::const_pointer_type; + using reference_type = typename data_type::reference_type; + using const_reference_type = typename data_type::const_reference_type; + using pointer_type = typename data_type::pointer_type; + using const_pointer_type = typename data_type::const_pointer_type; protected: bool set; - D data; + data_type data; - // don't be afraid of vertical space - optional_base() : set(false) - {} + optional_base() : set(false) {} - // I am not too fussy, you could get away with this... template <typename T> optional_base(bool set_, T&& init) : set(set_) { if (set) { @@ -122,13 +114,10 @@ namespace detail { } } - // I could be persuaded to go one line for these, but a bit of vertical alignment helps reference_type ref() { return data.ref(); } const_reference_type ref() const { return data.cref(); } public: - // I know that this is annoying.. - //~optional_base() { if (set) data.destruct(); } ~optional_base() { if (set) { data.destruct(); @@ -142,6 +131,7 @@ namespace detail { reference_type operator*() { return ref(); } reference_type get() { + // I find this super verbose :( if (set) { return ref(); } @@ -159,18 +149,15 @@ namespace detail { } } - // how about the following as a compromise for simple one-liners? - explicit operator bool() const - { return set; } + explicit operator bool() const { return set; } - // though this is just one more line... template <typename Y> - bool operator==(const Y &y) const { + bool operator==(const Y& y) const { return set && ref()==y; } template <typename Y> - bool operator==(const optional<Y> &o) const { + bool operator==(const optional<Y>& o) const { return (set && o.set && ref()==o.ref()) || (!set && !o.set); } @@ -178,45 +165,42 @@ namespace detail { if (set) { data.destruct(); } - set=false; + set = false; } - // see how we remove another typename and another ::type below template <typename F> - auto bind(F &&f) -> lift_type<decltype(data.apply(std::forward<F>(f)))> { + auto bind(F&& f) -> lift_type_t<decltype(data.apply(std::forward<F>(f)))> { using F_result_type = decltype(data.apply(std::forward<F>(f))); - using result_type = lift_type<F_result_type>; + using result_type = lift_type_t<F_result_type>; if (!set) { return result_type(); } - else return bind_impl<result_type,std::is_same<F_result_type,void>::value>::bind(data,std::forward<F>(f)); + + return bind_impl<result_type, std::is_void<F_result_type>::value>:: + bind(data, std::forward<F>(f)); } template <typename F> - auto bind(F &&f) const -> lift_type<decltype(data.apply(std::forward<F>(f)))> { + auto bind(F&& f) const -> lift_type_t<decltype(data.apply(std::forward<F>(f)))> { using F_result_type = decltype(data.apply(std::forward<F>(f))); - using result_type = lift_type<F_result_type>; + using result_type = lift_type_t<F_result_type>; if (!set) { return result_type(); } - else { - return - bind_impl< - result_type, - std::is_same<F_result_type,void>::value - >::bind(data, std::forward<F>(f)); - } + + return bind_impl<result_type, std::is_void<F_result_type>::value>:: + bind(data, std::forward<F>(f)); } template <typename F> - auto operator>>(F &&f) -> decltype(this->bind(std::forward<F>(f))) { + auto operator>>(F&& f) -> decltype(this->bind(std::forward<F>(f))) { return bind(std::forward<F>(f)); } template <typename F> - auto operator>>(F &&f) const -> decltype(this->bind(std::forward<F>(f))) { + auto operator>>(F&& f) const -> decltype(this->bind(std::forward<F>(f))) { return bind(std::forward<F>(f)); } @@ -224,7 +208,7 @@ namespace detail { template <typename R, bool F_void_return> struct bind_impl { template <typename DT,typename F> - static R bind(DT &d,F &&f) { + static R bind(DT& d,F&& f) { return R(d.apply(std::forward<F>(f))); } }; @@ -232,17 +216,22 @@ namespace detail { template <typename R> struct bind_impl<R,true> { template <typename DT,typename F> - static R bind(DT &d,F &&f) { + static R bind(DT& d,F&& f) { d.apply(std::forward<F>(f)); return R(true); } }; }; -} + + // type utilities + template <typename T> + using enable_unless_optional_t = enable_if_t<!is_optional<T>::value>; + +} // namespace detail template <typename X> struct optional: detail::optional_base<X> { - using base=detail::optional_base<X>; + using base = detail::optional_base<X>; using base::set; using base::ref; using base::reset; @@ -251,63 +240,54 @@ struct optional: detail::optional_base<X> { optional(): base() {} optional(nothing_t): base() {} - // ... this makes it much easier to read template < typename Y = X, - typename = typename std::enable_if<std::is_copy_constructible<Y>::value>::type - // and this is how it would look with my style : - //typename = std::enable_if<std::is_copy_constructible<Y>()> + typename = enable_if_copy_constructible_t<Y> > - optional(const X &x) - : base(true,x) - {} - - // and out of curiosity, this is how it would have looked if the C++ standards - // folks had got it right the first time + optional(const X& x): base(true, x) {} - template <typename Y=X,typename = typename std::enable_if<std::is_move_constructible<Y>::value>::type> - optional(X &&x): base(true,std::move(x)) {} + template < + typename Y = X, + typename = enable_if_move_constructible_t<Y> + > + optional(X&& x): base(true, std::move(x)) {} - optional(const optional &ot): base(ot.set,ot.ref()) {} + optional(const optional& ot): base(ot.set, ot.ref()) {} template <typename T> - optional(const optional<T> &ot): base(ot.set,ot.ref()) {} + optional(const optional<T>& ot): base(ot.set, ot.ref()) {} - optional(optional &&ot): base(ot.set,std::move(ot.ref())) {} + optional(optional&& ot): base(ot.set, std::move(ot.ref())) {} template <typename T> - optional(optional<T> &&ot): base(ot.set,std::move(ot.ref())) {} + optional(optional<T>&& ot): base(ot.set, std::move(ot.ref())) {} - // constexpr yay! - //template <typename Y,typename = typename std::enable_if<!detail::is_optional<Y>::value>::type> template < typename Y, - typename = typename std::enable_if<!detail::is_optional<Y>()>::type + typename = detail::enable_unless_optional_t<Y> > - optional &operator=(Y &&y) { + optional& operator=(Y&& y) { if (set) { - ref()=std::forward<Y>(y); + ref() = std::forward<Y>(y); } else { - set=true; + set = true; data.construct(std::forward<Y>(y)); } return *this; } - // small style point - //optional &operator=(const optional &o) { - optional& operator=(const optional &o) { + optional& operator=(const optional& o) { if (set) { if (o.set) { - ref()=o.ref(); + ref() = o.ref(); } else { reset(); } } else { - set=o.set; + set = o.set; if (set) { data.construct(o.ref()); } @@ -315,54 +295,54 @@ struct optional: detail::optional_base<X> { return *this; } - // this is much clearer - // I line the closing template > like I would a curly brace template < - typename Y=X, - typename = typename - std::enable_if< - std::is_move_assignable<Y>::value && - std::is_move_constructible<Y>::value - >::type + typename Y = X, + typename = typename std::enable_if< + std::is_move_assignable<Y>::value && + std::is_move_constructible<Y>::value + >::type > - optional& operator=(optional &&o) { - // fix the {} ! + optional& operator=(optional&& o) { if (set) { - if (o.set) ref()=std::move(o.ref()); + if (o.set) { + ref() = std::move(o.ref()); + } else reset(); } else { - set=o.set; - if (set) data.construct(std::move(o.ref())); + set = o.set; + if (set) { + data.construct(std::move(o.ref())); + } } return *this; } }; template <typename X> -struct optional<X &>: detail::optional_base<X &> { - using base=detail::optional_base<X &>; +struct optional<X&>: detail::optional_base<X&> { + using base=detail::optional_base<X&>; using base::set; using base::ref; using base::data; optional(): base() {} optional(nothing_t): base() {} - optional(X &x): base(true,x) {} + optional(X&x): base(true,x) {} template <typename T> - optional(optional<T &> &ot): base(ot.set,ot.ref()) {} + optional(optional<T&>& ot): base(ot.set,ot.ref()) {} template <typename Y,typename = typename std::enable_if<!detail::is_optional<Y>()>::type> - optional &operator=(Y &y) { - set=true; - ref()=y; + optional& operator=(Y& y) { + set = true; + ref() = y; return *this; } template <typename Y> - optional &operator=(optional<Y &> &o) { - set=o.set; + optional& operator=(optional<Y&>& o) { + set = o.set; data.construct(o); return *this; } @@ -374,7 +354,7 @@ struct optional<X &>: detail::optional_base<X &> { template <> struct optional<void>: detail::optional_base<void> { - using base=detail::optional_base<void>; + using base = detail::optional_base<void>; using base::set; optional(): base() {} @@ -383,19 +363,24 @@ struct optional<void>: detail::optional_base<void> { optional(T): base(true,true) {} template <typename T> - optional(const optional<T> &o): base(o.set,true) {} + optional(const optional<T>& o): base(o.set,true) {} template <typename T> - optional &operator=(T) { set=true; return *this; } + optional& operator=(T) { + set = true; + return *this; + } template <typename T> - optional &operator=(const optional<T> &o) { set=o.set; return *this; } + optional& operator=(const optional<T>& o) { + set = o.set; + return *this; + } - // override equality operators template <typename Y> - bool operator==(const Y &y) const { return false; } + bool operator==(const Y& y) const { return false; } - bool operator==(const optional<void> &o) const { + bool operator==(const optional<void>& o) const { return (set && o.set) || (!set && !o.set); } }; @@ -403,26 +388,26 @@ struct optional<void>: detail::optional_base<void> { template <typename A,typename B> typename std::enable_if< - detail::is_optional<A>() || detail::is_optional<B>(), + detail::is_optional<A>::value || detail::is_optional<B>::value, optional< typename std::common_type< - typename detail::wrapped_type<A>, - typename detail::wrapped_type<B> + detail::wrapped_type_t<A>, + detail::wrapped_type_t<B> >::type > >::type -operator|(A &&a,B &&b) { +operator|(A&& a,B&& b) { return a ? a : b; } template <typename A,typename B> typename std::enable_if< - detail::is_optional<A>() || detail::is_optional<B>(), - optional<detail::wrapped_type<B>> + detail::is_optional<A>::value || detail::is_optional<B>::value, + optional<detail::wrapped_type_t<B>> >::type operator&(A&& a,B&& b) { - using result_type=optional<detail::wrapped_type<B>>; - return a?b:result_type(); + using result_type = optional<detail::wrapped_type_t<B>>; + return a ? b: result_type(); } inline optional<void> provided(bool condition) { @@ -430,7 +415,7 @@ inline optional<void> provided(bool condition) { } template <typename X> -optional<X> just(X &&x) { +optional<X> just(X&& x) { return optional<X>(std::forward<X>(x)); } diff --git a/src/util/uninitialized.hpp b/src/util/uninitialized.hpp index f2449e41..846e46d5 100644 --- a/src/util/uninitialized.hpp +++ b/src/util/uninitialized.hpp @@ -5,13 +5,15 @@ * The uninitialized<X> structure holds space for an item of * type X, leaving its construction or destruction to the user. * - * Specialisations for reference types X & and for the void type + * Specialisations for reference types X& and for the void type * allow for the handling of non-value types in a uniform manner. */ #include <type_traits> #include <utility> +#include "util/meta.hpp" + namespace nest { namespace mc { namespace util { @@ -22,47 +24,47 @@ namespace util { template <typename X> struct uninitialized { private: - typename std::aligned_storage<sizeof(X),alignof(X)>::type data; + typename std::aligned_storage<sizeof(X), alignof(X)>::type data; public: - using pointer_type=X *; - using const_pointer_type=const X *; - using reference_type=X &; - using const_reference_type=const X &; + using pointer_type = X*; + using const_pointer_type = const X*; + using reference_type = X&; + using const_reference_type = const X&; - pointer_type ptr() { return reinterpret_cast<X *>(&data); } - const_pointer_type cptr() const { return reinterpret_cast<const X *>(&data); } + pointer_type ptr() { return reinterpret_cast<X*>(&data); } + const_pointer_type cptr() const { return reinterpret_cast<const X*>(&data); } - reference_type ref() { return *reinterpret_cast<X *>(&data); } - const_reference_type cref() const { return *reinterpret_cast<const X *>(&data); } + reference_type ref() { return *reinterpret_cast<X*>(&data); } + const_reference_type cref() const { return *reinterpret_cast<const X*>(&data); } // Copy construct the value. template < typename Y = X, - typename = typename - std::enable_if< std::is_copy_constructible<Y>::value >::type + typename = enable_if_copy_constructible_t<Y> > - void construct(const X &x) { + void construct(const X& x) { new(&data) X(x); } // General constructor for X, forwarding arguments. - template <typename... Y, - typename =typename std::enable_if<std::is_constructible<X,Y...>::value>::type> - void construct(Y&& ...args) { new(&data) X(std::forward<Y>(args)...); } + template < + typename... Y, + typename = enable_if_constructible_t<X, Y...> + > + void construct(Y&& ...args) { + new(&data) X(std::forward<Y>(args)...); + } void destruct() { ptr()->~X(); } // Apply the one-parameter functor F to the value by reference. template <typename F> - typename std::result_of<F(reference_type)>::type - apply(F &&f) { - return f(ref()); - } + result_of_t<F(reference_type)> apply(F&& f) { return f(ref()); } // Apply the one-parameter functor F to the value by const reference. template <typename F> - typename std::result_of<F(const_reference_type)>::type apply(F &&f) const { return f(cref()); } + result_of_t<F(const_reference_type)> apply(F&& f) const { return f(cref()); } }; /* Maintains storage for a pointer of type X, representing @@ -74,10 +76,10 @@ private: X *data; public: - using pointer_type=X *; - using const_pointer_type=const X *; - using reference_type=X &; - using const_reference_type=const X &; + using pointer_type = X*; + using const_pointer_type = const X*; + using reference_type = X&; + using const_reference_type = const X&; pointer_type ptr() { return data; } const_pointer_type cptr() const { return data; } @@ -85,15 +87,20 @@ public: reference_type ref() { return *data; } const_reference_type cref() const { return *data; } - void construct(X &x) { data=&x; } + void construct(X& x) { data = &x; } void destruct() {} // Apply the one-parameter functor F to the value by reference. template <typename F> - typename std::result_of<F(reference_type)>::type apply(F &&f) { return f(ref()); } + result_of_t<F(reference_type)> apply(F&& f) { + return f(ref()); + } + // Apply the one-parameter functor F to the value by const reference. template <typename F> - typename std::result_of<F(const_reference_type)>::type apply(F &&f) const { return f(cref()); } + result_of_t<F(const_reference_type)> apply(F&& f) const { + return f(cref()); + } }; /* Wrap a void type in an uninitialized template. @@ -102,10 +109,10 @@ public: */ template <> struct uninitialized<void> { - using pointer_type=void *; - using const_pointer_type=const void *; - using reference_type=void; - using const_reference_type=void; + using pointer_type = void*; + using const_pointer_type = const void*; + using reference_type = void; + using const_reference_type = void; pointer_type ptr() { return nullptr; } const_pointer_type cptr() const { return nullptr; } @@ -120,29 +127,9 @@ struct uninitialized<void> { // Equivalent to f() template <typename F> - typename std::result_of<F()>::type apply(F &&f) const { return f(); } + result_of_t<F()> apply(F&& f) const { return f(); } }; -// proposed change... -// is this too much? -template <typename...> -struct uninitialized_can_construct_: std::false_type {}; - -template <typename X,typename... Y> -struct uninitialized_can_construct_<X,Y...>: std::integral_constant<bool,std::is_constructible<X,Y...>::value> {}; - -template <typename X,typename Y> -struct uninitialized_can_construct_<X &,Y>: std::integral_constant<bool,std::is_convertible<X &,Y>::value> {}; - -template <typename... Y> -struct uninitialized_can_construct_<void,Y...>: std::true_type {}; - - -template <typename... X> -constexpr bool uninitialized_can_construct() { - return uninitialized_can_construct_<X...>::value; -} - } // namespace util } // namespace mc } // namespace nest -- GitLab