diff --git a/external/optionalm/README b/external/optionalm/README new file mode 100644 index 0000000000000000000000000000000000000000..206f59688c7a8d5687f53dec41e9db6af3340a61 --- /dev/null +++ b/external/optionalm/README @@ -0,0 +1 @@ +Adapted from github.com:halfflat/optionalm diff --git a/external/optionalm/optionalm.h b/external/optionalm/optionalm.h new file mode 100644 index 0000000000000000000000000000000000000000..d22c8ea27a4cd7304ae8becc6ebe373a355453c7 --- /dev/null +++ b/external/optionalm/optionalm.h @@ -0,0 +1,318 @@ +/*! \file optionalm.h + * \brief 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. + * + * 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. + */ + +#ifndef HF_OPTIONALM_H_ +#define HF_OPTIONALM_H_ + +#include <type_traits> +#include <stdexcept> +#include <utility> + +#include <optionalm/uninitialized.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wlogical-op-parentheses" + +namespace hf { +namespace optionalm { + +template <typename X> struct optional; + +struct optional_unset_error: std::runtime_error { + explicit optional_unset_error(const std::string &what_str): std::runtime_error(what_str) {} + optional_unset_error(): std::runtime_error("optional value unset") {} +}; + +struct optional_invalid_dereference: std::runtime_error { + explicit optional_invalid_dereference(const std::string &what_str): std::runtime_error(what_str) {} + optional_invalid_dereference(): std::runtime_error("derefernce of optional<void> value") {} +}; + +struct nothing_t {}; +constexpr nothing_t nothing{}; + +namespace detail { + template <typename Y> struct lift_type { typedef optional<Y> type; }; + template <typename Y> struct lift_type<optional<Y>> { typedef optional<Y> type; }; + + struct optional_tag {}; + + template <typename X> struct is_optional { + enum {value=std::is_base_of<optional_tag,typename std::decay<X>::type>::value }; + }; + + template <typename D,typename X> struct wrapped_type_ { typedef X type; }; + template <typename D,typename X> struct wrapped_type_<optional<D>,X> { typedef D type; }; + + template <typename X> struct wrapped_type { typedef typename wrapped_type_<typename std::decay<X>::type,X>::type type; }; + + template <typename X> + struct optional_base: detail::optional_tag { + template <typename Y> friend struct optional; + + protected: + typedef hf::optionalm::uninitialized<X> D; + + public: + typedef typename D::reference_type reference_type; + typedef typename D::const_reference_type const_reference_type; + typedef typename D::pointer_type pointer_type; + typedef typename D::const_pointer_type const_pointer_type; + + protected: + bool set; + D data; + + optional_base(): set(false) {} + + template <typename T> + optional_base(bool set_,T&& init): set(set_) { if (set) data.construct(std::forward<T>(init)); } + + reference_type ref() { return data.ref(); } + const_reference_type ref() const { return data.cref(); } + + public: + ~optional_base() { if (set) data.destruct(); } + + const_pointer_type operator->() const { return data.ptr(); } + pointer_type operator->() { return data.ptr(); } + + const_reference_type operator*() const { return ref(); } + reference_type operator*() { return ref(); } + + reference_type get() { + if (set) return ref(); + else throw optional_unset_error(); + } + + const_reference_type get() const { + if (set) return ref(); + else throw optional_unset_error(); + } + + explicit operator bool() const { return set; } + + template <typename Y> + bool operator==(const Y &y) const { return set && ref()==y; } + + template <typename Y> + bool operator==(const optional<Y> &o) const { + return set && o.set && ref()==o.ref() || !set && !o.set; + } + + void reset() { + if (set) data.destruct(); + set=false; + } + + template <typename F> + auto bind(F &&f) -> typename lift_type<decltype(data.apply(std::forward<F>(f)))>::type { + typedef decltype(data.apply(std::forward<F>(f))) F_result_type; + typedef typename lift_type<F_result_type>::type 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)); + } + + template <typename F> + auto bind(F &&f) const -> typename lift_type<decltype(data.apply(std::forward<F>(f)))>::type { + typedef decltype(data.apply(std::forward<F>(f))) F_result_type; + typedef typename lift_type<F_result_type>::type 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)); + } + + template <typename 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))) { return bind(std::forward<F>(f)); } + + private: + template <typename R,bool F_void_return> + struct bind_impl { + template <typename DT,typename F> + static R bind(DT &d,F &&f) { return R(d.apply(std::forward<F>(f))); } + }; + + template <typename R> + struct bind_impl<R,true> { + template <typename DT,typename F> + static R bind(DT &d,F &&f) { d.apply(std::forward<F>(f)); return R(true); } + }; + + }; +} + +template <typename X> +struct optional: detail::optional_base<X> { + typedef detail::optional_base<X> base; + using base::set; + using base::ref; + using base::reset; + using base::data; + + optional(): base() {} + optional(nothing_t): base() {} + + template <typename Y=X,typename = typename std::enable_if<std::is_copy_constructible<Y>::value>::type> + 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)) {} + + optional(const optional &ot): base(ot.set,ot.ref()) {} + + template <typename T> + optional(const optional<T> &ot): base(ot.set,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())) {} + + template <typename Y,typename = typename std::enable_if<!detail::is_optional<Y>::value>::type> + optional &operator=(Y &&y) { + if (set) ref()=std::forward<Y>(y); + else { + set=true; + data.construct(std::forward<Y>(y)); + } + return *this; + } + + optional &operator=(const optional &o) { + if (set) { + if (o.set) ref()=o.ref(); + else reset(); + } + else { + set=o.set; + if (set) data.construct(o.ref()); + } + return *this; + } + + template <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) { + if (set) { + if (o.set) ref()=std::move(o.ref()); + else reset(); + } + else { + set=o.set; + if (set) data.construct(std::move(o.ref())); + } + return *this; + } +}; + +template <typename X> +struct optional<X &>: detail::optional_base<X &> { + typedef detail::optional_base<X &> base; + using base::set; + using base::ref; + using base::data; + + optional(): base() {} + optional(nothing_t): base() {} + optional(X &x): base(true,x) {} + + template <typename T> + optional(optional<T &> &ot): base(ot.set,ot.ref()) {} + + template <typename Y,typename = typename std::enable_if<!detail::is_optional<Y>::value>::type> + optional &operator=(Y &y) { + set=true; + ref()=y; + return *this; + } + + template <typename Y> + optional &operator=(optional<Y &> &o) { + set=o.set; + data.construct(o); + return *this; + } +}; + + +/* special case for optional<void>, used as e.g. the result of + * binding to a void function */ + +template <> +struct optional<void>: detail::optional_base<void> { + typedef detail::optional_base<void> base; + using base::set; + + optional(): base() {} + + template <typename T> + optional(T): base(true,true) {} + + template <typename T> + optional(const optional<T> &o): base(o.set,true) {} + + template <typename T> + optional &operator=(T) { set=true; return *this; } + + template <typename T> + 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 optional<void> &o) const { + return set && o.set || !set && !o.set; + } +}; + + +template <typename A,typename B> +typename std::enable_if< + detail::is_optional<A>::value || detail::is_optional<B>::value, + optional<typename std::common_type<typename detail::wrapped_type<A>::type,typename detail::wrapped_type<B>::type>::type> +>::type +operator|(A &&a,B &&b) { + typedef typename std::common_type<typename detail::wrapped_type<A>::type,typename detail::wrapped_type<B>::type>::type common; + return a?a:b; +} + +template <typename A,typename B> +typename std::enable_if< + detail::is_optional<A>::value || detail::is_optional<B>::value, + optional<typename detail::wrapped_type<B>::type> +>::type +operator&(A &&a,B &&b) { + typedef optional<typename detail::wrapped_type<B>::type> result_type; + return a?b:result_type(); +} + +inline optional<void> provided(bool condition) { return condition?optional<void>(true):optional<void>(); } + +template <typename X> +optional<X> just(X &&x) { return optional<X>(std::forward<X>(x)); } + +}} // namespace hf::optionalm + +#pragma clang diagnostic pop + +#endif // ndef HF_OPTIONALM_H_ diff --git a/external/optionalm/uninitialized.h b/external/optionalm/uninitialized.h new file mode 100644 index 0000000000000000000000000000000000000000..264e3844bc9b21e3f6af44c92fa588141f517ac0 --- /dev/null +++ b/external/optionalm/uninitialized.h @@ -0,0 +1,175 @@ +/*! \file uninitialized.h + * \brief Represent a possibly-uninitialized value, reference or void. + * + * The \c uninitialized\<X\> structure holds space for an item of + * type \c X, leaving its construction or destruction to the user. + */ + +#ifndef HF_UNINITIALIZED_H_ +#define HF_UNINITIALIZED_H_ + +namespace hf { +namespace optionalm { + +/*! \defgroup uninitialized The uninitialized<X> classes. + * \{ + * + * The \c uninitialized\<X\> structure holds space for an item of + * type \c X, which can then be explicitly constructed or + * deconstructed through the \c construct() and \c destruct() + * member functions. + * + * Specialisations for reference types <tt>X &</tt> and for the + * \c void type allow handling non-value types in a uniform + * manner. + */ + +/*! \brief Maintains storage for a value of type X, with explicit + * construction and destruction. + * + * \tparam X + * The wrapped value type. + */ +template <typename X> +struct uninitialized { +private: + typename std::aligned_storage<sizeof(X),alignof(X)>::type data; + +public: + //! + typedef X *pointer_type; + //! + typedef const X *const_pointer_type; + //! + typedef X &reference_type; + //! + typedef const X &const_reference_type; + + //! Return a pointer to the value. + pointer_type ptr() { return reinterpret_cast<X *>(&data); } + //! Return a const pointer to the value. + const_pointer_type cptr() const { return reinterpret_cast<const X *>(&data); } + + //! Return a reference to the value. + reference_type ref() { return *reinterpret_cast<X *>(&data); } + //! Return a const reference to the value. + 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> + void construct(const X &x) { new(&data) X(x); } + + //! General constructor + 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)...); } + + //! Call the destructor of the value. + 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()); } + //! 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()); } +}; + +/*! \brief Maintains storage for a pointer of type X, representing + * a possibly uninitialized reference. + * + * \tparam X& + * Wrapped reference type. + */ +template <typename X> +struct uninitialized<X&> { +private: + X *data; + +public: + //! + typedef X *pointer_type; + //! + typedef const X *const_pointer_type; + //! + typedef X &reference_type; + //! + typedef const X &const_reference_type; + + //! Return a pointer to the value. + pointer_type ptr() { return data; } + //! Return a const pointer to the value. + const_pointer_type cptr() const { return data; } + + //! Return a reference to the value. + reference_type ref() { return *data; } + //! Return a const reference to the value. + const_reference_type cref() const { return *data; } + + //! Set the reference data. + void construct(X &x) { data=&x; } + //! Destruct is a NOP for reference data. + 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()); } + //! 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()); } +}; + +/*! \brief Wrap a void type in an uninitialized template. + * + * Allows the use of <tt>uninitialized\<X\></tt> for void \c X, for generic + * applications. + */ + +template <> +struct uninitialized<void> { + //! + typedef void *pointer_type; + //! + typedef const void *const_pointer_type; + //! + typedef void reference_type; + //! + typedef void const_reference_type; + + //! + pointer_type ptr() { return nullptr; } + //! + const_pointer_type cptr() const { return nullptr; } + + //! + reference_type ref() {} + //! + const_reference_type cref() const {} + + //! No operation. + void construct(...) {} + //! No operation. + void destruct() {} + + //! Equivalent to <tt>f()</tt> + template <typename F> + typename std::result_of<F()>::type apply(F &&f) const { return f(); } +}; + +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 {}; + +/*! \} */ + +}} // namespace hf::optionalm + +#endif // ndef HF_UNINITIALIZED_H_ + diff --git a/src/event_queue.hpp b/src/event_queue.hpp index 0a8b33707a2d82e949717fd243479ce33776120e..80b14a892c1cbafa0d39ba2a337d37190f98f873 100644 --- a/src/event_queue.hpp +++ b/src/event_queue.hpp @@ -1,9 +1,10 @@ #pragma once +#include <cstdint> #include <ostream> #include <queue> -#include <cstdint> +#include <optionalm/optionalm.h> namespace nest { namespace mc { @@ -45,14 +46,14 @@ public : } // pop until - std::pair<bool, local_event> pop_if_before(float t_until) { + hf::optionalm::optional<local_event> pop_if_before(float t_until) { if (!queue_.empty() && queue_.top().time < t_until) { auto ev = queue_.top(); queue_.pop(); - return {true, ev}; + return ev; } else { - return {false, {}}; + return hf::optionalm::nothing; } } diff --git a/src/fvm.hpp b/src/fvm.hpp index 4a908bba49887948e37bfa7fe3409b19df90bbb3..49d0af7a7ea2a24a486eb99f1d80079605ad5889 100644 --- a/src/fvm.hpp +++ b/src/fvm.hpp @@ -19,6 +19,7 @@ #include <util.hpp> #include <vector/include/Vector.hpp> +#include <optionalm/optionalm.h> #include <mechanisms/expsyn.hpp> @@ -510,18 +511,14 @@ void fvm_cell<T, I>::advance_to(T tfinal, T dt) } do { - auto tnext = std::min(tfinal, t_+dt); - auto next = events_.pop_if_before(tnext); - // if there is an event before tnext... - if(next.first) { - tnext = next.second.time; - } + auto tstep = std::min(tfinal, t_+dt); + auto next = events_.pop_if_before(tstep); + auto tnext = next? next->time: tstep; + advance(tnext-t_); t_ = tnext; - if(next.first) { // handle event - auto &e = next.second; - - mechanisms_[synapse_index_]->net_receive(e.target, e.weight); + if (next) { // handle event + mechanisms_[synapse_index_]->net_receive(next->target, next->weight); } } while(t_<tfinal); } diff --git a/tests/test_event_queue.cpp b/tests/test_event_queue.cpp index 3297784d506f6eff10078e249388942b85d2240f..e67ade0bea22a417ca417a78c550b67bb0c3e45b 100644 --- a/tests/test_event_queue.cpp +++ b/tests/test_event_queue.cpp @@ -18,7 +18,7 @@ TEST(event_queue, push) std::vector<float> times; while(q.size()) { times.push_back( - q.pop_if_before(std::numeric_limits<float>::max()).second.time + q.pop_if_before(std::numeric_limits<float>::max())->time ); } @@ -44,7 +44,7 @@ TEST(event_queue, push_range) std::vector<float> times; while(q.size()) { times.push_back( - q.pop_if_before(std::numeric_limits<float>::max()).second.time + q.pop_if_before(std::numeric_limits<float>::max())->time ); } @@ -68,26 +68,26 @@ TEST(event_queue, pop_if_before) EXPECT_EQ(q.size(), 4u); auto e1 = q.pop_if_before(0.); - EXPECT_FALSE(e1.first); + EXPECT_FALSE(e1); EXPECT_EQ(q.size(), 4u); auto e2 = q.pop_if_before(5.); - EXPECT_TRUE(e2.first); - EXPECT_EQ(e2.second.target, 1u); + EXPECT_TRUE(e2); + EXPECT_EQ(e2->target, 1u); EXPECT_EQ(q.size(), 3u); auto e3 = q.pop_if_before(5.); - EXPECT_TRUE(e3.first); - EXPECT_EQ(e3.second.target, 2u); + EXPECT_TRUE(e3); + EXPECT_EQ(e3->target, 2u); EXPECT_EQ(q.size(), 2u); auto e4 = q.pop_if_before(2.5); - EXPECT_FALSE(e4.first); + EXPECT_FALSE(e4); EXPECT_EQ(q.size(), 2u); auto e5 = q.pop_if_before(5.); - EXPECT_TRUE(e5.first); - EXPECT_EQ(e5.second.target, 3u); + EXPECT_TRUE(e5); + EXPECT_EQ(e5->target, 3u); EXPECT_EQ(q.size(), 1u); q.pop_if_before(5.); @@ -95,5 +95,5 @@ TEST(event_queue, pop_if_before) // empty queue should always return "false" auto e6 = q.pop_if_before(100.); - EXPECT_FALSE(e6.first); + EXPECT_FALSE(e6); }