diff --git a/arbor/backends/gpu/mechanism.cpp b/arbor/backends/gpu/mechanism.cpp index 5aa114ecf81bc04f6f4f99661cee16dab774d0f4..fc175c65604cf840e8933338b2a30502d4afe85d 100644 --- a/arbor/backends/gpu/mechanism.cpp +++ b/arbor/backends/gpu/mechanism.cpp @@ -158,6 +158,7 @@ void mechanism::instantiate(unsigned id, auto ion_binding = value_by_key(overrides.ion_rebind, ion_index_tbl[i].first).value_or(ion_index_tbl[i].first); ion_state* oion = ptr_by_key(shared.ion_data, ion_binding); + if (!oion) { throw arbor_internal_error("gpu/mechanism: mechanism holds ion with no corresponding shared state"); } diff --git a/arbor/include/arbor/simple_sampler.hpp b/arbor/include/arbor/simple_sampler.hpp index 2776a931a27dfe9f6582f940f8edea53c6beb9e4..ca227fee8d341cb5e7d1c8b02d2e403dd24d8867 100644 --- a/arbor/include/arbor/simple_sampler.hpp +++ b/arbor/include/arbor/simple_sampler.hpp @@ -129,51 +129,42 @@ class simple_sampler { public: explicit simple_sampler(trace_vector<V, Meta>& trace): trace_(trace) {} - // TODO: C++17 use if constexpr to test for Meta = void case. void operator()(probe_metadata pm, std::size_t n, const sample_record* recs) { - const Meta* m = util::any_cast<const Meta*>(pm.meta); - if (!m) { - throw std::runtime_error("unexpected metadata type in simple_sampler"); - } - - if (trace_.size()<=pm.index) { - trace_.resize(pm.index+1); - } - - if (trace_[pm.index].empty()) { - trace_[pm.index].meta = *m; - } + if constexpr (std::is_void_v<Meta>) { + if (trace_.size()<=pm.index) { + trace_.resize(pm.index+1); + } - for (std::size_t i = 0; i<n; ++i) { - if (!trace_push_back<V>::push_back(trace_[pm.index], recs[i])) { - throw std::runtime_error("unexpected sample type in simple_sampler"); + for (std::size_t i = 0; i<n; ++i) { + if (!trace_push_back<V>::push_back(trace_[pm.index], recs[i])) { + throw std::runtime_error("unexpected sample type in simple_sampler"); + } } } - } - -private: - trace_vector<V, Meta>& trace_; -}; + else { + const Meta* m = util::any_cast<const Meta*>(pm.meta); + if (!m) { + throw std::runtime_error("unexpected metadata type in simple_sampler"); + } -template <typename V> -class simple_sampler<V, void> { -public: - explicit simple_sampler(trace_vector<V, void>& trace): trace_(trace) {} + if (trace_.size()<=pm.index) { + trace_.resize(pm.index+1); + } - void operator()(probe_metadata pm, std::size_t n, const sample_record* recs) { - if (trace_.size()<=pm.index) { - trace_.resize(pm.index+1); - } + if (trace_[pm.index].empty()) { + trace_[pm.index].meta = *m; + } - for (std::size_t i = 0; i<n; ++i) { - if (!trace_push_back<V>::push_back(trace_[pm.index], recs[i])) { - throw std::runtime_error("unexpected sample type in simple_sampler"); + for (std::size_t i = 0; i<n; ++i) { + if (!trace_push_back<V>::push_back(trace_[pm.index], recs[i])) { + throw std::runtime_error("unexpected sample type in simple_sampler"); + } } } } private: - trace_vector<V, void>& trace_; + trace_vector<V, Meta>& trace_; }; template <typename V, typename Meta> diff --git a/arbor/include/arbor/util/expected.hpp b/arbor/include/arbor/util/expected.hpp index dd90d523dcc7c31c4f69e9919a9343116494b75e..9f5d1a4307b3841b437beff302e232b2ff00f5aa 100644 --- a/arbor/include/arbor/util/expected.hpp +++ b/arbor/include/arbor/util/expected.hpp @@ -472,20 +472,7 @@ struct expected<void, E> { // Swap ops. void swap(expected& other) { - // TODO: C++17 just use std::optional::swap; haven't implemented util::optional::swap. - if (data_) { - if (other.data_) { - std::swap(*data_, *other.data_); - } - else { - other.data_ = std::move(data_); - data_.reset(); - } - } - else if (other.data_) { - data_ = std::move(other.data_); - other.data_.reset(); - } + data_.swap(other.data); } // Accessors. diff --git a/arbor/morph/embed_pwlin.cpp b/arbor/morph/embed_pwlin.cpp index b46afbe2a0991d2b773e0e5bb5df7c1361a90440..f419b436fe9510cf42f2e577a6cb39e87c4023f4 100644 --- a/arbor/morph/embed_pwlin.cpp +++ b/arbor/morph/embed_pwlin.cpp @@ -33,9 +33,7 @@ double interpolate(const branch_pw_ratpoly<p, q>& f, unsigned bid, double pos) { const auto& pw = f.at(bid); if (is_degenerate(pw)) pos = 0; - auto piece = pw(pos); - auto& bounds = piece.first; // TODO: C++17 structured binding. - auto& element = piece.second; + auto [bounds, element] = pw(pos); if (bounds.first==bounds.second) return element[0]; else { diff --git a/arbor/morph/place_pwlin.cpp b/arbor/morph/place_pwlin.cpp index 136fcf90dcec081c79b4877ec15c58f9f4830f06..37df48d8c04578e26f60c2965215d49e37cefa11 100644 --- a/arbor/morph/place_pwlin.cpp +++ b/arbor/morph/place_pwlin.cpp @@ -49,20 +49,20 @@ static bool is_degenerate(const util::pw_elements<Elem>& pw) { } mpoint place_pwlin::at(mlocation loc) const { - const auto& index = data_->segment_index.at(loc.branch); - double pos = is_degenerate(index)? 0: loc.pos; + const auto& pw_index = data_->segment_index.at(loc.branch); + double pos = is_degenerate(pw_index)? 0: loc.pos; - auto piece = index(pos); // Here and below, TODO: C++17 structured bindings for piece.first and second. - return interpolate_segment(piece.first, data_->segments.at(piece.second), pos); + auto [bounds, index] = pw_index(pos); + return interpolate_segment(bounds, data_->segments.at(index), pos); } std::vector<mpoint> place_pwlin::all_at(mlocation loc) const { std::vector<mpoint> result; - const auto& index = data_->segment_index.at(loc.branch); - double pos = is_degenerate(index)? 0: loc.pos; + const auto& pw_index = data_->segment_index.at(loc.branch); + double pos = is_degenerate(pw_index)? 0: loc.pos; - for (auto piece: util::make_range(index.equal_range(pos))) { - result.push_back(interpolate_segment(piece.first, data_->segments.at(piece.second), pos)); + for (auto [bounds, index]: util::make_range(pw_index.equal_range(pos))) { + result.push_back(interpolate_segment(bounds, data_->segments.at(index), pos)); } return result; } @@ -72,17 +72,16 @@ static std::vector<msegment> extent_segments_impl(const place_pwlin_data& data, std::vector<msegment> result; for (mcable c: extent) { - const auto& index = data.segment_index.at(c.branch); - if (is_degenerate(index)) { + const auto& pw_index = data.segment_index.at(c.branch); + if (is_degenerate(pw_index)) { c.prox_pos = c.dist_pos = 0; } - auto b = index.equal_range(c.prox_pos).first; - auto e = index.equal_range(c.dist_pos).second; + auto b = pw_index.equal_range(c.prox_pos).first; + auto e = pw_index.equal_range(c.dist_pos).second; - for (auto piece: util::make_range(b, e)) { - const auto& bounds = piece.first; - const msegment& seg = data.segments.at(piece.second); + for (const auto [bounds, index]: util::make_range(b, e)) { + const msegment& seg = data.segments.at(index); auto partial_bounds = bounds; msegment partial = seg; diff --git a/arbor/util/cycle.hpp b/arbor/util/cycle.hpp index 5664be0a29029ce30a713ea7b647e81c1400a394..905c59e0d360e9ac2ea2a77f28c5fba102eacab1 100644 --- a/arbor/util/cycle.hpp +++ b/arbor/util/cycle.hpp @@ -185,33 +185,22 @@ cyclic_iterator<I, S> make_cyclic_iterator(const I& iter, const S& sentinel) { } -// TODO C++17: simplify with constexpr-if -namespace cycle_impl { +template <typename Seq> +auto cyclic_view(Seq&& s) { using std::begin; using std::end; - // cycle over regular sequences: - template <typename Seq> - auto cycle_(Seq&& s, std::true_type) { - auto b = begin(s); - auto e = end(s); + auto b = begin(s); + auto e = end(s); + + if constexpr (is_regular_sequence_v<Seq&&>) { return make_range(make_cyclic_iterator(b, e), make_cyclic_iterator(e, e)); } - - // cycle over sentinel-terminated sequences: - template <typename Seq> - auto cycle_(Seq&& s, std::false_type) { - auto b = begin(s); - auto e = end(s); + else { return make_range(make_cyclic_iterator(b, e), e); } } -template <typename Seq> -auto cyclic_view(Seq&& s) { - return cycle_impl::cycle_(std::forward<Seq>(s), is_regular_sequence<Seq&&>{}); -} - // Handle initializer lists template <typename T> auto cyclic_view(const std::initializer_list<T>& list) { diff --git a/arbor/util/filter.hpp b/arbor/util/filter.hpp index 32ea5516d4855aa2840ab76230ffd2632f9c753c..ad5801f0e4fed8811f4f7ff5ff02074612404757 100644 --- a/arbor/util/filter.hpp +++ b/arbor/util/filter.hpp @@ -187,32 +187,21 @@ filter_iterator<I, S, std::decay_t<F>> make_filter_iterator(const I& i, const S& return filter_iterator<I, S, std::decay_t<F>>(i, end, f); } -// TODO C++17: simplify with constexpr-if -namespace filter_impl { +template <typename Seq, typename F> +auto filter(Seq&& s, const F& f) { using std::begin; using std::end; - // filter over regular sequences: - template <typename Seq, typename F> - auto filter_(Seq&& s, const F& f, std::true_type) { - auto b = begin(s); - auto e = end(s); + auto b = begin(s); + auto e = end(s); + + if constexpr (is_regular_sequence_v<Seq&&>) { return make_range(make_filter_iterator(b, e, f), make_filter_iterator(e, e, f)); } - - // filter over sentinel-terminated sequences: - template <typename Seq, typename F> - auto filter_(Seq&& s, const F& f, std::false_type) { - auto b = begin(s); - auto e = end(s); + else { return make_range(make_filter_iterator(b, e, f), e); } } -template <typename Seq, typename F> -auto filter(Seq&& s, const F& f) { - return filter_impl::filter_(std::forward<Seq>(s), f, is_regular_sequence<Seq&&>{}); -} - } // namespace util } // namespace arb diff --git a/arbor/util/index_into.hpp b/arbor/util/index_into.hpp index c78e706b4e6a2c2e6d950152e454ba050af13d0b..ecc0721bee14d92e2912801518037146c5c44632 100644 --- a/arbor/util/index_into.hpp +++ b/arbor/util/index_into.hpp @@ -32,9 +32,9 @@ struct index_into_iterator { using reference = const value_type&; using iterator_category = std::conditional_t< - std::is_same<Sup, SupEnd>::value - && is_bidirectional_iterator_t<Sup>::value - && is_bidirectional_iterator_t<Sub>::value, + std::is_same_v<Sup, SupEnd> + && is_bidirectional_iterator_v<Sup> + && is_bidirectional_iterator_v<Sub>, std::bidirectional_iterator_tag, std::forward_iterator_tag >; diff --git a/arbor/util/meta.hpp b/arbor/util/meta.hpp index e86a0a04b90745a53d803239c98eb60537e0d194..d9f30584fdd9aaf30499b2bf7e1805cc72c3dfbd 100644 --- a/arbor/util/meta.hpp +++ b/arbor/util/meta.hpp @@ -72,15 +72,24 @@ using sequence_traits = impl_seqtrait::sequence_traits<Seq>; template <typename T> using is_sequence = impl_seqtrait::is_sequence<T>; +template <typename T> +inline constexpr bool is_sequence_v = is_sequence<T>::value; + template <typename T> using enable_if_sequence_t = std::enable_if_t<util::is_sequence<T>::value>; template <typename T> using is_contiguous = std::integral_constant<bool, sequence_traits<T>::is_contiguous>; +template <typename T> +inline constexpr bool is_contiguous_v = is_contiguous<T>::value; + template <typename T> using is_regular_sequence = std::integral_constant<bool, sequence_traits<T>::is_regular>; +template <typename T> +inline constexpr bool is_regular_sequence_v = is_regular_sequence<T>::value; + // Convenience short cuts for `enable_if` template <typename T> @@ -122,7 +131,7 @@ struct is_iterator<T, std::void_t<typename std::iterator_traits<T>::iterator_cat public std::true_type {}; template <typename T> -using is_iterator_t = typename util::is_iterator<T>::type; +inline constexpr bool is_iterator_v = is_iterator<T>::value; // Random access iterator test @@ -137,7 +146,7 @@ struct is_random_access_iterator<T, std::enable_if_t< >> : public std::true_type {}; template <typename T> -using is_random_access_iterator_t = typename util::is_random_access_iterator<T>::type; +inline constexpr bool is_random_access_iterator_v = is_random_access_iterator<T>::value; // Bidirectional iterator test @@ -156,7 +165,7 @@ struct is_bidirectional_iterator<T, std::enable_if_t< >> : public std::true_type {}; template <typename T> -using is_bidirectional_iterator_t = typename util::is_bidirectional_iterator<T>::type; +inline constexpr bool is_bidirectional_iterator_v = is_bidirectional_iterator<T>::value; // Forward iterator test @@ -179,8 +188,7 @@ struct is_forward_iterator<T, std::enable_if_t< >> : public std::true_type {}; template <typename T> -using is_forward_iterator_t = typename util::is_forward_iterator<T>::type; - +inline constexpr bool is_forward_iterator_v = is_forward_iterator<T>::value; template <typename I, typename E, typename = void, typename = void> struct common_random_access_iterator {}; @@ -202,7 +210,7 @@ struct common_random_access_iterator< }; template <typename I, typename E> -using common_random_access_iterator_t = typename util::common_random_access_iterator<I, E>::type; +using common_random_access_iterator_t = typename common_random_access_iterator<I, E>::type; template <typename I, typename E, typename V=void> struct has_common_random_access_iterator: @@ -212,6 +220,9 @@ template <typename I, typename E> struct has_common_random_access_iterator<I, E, std::void_t<util::common_random_access_iterator_t<I, E>>>: std::true_type {}; +template <typename I, typename E> +inline constexpr bool has_common_random_access_iterator_v = has_common_random_access_iterator<I, E>::value; + // Generic accessors: // * first and second for pairs and tuples; // * util::get<I> to forward to std::get<I> where applicable, but @@ -220,11 +231,5 @@ struct has_common_random_access_iterator<I, E, std::void_t<util::common_random_a static auto first = [](auto&& pair) -> decltype(auto) { return std::get<0>(std::forward<decltype(pair)>(pair)); }; static auto second = [](auto&& pair) -> decltype(auto) { return std::get<1>(std::forward<decltype(pair)>(pair)); }; -template <typename X, typename U> -decltype(auto) get(U&& u) { return std::get<X>(std::forward<U>(u));} - -template <std::size_t I, typename U> -decltype(auto) get(U&& u) { return std::get<I>(std::forward<U>(u));} - } // namespace util } // namespace arb diff --git a/arbor/util/ratelem.hpp b/arbor/util/ratelem.hpp index 7a01cada42df3e244f7a1ca726cb8a87e52b9796..cbb2bfac455f2e5b88ddb97b874e874eab867dae 100644 --- a/arbor/util/ratelem.hpp +++ b/arbor/util/ratelem.hpp @@ -45,61 +45,56 @@ struct array_init_n<0, sz> { static void set(A& array) {} }; -// TODO: C++17. The following is much nicer with if constexpr... - -template <unsigned a, unsigned c, unsigned k, bool upper> -struct rat_eval { - static double eval(const std::array<double, 1+a+c>& g, double x) { - std::array<double, a+c> h; - interpolate(h, g, x); - return rat_eval<a-1, c, k+1, upper>::eval(h, g, x); - } - - static double eval(const std::array<double, 1+a+c>& g, const std::array<double, 2+a+c>&, double x) { - std::array<double, a+c> h; - interpolate(h, g, x); - return rat_eval<a-1, c, k+1, upper>::eval(h, g, x); - } - - static void interpolate(std::array<double, a+c>& h, const std::array<double, a+c+1>& g, double x) { - constexpr double ook = 1./k; - for (unsigned i = 0; i<a+c; ++i) { - if (upper) { - h[i] = ook*((x - i)*g[i+1] + (i+k - x)*g[i]); - } - else { - // Using h[i] = k/(g[i+1]/(x - i) + g[i]/(i+k - x)) is more robust to - // singularities, but the expense should not be necessary if we stick - // to strictly monotonic elements for rational polynomials. - h[i] = k*g[i]*g[i+1]/(g[i]*(x - i) + g[i+1]*(i+k - x)); - } +template <unsigned k, bool upper, std::size_t m> +void rat_interpolate(std::array<double, m>& h, const std::array<double, m+1>& g, double x) { + constexpr double ook = 1./k; + for (unsigned i = 0; i<m; ++i) { + if (upper) { + h[i] = ook*((x - i)*g[i+1] + (i+k - x)*g[i]); + } + else { + // Using h[i] = k/(g[i+1]/(x - i) + g[i]/(i+k - x)) is more robust to + // singularities, but the expense should not be necessary if we stick + // to strictly monotonic elements for rational polynomials. + h[i] = k*g[i]*g[i+1]/(g[i]*(x - i) + g[i+1]*(i+k - x)); } } -}; - -template <unsigned k, bool upper> -struct rat_eval<0, 0, k, upper> { - static double eval(const std::array<double, 1>& g, double) { - return g[0]; - } +} - static double eval(const std::array<double, 1>& g, const std::array<double, 2>&, double) { +template <unsigned a, unsigned c, unsigned k, bool upper> +double rat_eval(const std::array<double, 1+a+c>& g, const std::array<double, 2+a+c>& p, double x) { + if constexpr (a==0 && c==0) { return g[0]; } -}; - -template <unsigned c, unsigned k, bool upper> -struct rat_eval<0, c, k, upper> { - static double eval(const std::array<double, 1+c>& g, const std::array<double, 2+c>& p, double x) { + else if constexpr (a==0) { // 'rhombus' interpolation std::array<double, c> h; for (unsigned i = 0; i<c; ++i) { h[i] = p[i+1] + k/((x - i)/(g[i+1]-p[i+1]) + (i+k - x)/(g[i]-p[i+1])); } - return rat_eval<0, c-1, k+1, upper>::eval(h, g, x); + return rat_eval<0, c-1, k+1, upper>(h, g, x); } -}; + else { + std::array<double, a+c> h; + rat_interpolate<k, upper>(h, g, x); + return rat_eval<a-1, c, k+1, upper>(h, g, x); + } +} + + +template <unsigned a, unsigned c, unsigned k, bool upper> +double rat_eval(const std::array<double, 1+a+c>& g, double x) { + if constexpr (a==0 && c==0) { + return g[0]; + } + else { + std::array<double, a+c> h; + rat_interpolate<k, upper>(h, g, x); + return rat_eval<a-1, c, k+1, upper>(h, g, x); + } +} + } // namespace impl @@ -146,7 +141,7 @@ struct rat_element { constexpr unsigned a = upper? p-q+(q>0): q-p+(p>0); constexpr unsigned c = p+q-a; - return impl::rat_eval<a, c, 1, upper>::eval(data_, x*(p+q)); + return impl::rat_eval<a, c, 1, upper>(data_, x*(p+q)); } // Node values. diff --git a/arbor/util/sentinel.hpp b/arbor/util/sentinel.hpp index 22e46ffc622f0f41edc0253cfa2708dd66391da0..f93185e445b74edc2be0b9184ef4a264eb89fc98 100644 --- a/arbor/util/sentinel.hpp +++ b/arbor/util/sentinel.hpp @@ -31,22 +31,22 @@ class sentinel_iterator { I& iter() { arb_assert(!is_sentinel()); - return get<0>(e_); + return std::get<0>(e_); } const I& iter() const { arb_assert(!is_sentinel()); - return get<0>(e_); + return std::get<0>(e_); } S& sentinel() { arb_assert(is_sentinel()); - return get<1>(e_); + return std::get<1>(e_); } const S& sentinel() const { arb_assert(is_sentinel()); - return get<1>(e_); + return std::get<1>(e_); } public: diff --git a/arbor/util/transform.hpp b/arbor/util/transform.hpp index 747c5c9544056834980b9fdec85bc0ce46d7eaad..26b4d5eb35f0023f231e2cb6b1a779aa3664da06 100644 --- a/arbor/util/transform.hpp +++ b/arbor/util/transform.hpp @@ -127,28 +127,18 @@ transform_iterator<I, std::decay_t<F>> make_transform_iterator(const I& i, const return transform_iterator<I, std::decay_t<F>>(i, f); } -// TODO C++17: simplify with constexpr-if -namespace transform_impl { +template <typename Seq, typename F> +auto transform_view(Seq&& s, const F& f) { using std::begin; using std::end; - // transform over regular sequences: - template <typename Seq, typename F> - auto transform_(Seq&& s, const F& f, std::true_type) { + if constexpr (is_regular_sequence_v<Seq&&>) { return make_range(make_transform_iterator(begin(s), f), make_transform_iterator(end(s), f)); } - - // transform over sentinel-terminated sequences: - template <typename Seq, typename F> - auto transform_(Seq&& s, const F& f, std::false_type) { + else { return make_range(make_transform_iterator(begin(s), f), end(s)); } } -template <typename Seq, typename F> -auto transform_view(Seq&& s, const F& f) { - return transform_impl::transform_(std::forward<Seq>(s), f, is_regular_sequence<Seq&&>{}); -} - } // namespace util } // namespace arb diff --git a/arborenv/concurrency.cpp b/arborenv/concurrency.cpp index 1880d8f2f0947e30501c8973f0172c8ab146bce7..715c65fce7e56dc8c4c38b0f920d755d4f32e20f 100644 --- a/arborenv/concurrency.cpp +++ b/arborenv/concurrency.cpp @@ -5,8 +5,7 @@ #include <arborenv/concurrency.hpp> -// TODO: C++17 use __has_include(<unistd.h>) -#if defined(__unix__) || defined(__APPLE__) && defined(__MACH__) +#if __has_include(<unistd.h>) #include <unistd.h> #endif