diff --git a/default.nix b/default.nix index 78a758cd..41a1a384 100644 --- a/default.nix +++ b/default.nix @@ -22,7 +22,7 @@ stdenv.mkDerivation rec { cmake gcc7 sass - pkgconfig + pkgs.pkg-config or pkgs.pkgconfig ]; cmakeFlags = [ "-Dlager_BUILD_TESTS=OFF" diff --git a/lager/lenses/at.hpp b/lager/lenses/at.hpp index 22e3d31f..9e4bbdf8 100644 --- a/lager/lenses/at.hpp +++ b/lager/lenses/at.hpp @@ -24,20 +24,51 @@ template using insert_opt_t = std::decay_t().insert( std::declval().value()))>; +template +using has_count_t = decltype(std::declval().count(std::declval())); + +// When building without exception support, returns whether the container has +// the key, using some heuristics to distinguish vector-likes or map-like +// containers. Otherwise it just returns true and we use the normal +// exception-based mechanism. +template +bool maybe_has_key(const Whole& whole, const Key& k) noexcept +{ +#ifdef LAGER_NO_EXCEPTIONS + if constexpr (zug::meta::is_detected::value) { + return whole.count(k) > 0; + } else if constexpr (std::is_convertible:: + value) { + const auto k_ = static_cast(k); + return k_ >= typename Whole::size_type{} && k_ < whole.size(); + } else { + static_assert( + zug::meta::is_detected::value || + std::is_convertible::value, + "at lense not supported with LAGER_NO_EXCEPTIONS"); + return true; + } +#else + return true; +#endif +} + template ::value && - !zug::meta::is_detected::value, + !zug::meta::is_detected::value, int> = 0> std::decay_t at_setter_impl(Whole&& whole, Part&& part, Key&& key) { auto r = std::forward(whole); - if (part.has_value()) { - LAGER_TRY { + if (maybe_has_key(r, key) && part.has_value()) { + LAGER_TRY + { r.at(std::forward(key)) = std::forward(part).value(); - } LAGER_CATCH(std::out_of_range const&) {} + } + LAGER_CATCH(std::out_of_range const&) {} } return r; } @@ -50,12 +81,14 @@ template < int> = 0> std::decay_t at_setter_impl(Whole&& whole, Part&& part, Key&& key) { - if (part.has_value()) { - LAGER_TRY { + if (maybe_has_key(whole, key) && part.has_value()) { + LAGER_TRY + { (void) whole.at(std::forward(key)); return std::forward(whole).set( std::forward(key), std::forward(part).value()); - } LAGER_CATCH(std::out_of_range const&) {} + } + LAGER_CATCH(std::out_of_range const&) {} } return std::forward(whole); } @@ -69,9 +102,12 @@ template < std::decay_t at_setter_impl(Whole&& whole, Part&& part, Key&& key) { if (part.has_value()) { - LAGER_TRY { - return std::forward(whole).insert(std::forward(part).value()); - } LAGER_CATCH(std::out_of_range const&) {} + LAGER_TRY + { + return std::forward(whole).insert( + std::forward(part).value()); + } + LAGER_CATCH(std::out_of_range const&) {} } return std::forward(whole); } @@ -91,11 +127,10 @@ auto at(Key key) return [f = LAGER_FWD(f), &key](auto&& whole) { using Part = std::optional>; return f([&]() -> Part { - LAGER_TRY { - return LAGER_FWD(whole).at(key); - } LAGER_CATCH(std::out_of_range const&) { + if (!detail::maybe_has_key(whole, key)) return std::nullopt; - } + LAGER_TRY { return LAGER_FWD(whole).at(key); } + LAGER_CATCH(std::out_of_range const&) { return std::nullopt; } }())([&](Part part) { return detail::at_setter_impl( LAGER_FWD(whole), std::move(part), key); diff --git a/lager/lenses/at_or.hpp b/lager/lenses/at_or.hpp index 5f356086..04b58db3 100644 --- a/lager/lenses/at_or.hpp +++ b/lager/lenses/at_or.hpp @@ -1,12 +1,13 @@ #pragma once +#include + #include #include #include #include -#include #include #include @@ -16,8 +17,8 @@ namespace detail { // detect if T satifsies the immer API for setting values template -using set_t = std::decay_t().set(std::declval(), std::declval()))>; +using set_t = std::decay_t().set(std::declval(), + std::declval()))>; template < typename Whole, @@ -27,10 +28,11 @@ template < int> = 0> std::decay_t at_or_setter_impl(Whole&& whole, Part&& part, Key&& key) { + if (!maybe_has_key(whole, key)) + return std::forward(whole); auto r = std::forward(whole); - LAGER_TRY { - r.at(std::forward(key)) = std::forward(part); - } LAGER_CATCH(std::out_of_range const&) {} + LAGER_TRY { r.at(std::forward(key)) = std::forward(part); } + LAGER_CATCH(std::out_of_range const&) {} return r; } @@ -42,11 +44,15 @@ template < int> = 0> std::decay_t at_or_setter_impl(Whole&& whole, Part&& part, Key&& key) { - LAGER_TRY { + if (!maybe_has_key(whole, key)) + return std::forward(whole); + LAGER_TRY + { (void) whole.at(std::forward(key)); return std::forward(whole).set(std::forward(key), std::forward(part)); - } LAGER_CATCH(std::out_of_range const&) {} + } + LAGER_CATCH(std::out_of_range const&) {} return std::forward(whole); } @@ -62,11 +68,10 @@ auto at_or(Key key) return [f = LAGER_FWD(f), &key](auto&& whole) { using Part = std::decay_t; return f([&]() -> Part { - LAGER_TRY { - return LAGER_FWD(whole).at(key); - } LAGER_CATCH(std::out_of_range const&) { + if (!detail::maybe_has_key(whole, key)) return Part{}; - } + LAGER_TRY { return LAGER_FWD(whole).at(key); } + LAGER_CATCH(std::out_of_range const&) { return Part{}; } }())([&](auto&& part) { return detail::at_or_setter_impl( LAGER_FWD(whole), LAGER_FWD(part), key); @@ -85,11 +90,10 @@ auto at_or(Key key, Default&& def) return [f = LAGER_FWD(f), &key, &def](auto&& whole) { using Part = std::decay_t; return f([&]() -> Part { - LAGER_TRY { - return LAGER_FWD(whole).at(key); - } LAGER_CATCH(std::out_of_range const&) { + if (!detail::maybe_has_key(whole, key)) return def; - } + LAGER_TRY { return LAGER_FWD(whole).at(key); } + LAGER_CATCH(std::out_of_range const&) { return def; } }())([&](auto&& part) { return detail::at_or_setter_impl( LAGER_FWD(whole), LAGER_FWD(part), key); diff --git a/lager/reader.hpp b/lager/reader.hpp index 8744bf65..28a3c800 100644 --- a/lager/reader.hpp +++ b/lager/reader.hpp @@ -71,10 +71,11 @@ struct reader_mixin auto node_() const { - if(auto node = detail::access::node(*static_cast(this))) { + if (auto node = + detail::access::node(*static_cast(this))) { return node; } - throw std::runtime_error("Accessing uninitialized reader"); + LAGER_THROW(std::runtime_error("Accessing uninitialized reader")); } }; @@ -101,7 +102,8 @@ class reader_base int> = 0> reader_base(reader_base x) : base_t{std::move(x)} - {} + { + } template , @@ -109,12 +111,14 @@ class reader_base int> = 0> reader_base(cursor_base x) : base_t{std::move(x)} - {} + { + } template reader_base(std::shared_ptr n) : base_t{std::move(n)} - {} + { + } }; /*! diff --git a/lager/writer.hpp b/lager/writer.hpp index f8ed2205..93dd54e7 100644 --- a/lager/writer.hpp +++ b/lager/writer.hpp @@ -70,10 +70,11 @@ struct writer_mixin auto node_() const { - if(auto node = detail::access::node(*static_cast(this))) { + if (auto node = + detail::access::node(*static_cast(this))) { return node; } - throw std::runtime_error("Accessing uninitialized writer"); + LAGER_THROW(std::runtime_error("Accessing uninitialized writer")); } }; @@ -100,16 +101,19 @@ class writer_base template writer_base(writer_base x) : node_(std::move(x.node_)) - {} + { + } template writer_base(cursor_base x) : node_(detail::access::node(std::move(x))) - {} + { + } writer_base(node_ptr_t sig) : node_(std::move(sig)) - {} + { + } }; /*!