diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d8497cbecea856af25a853ce399fe69ec0d63ed..7be418bede5135d4a7f6bc5be80fe8b6855b7913 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,10 @@ enable_language(CXX) set(SAVED_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # compilation flags -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++11 -pthread -Wall") +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include("CompilerOptions") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXXOPT_DEBUG} ${CXXOPT_CXX11} ${CXXOPT_PTHREAD} ${CXXOPT_WALL}") +# -g -std=c++11 -pthread -Wall") # this generates a .json file with full compilation command for each file set(CMAKE_EXPORT_COMPILE_COMMANDS "YES") diff --git a/README.md b/README.md index 58e5cb0c2b5c3985cf981310ca74c50580e7c262..0cb0c0d8dd27b41cf9358aebb5761cf221c72bef 100644 --- a/README.md +++ b/README.md @@ -15,19 +15,10 @@ module load cmake export CC=`which gcc` export CXX=`which g++` -# build modparser -cd external/modparser -cmake . -make -j -cd ../.. - -# create mechanisms -cd mechanisms -./generate.sh -cd .. - -# build main project -cmake . +# build main project (out-of-tree) +mkdir build +cd build +cmake .. make -j # test diff --git a/cmake/CompilerOptions.cmake b/cmake/CompilerOptions.cmake new file mode 100644 index 0000000000000000000000000000000000000000..e5447d88e7d0009f8ec697bb1463e3a7b737b0d8 --- /dev/null +++ b/cmake/CompilerOptions.cmake @@ -0,0 +1,14 @@ +# Compiler-aware compiler options + +set(CXXOPT_DEBUG "-g") +set(CXXOPT_PTHREAD "-pthread") +set(CXXOPT_CXX11 "-std=c++11") +set(CXXOPT_WALL "-Wall") + +if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + # Disable 'missing-braces' warning: this will inappropriately + # flag initializations such as + # std::array<int,3> a={1,2,3}; + + set(CXXOPT_WALL "${CXXOPT_WALL} -Wno-missing-braces") +endif() diff --git a/src/event_queue.hpp b/src/event_queue.hpp index 0a8b33707a2d82e949717fd243479ce33776120e..989e34873a1f3ff851d0a3a9f85276cea9d9bf7a 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 "util/optional.hpp" namespace nest { namespace mc { @@ -45,14 +46,14 @@ public : } // pop until - std::pair<bool, local_event> pop_if_before(float t_until) { + util::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 util::nothing; } } diff --git a/src/fvm.hpp b/src/fvm.hpp index 4a908bba49887948e37bfa7fe3409b19df90bbb3..603196c0cc66ebb2293c09dfd9a153fd7a806267 100644 --- a/src/fvm.hpp +++ b/src/fvm.hpp @@ -19,7 +19,6 @@ #include <util.hpp> #include <vector/include/Vector.hpp> - #include <mechanisms/expsyn.hpp> namespace nest { @@ -510,18 +509,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/src/util/optional.hpp b/src/util/optional.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a3c2da998d468c8564894f2db61b0340c056c70c --- /dev/null +++ b/src/util/optional.hpp @@ -0,0 +1,310 @@ +#pragma once + +/*! \file optional.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. + */ + +#include <type_traits> +#include <stdexcept> +#include <utility> + +#include "util/uninitialized.hpp" + +namespace nest { +namespace mc { +namespace util { + +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 { using type=optional<Y>; }; + template <typename Y> struct lift_type<optional<Y>> { using type=optional<Y>; }; + + 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_ { using type=X; }; + template <typename D,typename X> struct wrapped_type_<optional<D>,X> { using type=D; }; + + template <typename X> struct wrapped_type { using type=typename wrapped_type_<typename std::decay<X>::type,X>::type; }; + + template <typename X> + struct optional_base: detail::optional_tag { + template <typename Y> friend struct optional; + + protected: + using D=util::uninitialized<X>; + + public: + 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; + + 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 { + using F_result_type=decltype(data.apply(std::forward<F>(f))); + using result_type=typename lift_type<F_result_type>::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 { + using F_result_type=decltype(data.apply(std::forward<F>(f))); + using result_type=typename lift_type<F_result_type>::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> { + using base=detail::optional_base<X>; + 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 &> { + 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) {} + + 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> { + using base=detail::optional_base<void>; + 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) { + 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) { + using result_type=optional<typename detail::wrapped_type<B>::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 nest::mc::util diff --git a/src/util/uninitialized.hpp b/src/util/uninitialized.hpp new file mode 100644 index 0000000000000000000000000000000000000000..26ebcf90ef2c94ea0645ccb7d3a94f6a01fb2510 --- /dev/null +++ b/src/util/uninitialized.hpp @@ -0,0 +1,128 @@ +#pragma once + +/* Represent a possibly-uninitialized value, reference or void. + * + * 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 + * allow for the handling of non-value types in a uniform manner. + */ + +namespace nest { +namespace mc { +namespace util { + +/* Maintains storage for a value of type X, with explicit + * construction and destruction. + */ +template <typename X> +struct uninitialized { +private: + 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 &; + + 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); } + + // 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 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)...); } + + 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()); } +}; + +/* Maintains storage for a pointer of type X, representing + * a possibly uninitialized reference. + */ +template <typename X> +struct uninitialized<X&> { +private: + X *data; + +public: + 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; } + + reference_type ref() { return *data; } + const_reference_type cref() const { return *data; } + + 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()); } + // 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()); } +}; + +/* Wrap a void type in an uninitialized template. + * + * Allows the use of uninitialized<X> for void X, for generic applications. + */ +template <> +struct uninitialized<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; } + + reference_type ref() {} + const_reference_type cref() const {} + + // No operation. + void construct(...) {} + // No operation. + void destruct() {} + + // Equivalent to f() + 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 nest::mc::util + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9b61220058ee4f83d4102c68102dd85c7bfa9ece..33e4439834b902c2ad775dc157e03fb452c8f8ac 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,6 +20,7 @@ set(TEST_SOURCES test_fvm.cpp test_matrix.cpp test_mechanisms.cpp + test_optional.cpp test_parameters.cpp test_point.cpp test_segment.cpp @@ -27,6 +28,7 @@ set(TEST_SOURCES test_swcio.cpp test_synapses.cpp test_tree.cpp + test_uninitialized.cpp # unit test driver test.cpp 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); } diff --git a/tests/test_optional.cpp b/tests/test_optional.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6755525af3cfb70d610f29dc5fc0c525f68856be --- /dev/null +++ b/tests/test_optional.cpp @@ -0,0 +1,335 @@ +#include <typeinfo> +#include <array> +#include <algorithm> + +#include "gtest.h" +#include "util/optional.hpp" + +using namespace nest::mc::util; + +TEST(optionalm,ctors) { + optional<int> a,b(3),c=b,d=4; + + ASSERT_FALSE((bool)a); + ASSERT_TRUE((bool)b); + ASSERT_TRUE((bool)c); + ASSERT_TRUE((bool)d); + + EXPECT_EQ(3,b.get()); + EXPECT_EQ(3,c.get()); + EXPECT_EQ(4,d.get()); +} + +TEST(optionalm,unset_throw) { + optional<int> a; + int check=10; + + try { a.get(); } + catch(optional_unset_error &e) { + ++check; + } + EXPECT_EQ(11,check); + + check=20; + a=2; + try { a.get(); } + catch(optional_unset_error &e) { + ++check; + } + EXPECT_EQ(20,check); + + check=30; + a.reset(); + try { a.get(); } + catch(optional_unset_error &e) { + ++check; + } + EXPECT_EQ(31,check); +} + +TEST(optionalm,deref) { + struct foo { + int a; + explicit foo(int a_): a(a_) {} + double value() { return 3.0*a; } + }; + + optional<foo> f=foo(2); + EXPECT_EQ(6.0,f->value()); + EXPECT_EQ(2,(*f).a); +} + +TEST(optionalm,ctor_conv) { + optional<std::array<int,3>> x{{1,2,3}}; + EXPECT_EQ(3u,x->size()); +} + +TEST(optionalm,ctor_ref) { + int v=10; + optional<int &> a(v); + + EXPECT_EQ(10,a.get()); + v=20; + EXPECT_EQ(20,a.get()); + + optional<int &> b(a),c=b,d=v; + EXPECT_EQ(&(a.get()),&(b.get())); + EXPECT_EQ(&(a.get()),&(c.get())); + EXPECT_EQ(&(a.get()),&(d.get())); +} + +TEST(optionalm,assign_returns) { + optional<int> a=3; + + auto b=(a=4); + EXPECT_EQ(typeid(optional<int>),typeid(b)); + + auto bp=&(a=4); + EXPECT_EQ(&a,bp); +} + +namespace { + struct nomove { + int value; + + nomove(): value(0) {} + nomove(int i): value(i) {} + nomove(const nomove &n): value(n.value) {} + nomove(nomove &&n) = delete; + + nomove &operator=(const nomove &n) { value=n.value; return *this; } + + bool operator==(const nomove &them) const { return them.value==value; } + bool operator!=(const nomove &them) const { return !(*this==them); } + }; +} + +TEST(optionalm,ctor_nomove) { + optional<nomove> a(nomove(3)); + EXPECT_EQ(nomove(3),a.get()); + + optional<nomove> b; + b=a; + EXPECT_EQ(nomove(3),b.get()); + + b=optional<nomove>(nomove(4)); + EXPECT_EQ(nomove(4),b.get()); +} + +namespace { + struct nocopy { + int value; + + nocopy(): value(0) {} + nocopy(int i): value(i) {} + nocopy(const nocopy &n) = delete; + nocopy(nocopy &&n) { + value=n.value; + n.value=0; + } + + nocopy &operator=(const nocopy &n) = delete; + nocopy &operator=(nocopy &&n) { + value=n.value; + n.value=-1; + return *this; + } + + bool operator==(const nocopy &them) const { return them.value==value; } + bool operator!=(const nocopy &them) const { return !(*this==them); } + }; +} + +TEST(optionalm,ctor_nocopy) { + optional<nocopy> a(nocopy(5)); + EXPECT_EQ(nocopy(5),a.get()); + + optional<nocopy> b(std::move(a)); + EXPECT_EQ(nocopy(5),b.get()); + EXPECT_EQ(0,a.get().value); + + b=optional<nocopy>(nocopy(6)); + EXPECT_EQ(nocopy(6),b.get()); +} + +namespace { + optional<double> odd_half(int n) { + optional<double> h; + if (n%2==1) h=n/2.0; + return h; + } +} + +TEST(optionalm,bind) { + optional<int> a; + auto b=a.bind(odd_half); + + EXPECT_EQ(typeid(optional<double>),typeid(b)); + + a=10; + b=a.bind(odd_half); + EXPECT_FALSE((bool)b); + + a=11; + b=a.bind(odd_half); + EXPECT_TRUE((bool)b); + EXPECT_EQ(5.5,b.get()); + + b=a >> odd_half >> [](double x) { return (int)x; } >> odd_half; + EXPECT_TRUE((bool)b); + EXPECT_EQ(2.5,b.get()); +} + +TEST(optionalm,void) { + optional<void> a,b(true),c(a),d=b,e(false); + + EXPECT_FALSE((bool)a); + EXPECT_TRUE((bool)b); + EXPECT_FALSE((bool)c); + EXPECT_TRUE((bool)d); + EXPECT_TRUE((bool)e); + + auto x=a >> []() { return 1; }; + EXPECT_FALSE((bool)x); + + x=b >> []() { return 1; }; + EXPECT_TRUE((bool)x); + EXPECT_EQ(1,x.get()); +} + +TEST(optionalm,bind_to_void) { + optional<int> a,b(3); + + int call_count=0; + auto vf=[&call_count](int i) -> void { ++call_count; }; + + auto x=a >> vf; + EXPECT_EQ(typeid(optional<void>),typeid(x)); + EXPECT_FALSE((bool)x); + EXPECT_EQ(0,call_count); + + call_count=0; + x=b >> vf; + EXPECT_TRUE((bool)x); + EXPECT_EQ(1,call_count); +} + +TEST(optionalm,bind_to_optional_void) { + optional<int> a,b(3),c(4); + + int count=0; + auto count_if_odd=[&count](int i) { return i%2?(++count,optional<void>(true)):optional<void>(); }; + + auto x=a >> count_if_odd; + EXPECT_EQ(typeid(optional<void>),typeid(x)); + EXPECT_FALSE((bool)x); + EXPECT_EQ(0,count); + + count=0; + x=b >> count_if_odd; + EXPECT_TRUE((bool)x); + EXPECT_EQ(1,count); + + count=0; + x=c >> count_if_odd; + EXPECT_FALSE((bool)x); + EXPECT_EQ(0,count); +} + +TEST(optionalm,bind_with_ref) { + optional<int> a=10; + a >> [](int &v) {++v; }; + EXPECT_EQ(11,*a); +} + +namespace { + struct check_cref { + int operator()(const int &) { return 10; } + int operator()(int &) { return 11; } + }; +} + +TEST(optionalm,bind_constness) { + check_cref checker; + optional<int> a=1; + int v=*(a >> checker); + EXPECT_EQ(11,v); + + const optional<int> b=1; + v=*(b >> checker); + EXPECT_EQ(10,v); +} + + +TEST(optionalm,conversion) { + optional<double> a(3),b=5; + EXPECT_TRUE((bool)a); + EXPECT_TRUE((bool)b); + EXPECT_EQ(3.0,a.get()); + EXPECT_EQ(5.0,b.get()); + + optional<int> x; + optional<double> c(x); + optional<double> d=optional<int>(); + EXPECT_FALSE((bool)c); + EXPECT_FALSE((bool)d); + + auto doubler=[](double x) { return x*2; }; + auto y=optional<int>(3) >> doubler; + EXPECT_TRUE((bool)y); + EXPECT_EQ(6.0,y.get()); +} + +TEST(optionalm,or_operator) { + optional<const char *> default_msg="default"; + auto x=nullptr | default_msg; + EXPECT_TRUE((bool)x); + EXPECT_STREQ("default",x.get()); + + auto y="something" | default_msg; + EXPECT_TRUE((bool)y); + EXPECT_STREQ("something",y.get()); + + optional<int> a(1),b,c(3); + EXPECT_EQ(1,*(a|b|c)); + EXPECT_EQ(1,*(a|c|b)); + EXPECT_EQ(1,*(b|a|c)); + EXPECT_EQ(3,*(b|c|a)); + EXPECT_EQ(3,*(c|a|b)); + EXPECT_EQ(3,*(c|b|a)); +} + +TEST(optionalm,and_operator) { + optional<int> a(1); + optional<double> b(2.0); + + auto ab=a&b; + auto ba=b&a; + + EXPECT_EQ(typeid(ab),typeid(b)); + EXPECT_EQ(typeid(ba),typeid(a)); + EXPECT_EQ(2.0,*ab); + EXPECT_EQ(1,*ba); + + auto zb=false & b; + EXPECT_EQ(typeid(zb),typeid(b)); + EXPECT_FALSE((bool)zb); + + auto b3=b & 3; + EXPECT_EQ(typeid(b3),typeid(optional<int>)); + EXPECT_TRUE((bool)b3); + EXPECT_EQ(3,*b3); +} + +TEST(optionalm,provided) { + std::array<int,3> qs={1,0,3}; + std::array<int,3> ps={14,14,14}; + std::array<int,3> rs; + + std::transform(ps.begin(),ps.end(),qs.begin(),rs.begin(), + [](int p,int q) { return *( provided(q!=0) >> [=]() { return p/q; } | -1 ); }); + + EXPECT_EQ(14,rs[0]); + EXPECT_EQ(-1,rs[1]); + EXPECT_EQ(4,rs[2]); +} diff --git a/tests/test_uninitialized.cpp b/tests/test_uninitialized.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dcc9e47ce6a7fa266376e054e8f84a04d8ec818d --- /dev/null +++ b/tests/test_uninitialized.cpp @@ -0,0 +1,200 @@ +#include "gtest.h" + +#include "util/uninitialized.hpp" + +using namespace nest::mc::util; + +namespace { + struct count_ops { + count_ops() {} + count_ops(const count_ops &n) { ++copy_ctor_count; } + count_ops(count_ops &&n) { ++move_ctor_count; } + + count_ops &operator=(const count_ops &n) { ++copy_assign_count; return *this; } + count_ops &operator=(count_ops &&n) { ++move_assign_count; return *this; } + + static int copy_ctor_count,copy_assign_count; + static int move_ctor_count,move_assign_count; + static void reset_counts() { + copy_ctor_count=copy_assign_count=0; + move_ctor_count=move_assign_count=0; + } + }; + + int count_ops::copy_ctor_count=0; + int count_ops::copy_assign_count=0; + int count_ops::move_ctor_count=0; + int count_ops::move_assign_count=0; +} + +TEST(uninitialized,ctor) { + count_ops::reset_counts(); + + uninitialized<count_ops> ua; + ua.construct(count_ops{}); + + count_ops b; + ua.construct(b); + + EXPECT_EQ(1,count_ops::copy_ctor_count); + EXPECT_EQ(0,count_ops::copy_assign_count); + EXPECT_EQ(1,count_ops::move_ctor_count); + EXPECT_EQ(0,count_ops::move_assign_count); + + ua.ref()=count_ops{}; + ua.ref()=b; + + EXPECT_EQ(1,count_ops::copy_ctor_count); + EXPECT_EQ(1,count_ops::copy_assign_count); + EXPECT_EQ(1,count_ops::move_ctor_count); + EXPECT_EQ(1,count_ops::move_assign_count); +} + +namespace { + struct nocopy { + nocopy() {} + nocopy(const nocopy &n) = delete; + nocopy(nocopy &&n) { ++move_ctor_count; } + + nocopy &operator=(const nocopy &n) = delete; + nocopy &operator=(nocopy &&n) { ++move_assign_count; return *this; } + + static int move_ctor_count,move_assign_count; + static void reset_counts() { move_ctor_count=move_assign_count=0; } + }; + + int nocopy::move_ctor_count=0; + int nocopy::move_assign_count=0; +} + +TEST(uninitialized,ctor_nocopy) { + nocopy::reset_counts(); + + uninitialized<nocopy> ua; + ua.construct(nocopy{}); + + EXPECT_EQ(1,nocopy::move_ctor_count); + EXPECT_EQ(0,nocopy::move_assign_count); + + ua.ref()=nocopy{}; + + EXPECT_EQ(1,nocopy::move_ctor_count); + EXPECT_EQ(1,nocopy::move_assign_count); +} + +namespace { + struct nomove { + nomove() {} + nomove(const nomove &n) { ++copy_ctor_count; } + nomove(nomove &&n) = delete; + + nomove &operator=(const nomove &n) { ++copy_assign_count; return *this; } + nomove &operator=(nomove &&n) = delete; + + static int copy_ctor_count,copy_assign_count; + static void reset_counts() { copy_ctor_count=copy_assign_count=0; } + }; + + int nomove::copy_ctor_count=0; + int nomove::copy_assign_count=0; +} + +TEST(uninitialized,ctor_nomove) { + nomove::reset_counts(); + + uninitialized<nomove> ua; + ua.construct(nomove{}); // check against rvalue + + nomove b; + ua.construct(b); // check against non-const lvalue + + const nomove c; + ua.construct(c); // check against const lvalue + + EXPECT_EQ(3,nomove::copy_ctor_count); + EXPECT_EQ(0,nomove::copy_assign_count); + + nomove a; + ua.ref()=a; + + EXPECT_EQ(3,nomove::copy_ctor_count); + EXPECT_EQ(1,nomove::copy_assign_count); +} + +TEST(uninitialized,void) { + uninitialized<void> a,b; + a=b; + + EXPECT_EQ(typeid(a.ref()),typeid(void)); +} + +TEST(uninitialized,ref) { + uninitialized<int &> x,y; + int a; + + x.construct(a); + y=x; + + x.ref()=2; + EXPECT_EQ(2,a); + + y.ref()=3; + EXPECT_EQ(3,a); + EXPECT_EQ(3,x.cref()); + + EXPECT_EQ(&a,x.ptr()); + EXPECT_EQ((const int *)&a,x.cptr()); +} + +namespace { + struct apply_tester { + mutable int op_count=0; + mutable int const_op_count=0; + + int operator()(const int &a) const { ++const_op_count; return a+1; } + int operator()(int &a) const { ++op_count; return ++a; } + }; +} + +TEST(uninitialized,apply) { + uninitialized<int> ua; + ua.construct(10); + + apply_tester A; + int r=ua.apply(A); + EXPECT_EQ(11,ua.cref()); + EXPECT_EQ(11,r); + + uninitialized<int &> ub; + ub.construct(ua.ref()); + + r=ub.apply(A); + EXPECT_EQ(12,ua.cref()); + EXPECT_EQ(12,r); + + uninitialized<const int &> uc; + uc.construct(ua.ref()); + + r=uc.apply(A); + EXPECT_EQ(12,ua.cref()); + EXPECT_EQ(13,r); + + const uninitialized<int> ud(ua); + + r=ud.apply(A); + EXPECT_EQ(12,ua.cref()); + EXPECT_EQ(12,ud.cref()); + EXPECT_EQ(13,r); + + EXPECT_EQ(2,A.op_count); + EXPECT_EQ(2,A.const_op_count); +} + +TEST(uninitialized,void_apply) { + uninitialized<void> uv; + + auto f=[]() { return 11; }; + EXPECT_EQ(11,uv.apply(f)); + + EXPECT_EQ(12.5,uv.apply([]() { return 12.5; })); +} diff --git a/tests/util.hpp b/tests/util.hpp index 6252c7e439b334342c14b17285418a0e1f7a1c63..cfa89896af8039e6188d8fbc460f412d3407a1b3 100644 --- a/tests/util.hpp +++ b/tests/util.hpp @@ -60,7 +60,7 @@ nlohmann::json load_spike_data(const std::string& input_name) { nlohmann::json cell_data; - auto fid = std::ifstream(input_name); + std::ifstream fid(input_name); if(!fid.is_open()) { std::cerr << "error : unable to open file " << input_name << " : run the validation generation script first\n";