diff --git a/xo-arena/include/xo/arena/DArenaHashMap.hpp b/xo-arena/include/xo/arena/DArenaHashMap.hpp index b1659e67..960c3a2d 100644 --- a/xo-arena/include/xo/arena/DArenaHashMap.hpp +++ b/xo-arena/include/xo/arena/DArenaHashMap.hpp @@ -6,12 +6,50 @@ #pragma once #include "DArenaVector.hpp" +#include #include #include #include #include namespace xo { + struct verify_policy { + static verify_policy log_only() { + return verify_policy{.flags_ = 0x01}; + } + static verify_policy throw_only() { + return verify_policy{.flags_ = 0x02}; + } + static verify_policy chatty() { + return verify_policy{.flags_ = 0x03}; + } + + bool is_silent() const noexcept { return flags_ == 0; } + bool log_flag() const noexcept { return flags_ & 0x01; } + bool throw_flag() const noexcept { return flags_ & 0x02; } + + template + bool report_error(scope & log, Tn&&... args) + { + if (!this->is_silent()) { + // TODO: consider global arena here for string + std::string msg = tostr(std::forward(args)...); + + if (this->log_flag()) { + log.retroactively_enable(); + log(msg); + } + if (this->throw_flag()) { + throw std::runtime_error(msg); + } + } + return false; + } + + const char * c_self_ = "anonymous"; + uint8_t flags_; + }; + namespace mm { #ifdef NOT_YET enum class insert_error : int32_t { @@ -169,7 +207,7 @@ namespace xo { **/ std::pair try_insert(const std::pair & kv_pair); - bool verify_ok(bool /*throw_flag_not_implemented*/ = true) const; + bool verify_ok(verify_policy p = verify_policy::throw_only()) const; private: /** load group abstraction from control bytes starting at @p ix **/ @@ -382,8 +420,23 @@ namespace xo { **/ template bool - DArenaHashMap::verify_ok(bool /*throw_flag_not_implemented*/) const + DArenaHashMap::verify_ok(verify_policy policy) const { + using xo::scope; + using xo::tostr; + using xo::xtag; + + constexpr const char * c_self = "DArenaHashMap::verify_ok"; + scope log(XO_DEBUG(debug_flag_), xtag("size", size_)); + + /* SM1.1: size_ <= n_slot_ */ + if (size_ > n_slot_) { + return policy.report_error(log, + c_self, ": expect .size < .n_slot", + xtag("size", size_), + xtag("n_slot", n_slot_)); + } + return true; } } diff --git a/xo-arena/utest/random_hash_ops.hpp b/xo-arena/utest/random_hash_ops.hpp index 6216a457..548f72c8 100644 --- a/xo-arena/utest/random_hash_ops.hpp +++ b/xo-arena/utest/random_hash_ops.hpp @@ -197,7 +197,9 @@ namespace utest { xo::scope log(XO_DEBUG(catch_flag), xtag("lo", lo), xtag("hi", hi), xtag("k", k)); - REQUIRE_ORFAIL(ok_flag, catch_flag, p_map->verify_ok(catch_flag)); + auto policy = xo::verify_policy::chatty(); + + REQUIRE_ORFAIL(ok_flag, catch_flag, p_map->verify_ok(policy)); if ((hi <= lo) || (k == 0)) return true; @@ -223,7 +225,7 @@ namespace utest { */ auto insert_result = p_map->try_insert(typename HashMap::value_type(x, 10 * x)); - REQUIRE_ORFAIL(ok_flag, catch_flag, p_map->verify_ok(catch_flag)); + REQUIRE_ORFAIL(ok_flag, catch_flag, p_map->verify_ok(policy)); REQUIRE_ORFAIL(ok_flag, catch_flag, insert_result.second); diff --git a/xo-indentlog/include/xo/indentlog/scope.hpp b/xo-indentlog/include/xo/indentlog/scope.hpp index 3217e0e8..42a664c9 100644 --- a/xo-indentlog/include/xo/indentlog/scope.hpp +++ b/xo-indentlog/include/xo/indentlog/scope.hpp @@ -171,10 +171,27 @@ namespace xo { return true; } /*log*/ + /** re-enable + log (args...). + * No-op if scope already enabled. + * Useful in verify_ok() methods, to conditionally enable + * logging only when a test fails + **/ + template + void retroactively_enable(Tn&&... args) { + if (finalized_) { + this->finalized_ = false; + this->begin_scope(std::forward(args)...); + } + } + /** Log argument in pack @p args **/ template bool operator()(Tn&&... args) { return this->log(std::forward(args)...); } + /** If enabled, writes initial banner **/ + template + void begin_scope(Tn&&... args); + /** Optionally, call once to end scope before dtor. * Logs arguments in pack @p args **/ @@ -231,27 +248,7 @@ namespace xo { line_{setup.line_}, finalized_{!(setup.is_enabled())} { - if(setup.is_enabled()) { - state_impl_type * logstate = basic_scope::require_thread_local_state(); - std::ostream & os = logstate2stream(logstate); - - logstate->preamble(this->style_, this->name1_, this->name2_); - - tosn(os, " ", std::forward(args)...); - - if (log_config::location_enabled) { - /* prints on next call to flush2sbuf */ - logstate->set_location(this->file_, this->line_); - //tosn(os, " [", basename(this->file_), ":", this->line_, "]"); - } - - logstate->flush2sbuf(std::clog.rdbuf()); - - ///* next call to scope::log() can reset to beginning of buffer space */ - //logstate->ss().seekp(0); - - logstate->incr_nesting(); - } + this->begin_scope(std::forward(args)...); } /*ctor*/ template @@ -314,6 +311,34 @@ namespace xo { logstate->flush2sbuf(this->dest_sbuf_); } /*flush2sbuf*/ + template + template + void + basic_scope::begin_scope(Tn&&... args) + { + if(this->enabled()) { + state_impl_type * logstate = basic_scope::require_thread_local_state(); + std::ostream & os = logstate2stream(logstate); + + logstate->preamble(this->style_, this->name1_, this->name2_); + + tosn(os, " ", std::forward(args)...); + + if (log_config::location_enabled) { + /* prints on next call to flush2sbuf */ + logstate->set_location(this->file_, this->line_); + //tosn(os, " [", basename(this->file_), ":", this->line_, "]"); + } + + logstate->flush2sbuf(std::clog.rdbuf()); + + ///* next call to scope::log() can reset to beginning of buffer space */ + //logstate->ss().seekp(0); + + logstate->incr_nesting(); + } + } + template template void