diff --git a/src/math.hpp b/src/math.hpp index bf964ba38c07487914ffcf5768b7f39aee63a5d1..36767bcbc20c57a581210c8a3b5ea831aafbde0b 100644 --- a/src/math.hpp +++ b/src/math.hpp @@ -11,7 +11,7 @@ namespace math { template <typename T> T constexpr pi() { - return T(3.1415926535897932384626433832795); + return T(3.1415926535897932384626433832795l); } template <typename T = float> diff --git a/src/util/filter.hpp b/src/util/filter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6da37f57c175dacfd1a989c4e39a3da6544f47f8 --- /dev/null +++ b/src/util/filter.hpp @@ -0,0 +1,244 @@ +#pragma once + +/* + * An iterator adaptor that lazily skips items not matching a predicate. + */ + +#include <iterator> +#include <memory> +#include <type_traits> + +#include <util/iterutil.hpp> +#include <util/meta.hpp> +#include <util/range.hpp> + +#include <util/debug.hpp> + +namespace nest { +namespace mc { +namespace util { + +namespace impl { + template <typename I, bool = std::is_pointer<I>::value> + struct arrow { + using type = decltype(std::declval<I>().operator->()); + static type eval(I& x) { return x.operator->(); } + }; + + template <typename I> + struct arrow<I, true> { + using type = I; + static type eval(I& x) { return x; } + }; +} + +/* + * Iterate through a sequence such that dereference only + * gives items from the range that satisfy a given predicate. + * + * Type parameters: + * I Iterator type + * S Sentinel type compatible with I + * F Functional object + * + * The underlying sequence is described by an iterator of type + * I and a sentinel of type S. The predicate has type F. + */ + +template <typename I, typename S, typename F> +class filter_iterator { + mutable I inner_; + S end_; + mutable bool ok_; + + // F may be a lambda type, and thus non-copy assignable. The + // use of `uninitalized` allows us to work around this limitation; + // f_ will always be in an initalized state post-construction. + mutable uninitialized<F> f_; + + void advance() const { + if (ok_) return; + + for (;;) { + ok_ = inner_==end_ || f_.ref()(*inner_); + if (ok_) break; + ++inner_; + } + } + +public: + using value_type = typename std::iterator_traits<I>::value_type; + using difference_type = typename std::iterator_traits<I>::difference_type; + using iterator_category = typename std::conditional< + is_forward_iterator<I>::value, + std::forward_iterator_tag, + std::input_iterator_tag + >::type; + + using pointer = typename std::iterator_traits<I>::pointer; + using reference = typename std::iterator_traits<I>::reference; + + filter_iterator(): ok_{inner_==end_} {} + + template <typename J, typename K, typename G> + filter_iterator(J&& iter, K&& end, G&& f): + inner_(std::forward<J>(iter)), + end_(std::forward<K>(end)), + ok_{inner_==end_} + { + f_.construct(std::forward<G>(f)); + } + + filter_iterator(const filter_iterator& other): + inner_(other.inner_), + end_(other.end_), + ok_{other.ok_} + { + f_.construct(other.f_.cref()); + } + + filter_iterator(filter_iterator&& other): + inner_(std::move(other.inner_)), + end_(std::move(other.end_)), + ok_{other.ok_} + { + f_.construct(std::move(other.f_.ref())); + } + + filter_iterator& operator=(filter_iterator&& other) { + if (this!=&other) { + inner_ = std::move(other.inner_); + end_ = std::move(other.end_); + ok_ = other.ok_; + f_.construct(std::move(other.f_.ref())); + } + return *this; + } + + filter_iterator& operator=(const filter_iterator& other) { + if (this!=&other) { + inner_ = other.inner_; + end_ = other.end_; + ok_ = other.ok_; + f_.destruct(); + f_.construct(other.f_.cref()); + } + return *this; + } + + // forward and input iterator requirements + + auto operator*() const -> decltype(*(this->inner_)) { + advance(); + return *inner_; + } + + typename impl::arrow<I>::type + operator->() const { + advance(); + return impl::arrow<I>::eval(inner_); + } + + filter_iterator& operator++() { + advance(); + ok_ = false; + ++inner_; + return *this; + } + + filter_iterator operator++(int) { + auto c(*this); + ++*this; + advance(); + return c; + } + + bool operator==(const filter_iterator& other) const { + advance(); + other.advance(); + return inner_==other.inner_; + } + + bool operator!=(const filter_iterator& other) const { + return !(*this==other); + } + + // expose inner iterator for testing against a sentinel + template <typename Sentinel> + bool operator==(const Sentinel& s) const { + advance(); + return inner_==s; + } + + template <typename Sentinel> + bool operator!=(const Sentinel& s) const { return !(inner_==s); } + + // public access to inner iterator + const I& get() const { + advance(); + return inner_; + } +}; + +template <typename I, typename S, typename F> +filter_iterator<I, S, util::decay_t<F>> make_filter_iterator(const I& i, const S& end, const F& f) { + return filter_iterator<I, S, util::decay_t<F>>(i, end, f); +} + +// filter over const and non-const regular sequences: + +template < + typename Seq, + typename F, + typename seq_iter = typename sequence_traits<Seq>::iterator, + typename seq_sent = typename sequence_traits<Seq>::sentinel, + typename = enable_if_t<std::is_same<seq_iter, seq_sent>::value> +> +range<filter_iterator<seq_iter, seq_iter, util::decay_t<F>>> +filter(Seq& s, const F& f) { + return {make_filter_iterator(std::begin(s), std::end(s), f), + make_filter_iterator(std::end(s), std::end(s), f)}; +} + +template < + typename Seq, + typename F, + typename seq_citer = typename sequence_traits<Seq>::const_iterator, + typename seq_csent = typename sequence_traits<Seq>::const_sentinel, + typename = enable_if_t<std::is_same<seq_citer, seq_csent>::value> +> +range<filter_iterator<seq_citer, seq_citer, util::decay_t<F>>> +filter(const Seq& s, const F& f) { + return {make_filter_iterator(cbegin(s), cend(s), f), + make_filter_iterator(cend(s), cend(s), f)}; +} + +// filter over const and non-const sentinel-terminated sequences: + +template < + typename Seq, + typename F, + typename seq_iter = typename sequence_traits<Seq>::iterator, + typename seq_sent = typename sequence_traits<Seq>::sentinel, + typename = enable_if_t<!std::is_same<seq_iter, seq_sent>::value> +> +range<filter_iterator<seq_iter, seq_sent, util::decay_t<F>>, seq_sent> +filter(Seq& s, const F& f) { + return {make_filter_iterator(std::begin(s), std::end(s), f), std::end(s)}; +} + +template < + typename Seq, + typename F, + typename seq_citer = typename sequence_traits<Seq>::const_iterator, + typename seq_csent = typename sequence_traits<Seq>::const_sentinel, + typename = enable_if_t<!std::is_same<seq_citer, seq_csent>::value> +> +range<filter_iterator<seq_citer, seq_csent, util::decay_t<F>>, seq_csent> +filter(const Seq& s, const F& f) { + return {make_filter_iterator(cbegin(s), cend(s), f), cend(s)}; +} + +} // namespace util +} // namespace mc +} // namespace nest diff --git a/src/util/iterutil.hpp b/src/util/iterutil.hpp index 685398579a14c32971911087d772182d17b3d12d..8c327d4140c24790ca7576fafb190932508524e6 100644 --- a/src/util/iterutil.hpp +++ b/src/util/iterutil.hpp @@ -67,6 +67,20 @@ distance(I first, E last) { return ret; } +/* + * generic front() and back() methods for containers or ranges + */ + +template <typename Seq> +auto front(Seq& seq) -> decltype(*std::begin(seq)) { + return *std::begin(seq); +} + +template <typename Seq> +auto back(Seq& seq) -> decltype(*std::begin(seq)) { + return *upto(std::begin(seq), std::end(seq)); +} + /* * Provide a proxy object for operator->() for iterator adaptors that * present rvalues on dereference. diff --git a/src/util/rangeutil.hpp b/src/util/rangeutil.hpp index 839fb4527be34cf04777c65d7e934712aa7c57e3..bc47dec20767f05663283812ecd12caed831eb27 100644 --- a/src/util/rangeutil.hpp +++ b/src/util/rangeutil.hpp @@ -120,6 +120,47 @@ sort_by(const Seq& seq, const Proj& proj) { }); } +// Stable sort in-place by projection `proj` + +template <typename Seq, typename Proj> +enable_if_t<!std::is_const<typename sequence_traits<Seq>::reference>::value> +stable_sort_by(Seq& seq, const Proj& proj) { + using value_type = typename sequence_traits<Seq>::value_type; + auto canon = canonical_view(seq); + + std::stable_sort(std::begin(canon), std::end(canon), + [&proj](const value_type& a, const value_type& b) { + return proj(a) < proj(b); + }); +} + +template <typename Seq, typename Proj> +enable_if_t<!std::is_const<typename sequence_traits<Seq>::reference>::value> +stable_sort_by(const Seq& seq, const Proj& proj) { + using value_type = typename sequence_traits<Seq>::value_type; + auto canon = canonical_view(seq); + + std::stable_sort(std::begin(canon), std::end(canon), + [&proj](const value_type& a, const value_type& b) { + return proj(a) < proj(b); + }); +} + +// Range-interface for `all_of`, `any_of` + +template <typename Seq, typename Predicate> +bool all_of(const Seq& seq, const Predicate& pred) { + auto canon = canonical_view(seq); + return std::all_of(std::begin(canon), std::end(canon), pred); +} + +template <typename Seq, typename Predicate> +bool any_of(const Seq& seq, const Predicate& pred) { + auto canon = canonical_view(seq); + return std::any_of(std::begin(canon), std::end(canon), pred); +} + + // Accumulate by projection `proj` template < diff --git a/src/util/transform.hpp b/src/util/transform.hpp index ec86ea71d5c46f053711783e21795a78d3904a41..71c49e723017deb92258dc02be21e66da7dd9118 100644 --- a/src/util/transform.hpp +++ b/src/util/transform.hpp @@ -26,7 +26,10 @@ class transform_iterator: public iterator_adaptor<transform_iterator<I, F>, I> { friend class iterator_adaptor<transform_iterator<I, F>, I>; I inner_; - uninitialized<F> f_; // always in initialized state post-construction + + // F may be a lambda type, and thus non-copy assignable. The + // use of `uninitalized` allows us to work around this limitation; + uninitialized<F> f_; // provides access to inner iterator for adaptor. const I& inner() const { return inner_; } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 60ca6ab0572c74a580399364609d16b9c242adaa..f72a08b14359305e3eab73b220f90fece310eb68 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -7,6 +7,7 @@ set(TEST_SOURCES test_counter.cpp test_either.cpp test_event_queue.cpp + test_filter.cpp test_fvm.cpp test_fvm_multi.cpp test_cell_group.cpp diff --git a/tests/unit/common.hpp b/tests/unit/common.hpp new file mode 100644 index 0000000000000000000000000000000000000000..687bfef82e4f7ac895e17fdce17b0a5e64f77d38 --- /dev/null +++ b/tests/unit/common.hpp @@ -0,0 +1,109 @@ +#pragma once + +/* + * Convenience functions, structs used across + * more than one unit test. + */ + +namespace testing { + +// sentinel for use with range-related tests + +struct null_terminated_t { + bool operator==(const char *p) const { return !*p; } + bool operator!=(const char *p) const { return !!*p; } + + friend bool operator==(const char *p, null_terminated_t x) { + return x==p; + } + + friend bool operator!=(const char *p, null_terminated_t x) { + return x!=p; + } + + constexpr null_terminated_t() {} +}; + +constexpr null_terminated_t null_terminated; + +// wrap a value type, with copy operations disabled + +template <typename V> +struct nocopy { + V value; + + nocopy(): value{} {} + nocopy(V v): value(v) {} + nocopy(const nocopy& n) = delete; + + nocopy(nocopy&& n) { + value=n.value; + n.value=V{}; + ++move_ctor_count; + } + + nocopy& operator=(const nocopy& n) = delete; + nocopy& operator=(nocopy&& n) { + value=n.value; + n.value=V{}; + ++move_assign_count; + return *this; + } + + bool operator==(const nocopy& them) const { return them.value==value; } + bool operator!=(const nocopy& them) const { return !(*this==them); } + + static int move_ctor_count; + static int move_assign_count; + static void reset_counts() { + move_ctor_count = 0; + move_assign_count = 0; + } +}; + +template <typename V> +int nocopy<V>::move_ctor_count; + +template <typename V> +int nocopy<V>::move_assign_count; + +// wrap a value type, with move operations disabled + +template <typename V> +struct nomove { + V value; + + nomove(): value{} {} + nomove(V v): value(v) {} + nomove(nomove&& n) = delete; + + nomove(const nomove& n): value(n.value) { + ++copy_ctor_count; + } + + nomove& operator=(nomove&& n) = delete; + + nomove& operator=(const nomove& n) { + value=n.value; + ++copy_assign_count; + return *this; + } + + bool operator==(const nomove& them) const { return them.value==value; } + bool operator!=(const nomove& them) const { return !(*this==them); } + + static int copy_ctor_count; + static int copy_assign_count; + static void reset_counts() { + copy_ctor_count = 0; + copy_assign_count = 0; + } +}; + +template <typename V> +int nomove<V>::copy_ctor_count; + +template <typename V> +int nomove<V>::copy_assign_count; + +} // namespace testing diff --git a/tests/unit/test_filter.cpp b/tests/unit/test_filter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d357edd3c53fc62c1618f4173935bec27179af6 --- /dev/null +++ b/tests/unit/test_filter.cpp @@ -0,0 +1,96 @@ +#include "gtest.h" + +#include <cstring> +#include <list> + +#include <util/range.hpp> +#include <util/rangeutil.hpp> +#include <util/filter.hpp> +#include <util/transform.hpp> + +#include "common.hpp" + +using namespace nest::mc; +using util::filter; +using util::assign; +using util::canonical_view; + +util::range<const char*, testing::null_terminated_t> +cstring(const char* s) { + return {s, testing::null_terminated}; +} + +util::range<char*, testing::null_terminated_t> +cstring(char* s) { + return {s, testing::null_terminated}; +} + +TEST(filter, const_regular) { + std::list<int> ten = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::list<int> odd = {1, 3, 5, 7, 9}; + std::list<int> even = {2, 4, 6, 8, 10}; + std::list<int> check; + + assign(check, filter(ten, [](int i) { return i%2!=0; })); + EXPECT_EQ(odd, check); + + assign(check, filter(ten, [](int i) { return i%2==0; })); + EXPECT_EQ(even, check); +} + +TEST(filter, const_sentinel) { + auto xyzzy = cstring("xyzzy"); + std::string check; + + assign(check, filter(xyzzy, [](char c) { return c!='y'; })); + EXPECT_EQ(check, "xzz"); + + assign(check, filter(xyzzy, [](char c) { return c!='x'; })); + EXPECT_EQ(check, "yzzy"); +} + +TEST(filter, modify_regular) { + int nums[] = {1, -2, 3, -4, -5}; + + for (auto& n: filter(nums, [](int i) { return i<0; })) { + n *= n; + } + + int check[] = {1, 4, 3, 16, 25}; + for (unsigned i = 0; i<5; ++i) { + EXPECT_EQ(check[i], nums[i]); + } +} + +TEST(filter, modify_sentinel) { + char xoxo[] = "xoxo"; + for (auto& c: canonical_view(filter(cstring(xoxo), [](char c) { return c=='o'; }))) { + c = 'q'; + } + + EXPECT_TRUE(!std::strcmp(xoxo, "xqxq")); +} + + +TEST(filter, laziness) { + std::list<int> ten = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + int derefs = 0; + auto count_derefs = [&derefs](int i) { ++derefs; return i; }; + + auto odd = filter(util::transform_view(ten, count_derefs), + [](int i) { return i%2!=0; }); + + auto iter = std::begin(odd); + + EXPECT_EQ(0, derefs); + + EXPECT_EQ(1, *iter); + EXPECT_EQ(2, derefs); // one for check, one for operator*() + ++iter; // does not need to scan ahead + EXPECT_EQ(2, derefs); + + ++iter; // now it does need to scan ahead, testing values 2 and 3. + EXPECT_EQ(4, derefs); + EXPECT_EQ(5, *iter); +} diff --git a/tests/unit/test_math.cpp b/tests/unit/test_math.cpp index b32bd194cb75b9df1c31c34c0323fdc0b95bce9e..5dff6c81e950d901012ffe4dcce995c02a3b5cd3 100644 --- a/tests/unit/test_math.cpp +++ b/tests/unit/test_math.cpp @@ -6,6 +6,78 @@ using namespace nest::mc::math; +TEST(math, pi) { + // check regression against long double literal in implementation + auto pi_ld = pi<long double>(); + auto pi_d = pi<double>(); + + if (std::numeric_limits<long double>::digits>std::numeric_limits<double>::digits) { + EXPECT_NE(0.0, pi_ld-pi_d); + } + else { + EXPECT_EQ(0.0, pi_ld-pi_d); + } + + // library quality of implementation dependent, but expect cos(pi) to be within + // 1 epsilon of -1. + + auto eps_d = std::numeric_limits<double>::epsilon(); + auto cos_pi_d = std::cos(pi_d); + EXPECT_LE(-1.0-eps_d, cos_pi_d); + EXPECT_GE(-1.0+eps_d, cos_pi_d); + + auto eps_ld = std::numeric_limits<long double>::epsilon(); + auto cos_pi_ld = std::cos(pi_ld); + EXPECT_LE(-1.0-eps_ld, cos_pi_ld); + EXPECT_GE(-1.0+eps_ld, cos_pi_ld); +} + +TEST(math, lerp) { + // expect exact computation when u is zero or one + double a = 1.0/3; + double b = 11.0/7; + + EXPECT_EQ(a, lerp(a, b, 0.)); + EXPECT_EQ(b, lerp(a, b, 1.)); + + // expect exact computation here as well + EXPECT_EQ(2.75, lerp(2.0, 3.0, 0.75)); + + // and otherwise to be close + EXPECT_DOUBLE_EQ(100.101, lerp(100.1, 200.1, 0.00001)); + EXPECT_DOUBLE_EQ(200.099, lerp(100.1, 200.1, 0.99999)); + + // should be able to lerp with differing types for end points and u + EXPECT_EQ(0.25f, lerp(0.f, 1.f, 0.25)); +} + +TEST(math, frustrum) { + // cross check against cone calculation + auto cone_area = [](double l, double r) { + return std::hypot(l,r)*r*pi<double>(); + }; + + auto cone_volume = [](double l, double r) { + return pi<double>()*square(r)*l/3.0; + }; + + EXPECT_DOUBLE_EQ(cone_area(5.0, 1.3), area_frustrum(5.0, 0.0, 1.3)); + EXPECT_DOUBLE_EQ(cone_volume(5.0, 1.3), volume_frustrum(5.0, 0.0, 1.3)); + + double r1 = 7.0; + double r2 = 9.0; + double l = 11.0; + + double s = l*r2/(r2-r1); + double ca = cone_area(s, r2)-cone_area(s-l, r1); + double cv = cone_volume(s, r2)-cone_volume(s-l, r1); + + EXPECT_DOUBLE_EQ(ca, area_frustrum(l, r1, r2)); + EXPECT_DOUBLE_EQ(ca, area_frustrum(l, r2, r1)); + EXPECT_DOUBLE_EQ(cv, volume_frustrum(l, r1, r2)); + EXPECT_DOUBLE_EQ(cv, volume_frustrum(l, r2, r1)); +} + TEST(math, infinity) { // check values for float, double, long double auto finf = infinity<float>(); diff --git a/tests/unit/test_optional.cpp b/tests/unit/test_optional.cpp index fe34cc3dc1278f02b866aefc3c0b8942a1169d98..4ada9ca81730e28aa612b2899e94eebbcfdb32aa 100644 --- a/tests/unit/test_optional.cpp +++ b/tests/unit/test_optional.cpp @@ -4,6 +4,7 @@ #include "gtest.h" #include "util/optional.hpp" +#include "common.hpp" using namespace nest::mc::util; @@ -128,21 +129,9 @@ TEST(optionalm,assign_reference) { EXPECT_EQ(&br, &check_rval2); } -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) { + using nomove = testing::nomove<int>; + optional<nomove> a(nomove(3)); EXPECT_EQ(nomove(3),a.get()); @@ -154,38 +143,25 @@ TEST(optionalm,ctor_nomove) { EXPECT_EQ(nomove(4),b.get()); } -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) { + using nocopy = testing::nocopy<int>; + optional<nocopy> a(nocopy(5)); EXPECT_EQ(nocopy(5),a.get()); + nocopy::reset_counts(); optional<nocopy> b(std::move(a)); EXPECT_EQ(nocopy(5),b.get()); EXPECT_EQ(0,a.get().value); + EXPECT_EQ(1, nocopy::move_ctor_count); + EXPECT_EQ(0, nocopy::move_assign_count); + nocopy::reset_counts(); b=optional<nocopy>(nocopy(6)); EXPECT_EQ(nocopy(6),b.get()); + EXPECT_EQ(1, nocopy::move_ctor_count); + EXPECT_EQ(1, nocopy::move_assign_count); + } static optional<double> odd_half(int n) { diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp index f486d2239c80f183a73f949ee624657902f14a75..2acf141253e42c01cf57f3c8798b4aa3eb741d3a 100644 --- a/tests/unit/test_range.cpp +++ b/tests/unit/test_range.cpp @@ -18,7 +18,10 @@ #include <util/sentinel.hpp> #include <util/transform.hpp> +#include "common.hpp" + using namespace nest::mc; +using testing::null_terminated; TEST(range, list_iterator) { std::list<int> l = { 2, 4, 6, 8, 10 }; @@ -143,23 +146,6 @@ TEST(range, const_iterator) { EXPECT_TRUE((std::is_same<const int&, decltype(r_const.front())>::value)); } -struct null_terminated_t { - bool operator==(const char *p) const { return !*p; } - bool operator!=(const char *p) const { return !!*p; } - - friend bool operator==(const char *p, null_terminated_t x) { - return x==p; - } - - friend bool operator!=(const char *p, null_terminated_t x) { - return x!=p; - } - - constexpr null_terminated_t() {} -}; - -constexpr null_terminated_t null_terminated; - TEST(range, sentinel) { const char *cstr = "hello world"; std::string s; @@ -345,6 +331,17 @@ TEST(range, sort) { // reverse sort by transform c to -c util::sort_by(util::strict_view(cstr_range), [](char c) { return -c; }); EXPECT_EQ(std::string("ywohd"), cstr); + + // stable sort: move capitals to front, numbers to back + auto rank = [](char c) { + return std::isupper(c)? 0: std::isdigit(c)? 2: 1; + }; + + char mixed[] = "t5hH4E3erLL2e1O"; + auto mixed_range = util::make_range(std::begin(mixed), null_terminated); + + util::stable_sort_by(util::strict_view(mixed_range), rank); + EXPECT_EQ(std::string("HELLOthere54321"), mixed); } TEST(range, sum_by) { @@ -361,6 +358,37 @@ TEST(range, sum_by) { EXPECT_EQ(10u, count); } +TEST(range, all_of_any_of) { + // make a C string into a sentinel-terminated range + auto cstr = [](const char* s) { return util::make_range(s, null_terminated); }; + + // predicate throws on finding 'x' in order to check + // early stop criterion. + auto pred = [](char c) { return c=='x'? throw c:c<'5'; }; + + // all + EXPECT_TRUE(util::all_of(std::string(), pred)); + EXPECT_TRUE(util::all_of(std::string("1234"), pred)); + EXPECT_FALSE(util::all_of(std::string("12345"), pred)); + EXPECT_FALSE(util::all_of(std::string("12345x"), pred)); + + EXPECT_TRUE(util::all_of(cstr(""), pred)); + EXPECT_TRUE(util::all_of(cstr("1234"), pred)); + EXPECT_FALSE(util::all_of(cstr("12345"), pred)); + EXPECT_FALSE(util::all_of(cstr("12345x"), pred)); + + // any + EXPECT_FALSE(util::any_of(std::string(), pred)); + EXPECT_FALSE(util::any_of(std::string("8765"), pred)); + EXPECT_TRUE(util::any_of(std::string("87654"), pred)); + EXPECT_TRUE(util::any_of(std::string("87654x"), pred)); + + EXPECT_FALSE(util::any_of(cstr(""), pred)); + EXPECT_FALSE(util::any_of(cstr("8765"), pred)); + EXPECT_TRUE(util::any_of(cstr("87654"), pred)); + EXPECT_TRUE(util::any_of(cstr("87654x"), pred)); +} + #ifdef WITH_TBB TEST(range, tbb_split) { diff --git a/tests/unit/test_uninitialized.cpp b/tests/unit/test_uninitialized.cpp index 1f04510d7df6af054eaec854d7e0c954ad5e73c8..718eaface74da5e7ac27812bc6292a64b538af2d 100644 --- a/tests/unit/test_uninitialized.cpp +++ b/tests/unit/test_uninitialized.cpp @@ -1,6 +1,7 @@ #include "gtest.h" #include "util/uninitialized.hpp" +#include "common.hpp" using namespace nest::mc::util; @@ -50,24 +51,8 @@ TEST(uninitialized,ctor) { 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) { + using nocopy = testing::nocopy<int>; nocopy::reset_counts(); uninitialized<nocopy> ua; @@ -82,24 +67,8 @@ TEST(uninitialized,ctor_nocopy) { 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) { + using nomove = testing::nomove<int>; nomove::reset_counts(); uninitialized<nomove> ua;