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