From 548fd17fc0c2db4d833e3ccc32b75275e4ab59c4 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 6 Aug 2025 19:08:54 +1000 Subject: [PATCH 01/33] create stream_listener --- include/warthog/io/stream_listener.h | 59 ++++++++++++++++++++++++++++ src/CMakeLists.txt | 1 + src/io/listeners.cpp | 32 +++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 include/warthog/io/stream_listener.h create mode 100644 src/io/listeners.cpp diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_listener.h new file mode 100644 index 0000000..16e8211 --- /dev/null +++ b/include/warthog/io/stream_listener.h @@ -0,0 +1,59 @@ +#ifndef WARTHOG_IO_STREAM_LISTENER_H +#define WARTHOG_IO_STREAM_LISTENER_H + +// listener/stream_listener.h +// +// A search listener is a callback class that executes specialised +// code for partiular search events, such as when: +// - a node is generated +// - a node is expanded +// - a node is relaxed +// +// This class implements dummy listener with empty event handlers. +// +// @author: Ryan Hechenberger +// @created: 2025-08-01 +// + +#include +#include +#include +#include +#include + +namespace warthog::io +{ + +class stream_listener +{ +public: + using shared_stream_t = std::shared_ptr; + stream_listener() = default; + stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + stream_listener(std::ostream& stream); + stream_listener(const shared_stream_t& stream); + ~stream_listener(); + + void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + void stream(std::ostream& stream); + void stream_share(const shared_stream_t& stream); + void stream_share(const stream_listener& stream); + void stream_stdin(); + void stream_stderr(); + void clear_stream(); + + operator bool() const noexcept { return stream_ != nullptr; } + + std::ostream& stream() noexcept { assert(stream_ != nullptr); return *stream_; } + const shared_stream_t& shared_stream() noexcept { return shared_stream_; } + +private: + std::ostream* stream_ = nullptr; + shared_stream_t shared_stream_; +}; + +using void_listener = stream_listener; + +} // namespace warthog::io + +#endif // WARTHOG_IO_STREAM_LISTENER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f53bdd..20fc3a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ geometry/geography.cpp geometry/geom.cpp io/grid.cpp +io/listeners.cpp memory/node_pool.cpp diff --git a/src/io/listeners.cpp b/src/io/listeners.cpp new file mode 100644 index 0000000..9ff0e1c --- /dev/null +++ b/src/io/listeners.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +namespace warthog::io +{ + +stream_listener::stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + shared_stream_ = std::make_shared(filename, mode); + stream_ = shared_stream_.get(); +} +stream_listener::stream_listener(std::ostream& stream) +{ + stream_ = &stream; +} +stream_listener::stream_listener(const shared_stream_t& stream) +{ + shared_stream_ = stream; + stream_ = shared_stream_.get(); +} +stream_listener::~stream_listener() = default; + +void stream_open(const std::filesystem::path& filename); +void stream(std::ostream& stream); +void stream_share(const shared_stream_t& stream); +void stream_share(const stream_listener& stream); +void stream_stdin(); +void stream_stderr(); +void clear_stream(); + +} // namespace warthog::io From 0af8334de9c428f415f55bc74e5cf8eea39b0e89 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 6 Aug 2025 21:02:39 +1000 Subject: [PATCH 02/33] added listener functionality --- include/warthog/io/listener.h | 43 +++++++++++++++++++ include/warthog/io/stream_listener.h | 2 +- .../warthog/search/unidirectional_search.h | 29 ++++++------- src/io/listeners.cpp | 43 ++++++++++++++++--- 4 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 include/warthog/io/listener.h diff --git a/include/warthog/io/listener.h b/include/warthog/io/listener.h new file mode 100644 index 0000000..e6ee83b --- /dev/null +++ b/include/warthog/io/listener.h @@ -0,0 +1,43 @@ +#ifndef WARTHOG_IO_LISTENER_H +#define WARTHOG_IO_LISTENER_H + +// listener/stream_listener.h +// +// The lister base class, has many invoker functions for set listen events +// +// This class implements dummy listener with empty event handlers. +// +// @author: Ryan Hechenberger +// @created: 2025-08-06 +// + +#include + +#define WARTHOG_LISTENER_DEFINE(func_name) \ +template \ +concept listener_has_##func_name = requires(Listener L, Args&&... args) \ +{ \ + { L.generate_node(std::forward(args)...) }; \ +}; \ +template \ +void listener_##func_name(Listeners& L, Args&&... args) \ +{ \ + if constexpr (I < std::tuple_size_v) { \ + using T = std::tuple_element_t; \ + if constexpr (listener_has_##func_name ) { \ + std::get(L).func_name(std::forward(args)...); \ + } \ + listener_##func_name (L, std::forward(args)...); \ + } \ +} + +namespace warthog::io +{ + +WARTHOG_LISTENER_DEFINE(generate_node) +WARTHOG_LISTENER_DEFINE(expand_node) +WARTHOG_LISTENER_DEFINE(relax_node) + +} // namespace warthog::io + +#endif // WARTHOG_IO_LISTENER_H diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_listener.h index 16e8211..8a433cc 100644 --- a/include/warthog/io/stream_listener.h +++ b/include/warthog/io/stream_listener.h @@ -38,7 +38,7 @@ class stream_listener void stream(std::ostream& stream); void stream_share(const shared_stream_t& stream); void stream_share(const stream_listener& stream); - void stream_stdin(); + void stream_stdout(); void stream_stderr(); void clear_stream(); diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index b8fec89..1138fbd 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -10,12 +10,12 @@ // @created: 2021-10-13 // -#include "dummy_listener.h" #include "problem_instance.h" #include "search.h" #include "search_parameters.h" #include "solution.h" #include "uds_traits.h" +#include #include #include #include @@ -41,7 +41,7 @@ namespace warthog::search // used determine if a search should continue or terminate. // (default: search for any solution, until OPEN is exhausted) template< - class H, class E, class Q = util::pqueue_min, class L = dummy_listener, + typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>, admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> @@ -49,9 +49,8 @@ class unidirectional_search { public: unidirectional_search( - H* heuristic, E* expander, Q* queue, L* listener = nullptr) - : heuristic_(heuristic), expander_(expander), open_(queue), - listener_(listener) + H* heuristic, E* expander, Q* queue, L listeners = L{}) + : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) { } ~unidirectional_search() { } @@ -113,10 +112,10 @@ class unidirectional_search } } - void - set_listener(L* listener) + L& + get_listeners() noexcept { - listener_ = listener; + return listeners_; } E* @@ -151,7 +150,7 @@ class unidirectional_search H* heuristic_; E* expander_; Q* open_; - L* listener_; + [[no_unique_address]] L listeners_; // no copy ctor unidirectional_search(const unidirectional_search& other) { } @@ -223,7 +222,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - listener_->generate_node(0, start, 0, UINT32_MAX); + io::listener_generate_node(listeners_, nullptr, start, nullptr, UINT32_MAX); user(pi->verbose_, pi); trace(pi->verbose_, "Start node:", *start); update_ub(start, sol, pi); @@ -247,7 +246,7 @@ class unidirectional_search current->set_expanded(true); // NB: set before generating succ sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); - listener_->expand_node(current); + io::listener_expand_node(listeners_, current); trace(pi->verbose_, "Expanding:", *current); // Generate successors of the current node @@ -258,7 +257,7 @@ class unidirectional_search expander_->get_successor(i, n, cost_to_n); sol->met_.nodes_generated_++; cost_t gval = current->get_g() + cost_to_n; - listener_->generate_node(current, n, gval, i); + io::listener_generate_node(listeners_, current, n, gval, i); // Generate new search nodes, provided they're not // dominated by the current upperbound @@ -282,7 +281,7 @@ class unidirectional_search < sol->sum_of_edge_costs_) { n->relax(gval, current->get_id()); - listener_->relax_node(n); + io::listener_relax_node(listeners_, n); if(open_->contains(n)) { @@ -327,10 +326,10 @@ class unidirectional_search }; template< - class H, class E, class Q = util::pqueue_min, class L = dummy_listener> + typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>> unidirectional_search( H* heuristic, E* expander, Q* queue, - L* listener = nullptr) -> unidirectional_search; + L listeners = L{}) -> unidirectional_search; } // namespace warthog::search diff --git a/src/io/listeners.cpp b/src/io/listeners.cpp index 9ff0e1c..32d1f04 100644 --- a/src/io/listeners.cpp +++ b/src/io/listeners.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace warthog::io { @@ -21,12 +22,40 @@ stream_listener::stream_listener(const shared_stream_t& stream) } stream_listener::~stream_listener() = default; -void stream_open(const std::filesystem::path& filename); -void stream(std::ostream& stream); -void stream_share(const shared_stream_t& stream); -void stream_share(const stream_listener& stream); -void stream_stdin(); -void stream_stderr(); -void clear_stream(); +void stream_listener::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + shared_stream_ = std::make_shared(filename, mode); + stream_ = shared_stream_.get(); +} +void stream_listener::stream(std::ostream& stream) +{ + stream_ = &stream; + shared_stream_ = nullptr; +} +void stream_listener::stream_share(const shared_stream_t& stream) +{ + shared_stream_ = stream; + stream_ = shared_stream_.get(); +} +void stream_listener::stream_share(const stream_listener& stream) +{ + shared_stream_ = stream.shared_stream_; + stream_ = stream.stream_; +} +void stream_listener::stream_stdout() +{ + shared_stream_ = nullptr; + stream_ = &std::cout; +} +void stream_listener::stream_stderr() +{ + shared_stream_ = nullptr; + stream_ = &std::cerr; +} +void stream_listener::clear_stream() +{ + shared_stream_ = nullptr; + stream_ = nullptr; +} } // namespace warthog::io From 1524765249b84e0882e5fee1f2792569c2c4cd23 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 Aug 2025 16:12:59 +1000 Subject: [PATCH 03/33] trace for grid --- include/warthog/io/grid_trace.h | 63 +++++++++++++++++++ include/warthog/io/listener.h | 23 ++++++- include/warthog/io/posthoc_listener.h | 56 +++++++++++++++++ include/warthog/io/stream_listener.h | 4 +- .../warthog/search/unidirectional_search.h | 13 ++-- src/CMakeLists.txt | 1 + src/io/listeners.cpp | 10 +++ 7 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 include/warthog/io/grid_trace.h create mode 100644 include/warthog/io/posthoc_listener.h diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h new file mode 100644 index 0000000..a2c8a08 --- /dev/null +++ b/include/warthog/io/grid_trace.h @@ -0,0 +1,63 @@ +#ifndef WARTHOG_IO_GRID_TRACE_H +#define WARTHOG_IO_GRID_TRACE_H + +// listener/grid_trace.h +// +// @author: Ryan Hechenberger +// @created: 2025-08-07 +// + +#include "posthoc_listener.h" +#include +#include +#include +#include + +namespace warthog::io +{ + +/// @brief class that produces a posthoc trace for the gridmap domain +class grid_trace : public posthoc_listener +{ +public: + using node = search::search_node; + using posthoc_listener::posthoc_listener; + + void set_grid(domain::gridmap* grid) noexcept + { + grid_ = grid; + } + + void print_posthoc_header() override; + + /// @brief Checks that grid_ != nullptr + void event(const char* name) const + { + if (grid_ == nullptr) { + throw std::logic_error("grid_trace::grid_ is null"); + } + } + + void begin_search(int id, const search::search_problem_instance& pi); + + void + expand_node(const node& current) const; + + void + relax_node(const node& current) const; + + void + generate_node( + const node* parent, const node& child, cost_t edge_cost, + uint32_t edge_id) const; + + void + close_node(const node& current) const; + +protected: + domain::gridmap* grid_; +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_GRID_TRACE_H diff --git a/include/warthog/io/listener.h b/include/warthog/io/listener.h index e6ee83b..ffb0b18 100644 --- a/include/warthog/io/listener.h +++ b/include/warthog/io/listener.h @@ -13,30 +13,47 @@ #include -#define WARTHOG_LISTENER_DEFINE(func_name) \ +#define WARTHOG_LISTENER_HAS_FN(func_name) \ template \ concept listener_has_##func_name = requires(Listener L, Args&&... args) \ { \ - { L.generate_node(std::forward(args)...) }; \ -}; \ + { L.func_name(std::forward(args)...) }; \ +}; +#define WARTHOG_LISTENER_FN(func_name) \ template \ void listener_##func_name(Listeners& L, Args&&... args) \ { \ if constexpr (I < std::tuple_size_v) { \ using T = std::tuple_element_t; \ if constexpr (listener_has_##func_name ) { \ + /* call event first if present */ \ + if constexpr (listener_has_event ) std::get(L).event(#func_name); \ std::get(L).func_name(std::forward(args)...); \ + } else { \ + if constexpr (listener_has_missing_event ) \ + std::get(L).missing_event(#func_name); \ } \ listener_##func_name (L, std::forward(args)...); \ } \ } +#define WARTHOG_LISTENER_DEFINE(func_name) \ + WARTHOG_LISTENER_HAS_FN(func_name) \ + WARTHOG_LISTENER_FN(func_name) + namespace warthog::io { +// functions used by WARTHOG_LISTENER_FN +WARTHOG_LISTENER_HAS_FN(event) +WARTHOG_LISTENER_HAS_FN(missing_event) + +WARTHOG_LISTENER_DEFINE(begin_search) +WARTHOG_LISTENER_DEFINE(end_search) WARTHOG_LISTENER_DEFINE(generate_node) WARTHOG_LISTENER_DEFINE(expand_node) WARTHOG_LISTENER_DEFINE(relax_node) +WARTHOG_LISTENER_DEFINE(close_node) } // namespace warthog::io diff --git a/include/warthog/io/posthoc_listener.h b/include/warthog/io/posthoc_listener.h new file mode 100644 index 0000000..14447b3 --- /dev/null +++ b/include/warthog/io/posthoc_listener.h @@ -0,0 +1,56 @@ +#ifndef WARTHOG_IO_POSTHOC_LISTENER_H +#define WARTHOG_IO_POSTHOC_LISTENER_H + +// listener/posthoc_listener.h +// +// @author: Ryan Hechenberger +// @created: 2025-08-07 +// + +#include "stream_listener.h" + +namespace warthog::io +{ + +/// @brief base posthoc listener class. +class posthoc_listener : public stream_listener +{ +public: + using stream_listener::stream_listener; + + // will print the header if not already printed + void event(const char*); + + virtual void print_posthoc_header(); + + template + void begin_search(int id, Args&&...) + { + do_trace_ = false; + if (done_trace_) return; // do not repeat a trace + if (stream_listener::operator bool() && id == search_id_) { + do_trace_ = true; + done_trace_ = true; + print_posthoc_header(); + } + } + template + void end_search(Args&&...) + { + do_trace_ = false; + } + + operator bool() const noexcept + { + return do_trace_; + } + +protected: + int search_id_ = 0; + bool do_trace_ = false; + bool done_trace_ = false; +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_POSTHOC_LISTENER_H diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_listener.h index 8a433cc..809e9d8 100644 --- a/include/warthog/io/stream_listener.h +++ b/include/warthog/io/stream_listener.h @@ -44,8 +44,8 @@ class stream_listener operator bool() const noexcept { return stream_ != nullptr; } - std::ostream& stream() noexcept { assert(stream_ != nullptr); return *stream_; } - const shared_stream_t& shared_stream() noexcept { return shared_stream_; } + std::ostream& stream() const noexcept { assert(stream_ != nullptr); return *stream_; } + const shared_stream_t& shared_stream() const noexcept { return shared_stream_; } private: std::ostream* stream_ = nullptr; diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 1138fbd..3417095 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -211,6 +211,8 @@ class unidirectional_search mytimer.start(); open_->clear(); + io::listener_begin_search(listeners_, static_cast(pi->instance_id_), *pi); + // initialise the start node and push to OPEN { if(pi->start_ == pad_id::max()) { return; } @@ -222,7 +224,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - io::listener_generate_node(listeners_, nullptr, start, nullptr, UINT32_MAX); + io::listener_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); user(pi->verbose_, pi); trace(pi->verbose_, "Start node:", *start); update_ub(start, sol, pi); @@ -246,7 +248,7 @@ class unidirectional_search current->set_expanded(true); // NB: set before generating succ sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); - io::listener_expand_node(listeners_, current); + io::listener_expand_node(listeners_, *current); trace(pi->verbose_, "Expanding:", *current); // Generate successors of the current node @@ -257,18 +259,18 @@ class unidirectional_search expander_->get_successor(i, n, cost_to_n); sol->met_.nodes_generated_++; cost_t gval = current->get_g() + cost_to_n; - io::listener_generate_node(listeners_, current, n, gval, i); // Generate new search nodes, provided they're not // dominated by the current upperbound if(n->get_search_number() != current->get_search_number()) { initialise_node_(n, current->get_id(), gval, pi, par, sol); - if(n->get_f() < sol->sum_of_edge_costs_) + if(n->get_f() <= sol->sum_of_edge_costs_) { open_->push(n); trace(pi->verbose_, "Generate:", *n); update_ub(current, sol, pi); + io::listener_generate_node(listeners_, current, *n, gval, i); continue; } } @@ -281,7 +283,7 @@ class unidirectional_search < sol->sum_of_edge_costs_) { n->relax(gval, current->get_id()); - io::listener_relax_node(listeners_, n); + io::listener_relax_node(listeners_, *n); if(open_->contains(n)) { @@ -308,6 +310,7 @@ class unidirectional_search // patched until AC FC RP reworked sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); } + io::listener_close_node(listeners_, *current); } sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 20fc3a8..5abf0b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ geometry/geom.cpp io/grid.cpp io/listeners.cpp +io/traces.cpp memory/node_pool.cpp diff --git a/src/io/listeners.cpp b/src/io/listeners.cpp index 32d1f04..18b0456 100644 --- a/src/io/listeners.cpp +++ b/src/io/listeners.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -58,4 +59,13 @@ void stream_listener::clear_stream() stream_ = nullptr; } +void posthoc_listener::print_posthoc_header() +{ + if (*this) { + stream() << R"posthoc(version: 1.4.0 +events: +)posthoc"; + } +} + } // namespace warthog::io From e6c0f8826661802f982c2bebaa0c47a890966b5a Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 7 Aug 2025 16:13:13 +1000 Subject: [PATCH 04/33] trace for grid --- src/io/traces.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/io/traces.cpp diff --git a/src/io/traces.cpp b/src/io/traces.cpp new file mode 100644 index 0000000..1bd22ce --- /dev/null +++ b/src/io/traces.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +namespace warthog::io +{ + + +void grid_trace::print_posthoc_header() +{ + if (*this) { + stream() << R"posthoc(version: 1.4.0 +views: + cell: + - $: rect + width: 1 + height: 1 + x: ${{$.x}} + y: ${{$.y}} + fill: ${{$.fill}} + clear: ${{ $.clear }} + main: + - $: cell + $if: ${{ $.type == 'source' }} + fill: green + clear: false + - $: cell + $if: ${{ $.type == 'destination' }} + fill: red + clear: false + - $: cell + $if: ${{ $.type == 'expand' }} + fill: blue + clear: close + - $: cell + $if: ${{ $.type == 'generate' }} + fill: orange + clear: true +pivot: + x: ${{ $.x + 0.5 }} + y: ${{ $.y + 0.5 }} + scale: 1 +events: +)posthoc"; + } +} + +void grid_trace::begin_search(int id, const search::search_problem_instance& pi) +{ + posthoc_listener::begin_search(id); + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(pi.start_, x, y); + stream() << std::format(" - {{ type: source, id: {}, x: {}, y: {} }}\n", + pi.start_.id, x, y + ); + grid_->to_unpadded_xy(pi.target_, x, y); + stream() << std::format(" - {{ type: destination, id: {}, x: {}, y: {} }}\n", + pi.target_.id, x, y + ); + } +} + +void +grid_trace::expand_node(const node& current) const +{ + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(current.get_id(), x, y); + stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, x, y, current.get_f(), current.get_g() + ); + } +} + +void +grid_trace::relax_node(const node& current) const +{ + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(current.get_id(), x, y); + stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, x, y, current.get_f(), current.get_g() + ); + } +} + + +void +grid_trace::close_node(const node& current) const +{ + if (*this) { + assert(grid_ != nullptr); + uint32_t x, y; + grid_->to_unpadded_xy(current.get_id(), x, y); + stream() << std::format(" - {{ type: close, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, x, y, current.get_f(), current.get_g() + ); + } +} + + +void +grid_trace::generate_node(const node* parent, const node& child, cost_t, uint32_t) const +{ + if (*this) { + assert(grid_ != nullptr); + std::string pid; + if (parent != nullptr) { + pid = std::format(", pId: {}", parent->get_id().id); + } + uint32_t x, y; + grid_->to_unpadded_xy(child.get_id(), x, y); + stream() << std::format(" - {{ type: generate, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + child.get_id().id, pid, x, y, child.get_f(), child.get_g() + ); + } +} + +} // namespace warthog::io From f81c8adecd077070fdb2c63efa65c07e48c94dd2 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 5 Sep 2025 13:31:01 +1000 Subject: [PATCH 05/33] rename listener to observer --- include/warthog/io/{listener.h => observer.h} | 0 include/warthog/io/{posthoc_listener.h => posthoc_trace.h} | 0 include/warthog/io/{stream_listener.h => stream_observer.h} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename include/warthog/io/{listener.h => observer.h} (100%) rename include/warthog/io/{posthoc_listener.h => posthoc_trace.h} (100%) rename include/warthog/io/{stream_listener.h => stream_observer.h} (100%) diff --git a/include/warthog/io/listener.h b/include/warthog/io/observer.h similarity index 100% rename from include/warthog/io/listener.h rename to include/warthog/io/observer.h diff --git a/include/warthog/io/posthoc_listener.h b/include/warthog/io/posthoc_trace.h similarity index 100% rename from include/warthog/io/posthoc_listener.h rename to include/warthog/io/posthoc_trace.h diff --git a/include/warthog/io/stream_listener.h b/include/warthog/io/stream_observer.h similarity index 100% rename from include/warthog/io/stream_listener.h rename to include/warthog/io/stream_observer.h From d439de10e2c72de896191bc3e2b668c587ab09ad Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 5 Sep 2025 14:45:42 +1000 Subject: [PATCH 06/33] rename src observers --- src/io/{listeners.cpp => observer.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/io/{listeners.cpp => observer.cpp} (100%) diff --git a/src/io/listeners.cpp b/src/io/observer.cpp similarity index 100% rename from src/io/listeners.cpp rename to src/io/observer.cpp From 8232fddb009b089be83527e4d28dbe17870ec8bd Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 11 Sep 2025 16:56:04 +1000 Subject: [PATCH 07/33] listener renamed to observer --- include/warthog/io/grid_trace.h | 6 +- include/warthog/io/observer.h | 57 ++++++++++--------- include/warthog/io/posthoc_trace.h | 33 +++++++---- include/warthog/io/stream_observer.h | 22 +++---- .../warthog/search/unidirectional_search.h | 14 ++--- src/CMakeLists.txt | 4 +- src/io/observer.cpp | 28 ++++----- src/io/traces.cpp | 2 +- 8 files changed, 89 insertions(+), 77 deletions(-) diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index a2c8a08..a95b7a7 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -7,7 +7,7 @@ // @created: 2025-08-07 // -#include "posthoc_listener.h" +#include "posthoc_trace.h" #include #include #include @@ -17,11 +17,11 @@ namespace warthog::io { /// @brief class that produces a posthoc trace for the gridmap domain -class grid_trace : public posthoc_listener +class grid_trace : public posthoc_trace { public: using node = search::search_node; - using posthoc_listener::posthoc_listener; + using posthoc_trace::posthoc_trace; void set_grid(domain::gridmap* grid) noexcept { diff --git a/include/warthog/io/observer.h b/include/warthog/io/observer.h index ffb0b18..f2e9e57 100644 --- a/include/warthog/io/observer.h +++ b/include/warthog/io/observer.h @@ -1,11 +1,17 @@ -#ifndef WARTHOG_IO_LISTENER_H -#define WARTHOG_IO_LISTENER_H +#ifndef WARTHOG_IO_OBSERVER_H +#define WARTHOG_IO_OBSERVER_H -// listener/stream_listener.h +// io/observer.h // -// The lister base class, has many invoker functions for set listen events +// The observer pattern defines methods to tightly-bind user provided observers to certain event patterns. +// Provide a tuple of observers, where some event is triggered will notifiy all observers with function of event name that is callable. // -// This class implements dummy listener with empty event handlers. +// These function names must be registered before use, common ones registered here. +// +// To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). +// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observer. +// This will run through each element in tuple (i) and call i.[function](args...) if able. +// If i.event([function],args...) is a valid callable, calls this function first, also tries i.event([function]). // // @author: Ryan Hechenberger // @created: 2025-08-06 @@ -13,48 +19,45 @@ #include -#define WARTHOG_LISTENER_HAS_FN(func_name) \ +#define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ template \ -concept listener_has_##func_name = requires(Listener L, Args&&... args) \ +concept observer_has_##func_name = requires(Listener L, Args&&... args) \ { \ { L.func_name(std::forward(args)...) }; \ }; -#define WARTHOG_LISTENER_FN(func_name) \ +#define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ template \ -void listener_##func_name(Listeners& L, Args&&... args) \ +void observer_##func_name(Listeners& L, Args&&... args) \ { \ if constexpr (I < std::tuple_size_v) { \ using T = std::tuple_element_t; \ - if constexpr (listener_has_##func_name ) { \ - /* call event first if present */ \ - if constexpr (listener_has_event ) std::get(L).event(#func_name); \ + constexpr bool has_event = observer_has_##func_name ; \ + if constexpr (observer_has_event) { std::get(L).event( #func_name , std::forward(args)... ); } \ + else if constexpr (observer_has_event) { std::get(L).event( #func_name ); } \ + if constexpr (has_event) { \ std::get(L).func_name(std::forward(args)...); \ - } else { \ - if constexpr (listener_has_missing_event ) \ - std::get(L).missing_event(#func_name); \ } \ listener_##func_name (L, std::forward(args)...); \ } \ } -#define WARTHOG_LISTENER_DEFINE(func_name) \ - WARTHOG_LISTENER_HAS_FN(func_name) \ - WARTHOG_LISTENER_FN(func_name) +#define WARTHOG_OBSERVER_DEFINE(func_name) \ + WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ + WARTHOG_OBSERVER_DEFINE_CALL(func_name) namespace warthog::io { // functions used by WARTHOG_LISTENER_FN -WARTHOG_LISTENER_HAS_FN(event) -WARTHOG_LISTENER_HAS_FN(missing_event) +WARTHOG_OBSERVER_DEFINE_HAS(event) -WARTHOG_LISTENER_DEFINE(begin_search) -WARTHOG_LISTENER_DEFINE(end_search) -WARTHOG_LISTENER_DEFINE(generate_node) -WARTHOG_LISTENER_DEFINE(expand_node) -WARTHOG_LISTENER_DEFINE(relax_node) -WARTHOG_LISTENER_DEFINE(close_node) +WARTHOG_OBSERVER_DEFINE(begin_search) +WARTHOG_OBSERVER_DEFINE(end_search) +WARTHOG_OBSERVER_DEFINE(generate_node) +WARTHOG_OBSERVER_DEFINE(expand_node) +WARTHOG_OBSERVER_DEFINE(relax_node) +WARTHOG_OBSERVER_DEFINE(close_node) } // namespace warthog::io -#endif // WARTHOG_IO_LISTENER_H +#endif // WARTHOG_IO_OBSERVER_H diff --git a/include/warthog/io/posthoc_trace.h b/include/warthog/io/posthoc_trace.h index 14447b3..04f8627 100644 --- a/include/warthog/io/posthoc_trace.h +++ b/include/warthog/io/posthoc_trace.h @@ -1,34 +1,43 @@ -#ifndef WARTHOG_IO_POSTHOC_LISTENER_H -#define WARTHOG_IO_POSTHOC_LISTENER_H +#ifndef WARTHOG_IO_POSTHOC_TRACE_H +#define WARTHOG_IO_POSTHOC_TRACE_H -// listener/posthoc_listener.h +// io/posthoc_trace.h +// +// stream_observer that outputs a trace for use with posthoc visuliser. +// See https://posthoc-app.pathfinding.ai/ // // @author: Ryan Hechenberger // @created: 2025-08-07 // -#include "stream_listener.h" +#include "stream_observer.h" namespace warthog::io { -/// @brief base posthoc listener class. -class posthoc_listener : public stream_listener +/// @brief base posthoc observer class. +/// +/// event begin_search and end_search will setup the trace to print only a specified. +/// Inherit to create new trace format by overriding print_posthoc_header for custom header. +/// Add own events to print posthoc event to stream() if (*this) holds true, +/// (*this) holds true iff id is on search_id between begin_search and end_search the first time only. +class posthoc_trace : public stream_observer { public: - using stream_listener::stream_listener; - - // will print the header if not already printed - void event(const char*); + using stream_observer::stream_observer; + // override print the header if not already printed virtual void print_posthoc_header(); + int search_id() const noexcept { return search_id_; } + void search_id(int sid) noexcept { search_id_ = sid; } + template void begin_search(int id, Args&&...) { do_trace_ = false; if (done_trace_) return; // do not repeat a trace - if (stream_listener::operator bool() && id == search_id_) { + if (stream_observer::operator bool() && id == search_id_) { do_trace_ = true; done_trace_ = true; print_posthoc_header(); @@ -53,4 +62,4 @@ class posthoc_listener : public stream_listener } // namespace warthog::io -#endif // WARTHOG_IO_POSTHOC_LISTENER_H +#endif // WARTHOG_IO_POSTHOC_TRACE_H diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index 809e9d8..9229b07 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -1,5 +1,5 @@ -#ifndef WARTHOG_IO_STREAM_LISTENER_H -#define WARTHOG_IO_STREAM_LISTENER_H +#ifndef WARTHOG_IO_STEAM_OBSERVER_H +#define WARTHOG_IO_STEAM_OBSERVER_H // listener/stream_listener.h // @@ -24,20 +24,20 @@ namespace warthog::io { -class stream_listener +class stream_observer { public: using shared_stream_t = std::shared_ptr; - stream_listener() = default; - stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); - stream_listener(std::ostream& stream); - stream_listener(const shared_stream_t& stream); - ~stream_listener(); + stream_observer() = default; + stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + stream_observer(std::ostream& stream); + stream_observer(const shared_stream_t& stream); + ~stream_observer(); void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); void stream(std::ostream& stream); void stream_share(const shared_stream_t& stream); - void stream_share(const stream_listener& stream); + void stream_share(const stream_observer& stream); void stream_stdout(); void stream_stderr(); void clear_stream(); @@ -52,8 +52,8 @@ class stream_listener shared_stream_t shared_stream_; }; -using void_listener = stream_listener; +using void_listener = stream_observer; } // namespace warthog::io -#endif // WARTHOG_IO_STREAM_LISTENER_H +#endif // WARTHOG_IO_STEAM_OBSERVER_H diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 3417095..25b2803 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -15,7 +15,7 @@ #include "search_parameters.h" #include "solution.h" #include "uds_traits.h" -#include +#include #include #include #include @@ -211,7 +211,7 @@ class unidirectional_search mytimer.start(); open_->clear(); - io::listener_begin_search(listeners_, static_cast(pi->instance_id_), *pi); + io::observer_begin_search(listeners_, static_cast(pi->instance_id_), *pi); // initialise the start node and push to OPEN { @@ -224,7 +224,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - io::listener_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); + io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); user(pi->verbose_, pi); trace(pi->verbose_, "Start node:", *start); update_ub(start, sol, pi); @@ -248,7 +248,7 @@ class unidirectional_search current->set_expanded(true); // NB: set before generating succ sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); - io::listener_expand_node(listeners_, *current); + io::observer_expand_node(listeners_, *current); trace(pi->verbose_, "Expanding:", *current); // Generate successors of the current node @@ -270,7 +270,7 @@ class unidirectional_search open_->push(n); trace(pi->verbose_, "Generate:", *n); update_ub(current, sol, pi); - io::listener_generate_node(listeners_, current, *n, gval, i); + io::observer_generate_node(listeners_, current, *n, gval, i); continue; } } @@ -283,7 +283,7 @@ class unidirectional_search < sol->sum_of_edge_costs_) { n->relax(gval, current->get_id()); - io::listener_relax_node(listeners_, *n); + io::observer_relax_node(listeners_, *n); if(open_->contains(n)) { @@ -310,7 +310,7 @@ class unidirectional_search // patched until AC FC RP reworked sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); } - io::listener_close_node(listeners_, *current); + io::observer_close_node(listeners_, *current); } sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5abf0b7..9420f49 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,8 @@ geometry/geography.cpp geometry/geom.cpp io/grid.cpp -io/listeners.cpp +io/log.cpp +io/observer.cpp io/traces.cpp memory/node_pool.cpp @@ -28,5 +29,4 @@ util/gm_parser.cpp util/helpers.cpp util/scenario_manager.cpp util/timer.cpp - ) diff --git a/src/io/observer.cpp b/src/io/observer.cpp index 18b0456..fd8567f 100644 --- a/src/io/observer.cpp +++ b/src/io/observer.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include @@ -7,59 +7,59 @@ namespace warthog::io { -stream_listener::stream_listener(const std::filesystem::path& filename, std::ios_base::openmode mode) +stream_observer::stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); stream_ = shared_stream_.get(); } -stream_listener::stream_listener(std::ostream& stream) +stream_observer::stream_observer(std::ostream& stream) { stream_ = &stream; } -stream_listener::stream_listener(const shared_stream_t& stream) +stream_observer::stream_observer(const shared_stream_t& stream) { shared_stream_ = stream; stream_ = shared_stream_.get(); } -stream_listener::~stream_listener() = default; +stream_observer::~stream_observer() = default; -void stream_listener::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void stream_observer::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); stream_ = shared_stream_.get(); } -void stream_listener::stream(std::ostream& stream) +void stream_observer::stream(std::ostream& stream) { stream_ = &stream; shared_stream_ = nullptr; } -void stream_listener::stream_share(const shared_stream_t& stream) +void stream_observer::stream_share(const shared_stream_t& stream) { shared_stream_ = stream; stream_ = shared_stream_.get(); } -void stream_listener::stream_share(const stream_listener& stream) +void stream_observer::stream_share(const stream_observer& stream) { shared_stream_ = stream.shared_stream_; stream_ = stream.stream_; } -void stream_listener::stream_stdout() +void stream_observer::stream_stdout() { shared_stream_ = nullptr; stream_ = &std::cout; } -void stream_listener::stream_stderr() +void stream_observer::stream_stderr() { shared_stream_ = nullptr; stream_ = &std::cerr; } -void stream_listener::clear_stream() +void stream_observer::clear_stream() { shared_stream_ = nullptr; stream_ = nullptr; } -void posthoc_listener::print_posthoc_header() +void posthoc_trace::print_posthoc_header() { if (*this) { stream() << R"posthoc(version: 1.4.0 diff --git a/src/io/traces.cpp b/src/io/traces.cpp index 1bd22ce..fb58898 100644 --- a/src/io/traces.cpp +++ b/src/io/traces.cpp @@ -47,7 +47,7 @@ void grid_trace::print_posthoc_header() void grid_trace::begin_search(int id, const search::search_problem_instance& pi) { - posthoc_listener::begin_search(id); + posthoc_trace::begin_search(id); if (*this) { assert(grid_ != nullptr); uint32_t x, y; From 0c755f7a5f98e38213de69e1086e65ca5e2d027a Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 11 Sep 2025 16:56:15 +1000 Subject: [PATCH 08/33] added first part of log --- include/warthog/io/log.h | 92 ++++++++++++++++++++++++++++++++++++++ src/io/log.cpp | 96 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 include/warthog/io/log.h create mode 100644 src/io/log.cpp diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h new file mode 100644 index 0000000..391a6ae --- /dev/null +++ b/include/warthog/io/log.h @@ -0,0 +1,92 @@ +#ifndef WARTHOG_IO_OBSERVER_H +#define WARTHOG_IO_OBSERVER_H + +// io/log.h +// +// Logger class. +// +// @author: Ryan Hechenberger +// @created: 2025-09-09 +// + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WARTHOG_LOG +#define WARTHOG_DEFAULT_LOG_LEVEL WARTHOG_LOG +#elif !defined(NDEBUG) +#define WARTHOG_DEFAULT_LOG_LEVEL 1 +#else +#define WARTHOG_DEFAULT_LOG_LEVEL 4 +#endif + +#define WARTHOG_TIME_FORMAT "%FT%T%Z" + +namespace warthog::io +{ + + +enum class log_level +{ + TRACE = 0, + DEBUG = 1, + INFORMATION = 2, + WARNING = 3, + ERROR = 4, + CRITICAL = 5, + NONE = 6 +}; + +struct log_sink +{ + using call_type = void (void*, std::string_view msg); + void* data = nullptr; + std::array(log_level::NONE)> call = {}; + + void log(log_level level, std::string_view msg) + { + if (static_cast(level) < call.size() && (std::bit_cast(data) | std::bit_cast(call[static_cast(level)])) != 0) + { + (*call[static_cast(level)])(data, msg); + } + } + + const log_sink& sink() const noexcept { return *this; } +}; + +class log_stream_sink : protected log_sink +{ +public: + log_stream_sink(); + log_stream_sink(bool err); + log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + log_stream_sink(std::ostream& stream); + ~log_stream_sink() = default; + + void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + void open(std::ostream& stream); + void open_stderr(); + + static void write_trace(void*, std::string_view msg); + static void write_debug(void*, std::string_view msg); + static void write_information(void*, std::string_view msg); + static void write_warning(void*, std::string_view msg); + static void write_error(void*, std::string_view msg); + static void write_critical(void*, std::string_view msg); + + using log_sink::sink; + +protected: + std::ostream* log_stream_ = nullptr; + std::unique_ptr owned_file_; + std::mutex lock_; +}; + +} // namespace warthog::io + +#endif // WARTHOG_IO_OBSERVER_H diff --git a/src/io/log.cpp b/src/io/log.cpp new file mode 100644 index 0000000..9b849c9 --- /dev/null +++ b/src/io/log.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +namespace warthog::io +{ + +log_stream_sink::log_stream_sink() : log_sink{nullptr, + {{&log_stream_sink::write_trace,&log_stream_sink::write_debug, + &log_stream_sink::write_information,&log_stream_sink::write_warning, + &log_stream_sink::write_error,&log_stream_sink::write_critical}}} +{ } + +log_stream_sink::log_stream_sink(bool err) +{ + if (err) { + data = this; + log_stream_ = &std::cerr; + } +} +log_stream_sink::log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + data = this; + owned_file_ = std::make_unique(filename, mode); + log_stream_ = owned_file_.get(); +} +log_stream_sink::log_stream_sink(std::ostream& stream) +{ + data = this; + log_stream_ = &stream; +} + +void log_stream_sink::open(const std::filesystem::path& filename, std::ios_base::openmode mode) +{ + data = this; + owned_file_ = std::make_unique(filename, mode); + log_stream_ = owned_file_.get(); +} +void log_stream_sink::open(std::ostream& stream) +{ + data = this; + owned_file_.release(); + log_stream_ = &stream; +} +void log_stream_sink::open_stderr() +{ + data = this; + owned_file_.release(); + log_stream_ = &std::cerr; +} + +void log_stream_sink::write_trace(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_debug(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_information(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_warning(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_error(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}", std::chrono::utc_clock::now(), msg); +} +void log_stream_sink::write_critical(void* logger, std::string_view msg) +{ + log_stream_sink* log_stream = static_cast(logger); + assert(log_stream != nullptr && log_stream->log_stream_); + std::lock_guard lock(log_stream->lock_); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}", std::chrono::utc_clock::now(), msg); +} + +} // namespace warthog::io From 7660b0d84ed4a78f004b7860f20c3c0bc89a8a4f Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 12 Sep 2025 15:39:27 +1000 Subject: [PATCH 09/33] log in debug phase --- apps/warthog.cpp | 3 + include/warthog/io/log.h | 147 +++++++++++++++++++++++++++++++++++---- src/io/log.cpp | 87 +++++++++++++++++++---- 3 files changed, 210 insertions(+), 27 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index a5d4ef1..9269481 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "cfg.h" #include @@ -111,6 +112,8 @@ run_experiments( warthog::util::scenario_manager& scenmgr, bool verbose, bool checkopt, std::ostream& out) { + WARTHOG_DEBUG(warthog::io::glog(), alg_name); + WARTHOG_WARNING_FMT(warthog::io::glog(), "Search with {} verbose {} and ceckopt {}", alg_name, verbose, checkopt); warthog::search::search_parameters par; warthog::search::solution sol; auto* expander = algo.get_expander(); diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 391a6ae..919ef16 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -1,5 +1,5 @@ -#ifndef WARTHOG_IO_OBSERVER_H -#define WARTHOG_IO_OBSERVER_H +#ifndef WARTHOG_IO_LOG_H +#define WARTHOG_IO_LOG_H // io/log.h // @@ -16,21 +16,23 @@ #include #include #include +#include +#include +#include #ifdef WARTHOG_LOG #define WARTHOG_DEFAULT_LOG_LEVEL WARTHOG_LOG #elif !defined(NDEBUG) #define WARTHOG_DEFAULT_LOG_LEVEL 1 #else -#define WARTHOG_DEFAULT_LOG_LEVEL 4 +#define WARTHOG_DEFAULT_LOG_LEVEL 2 #endif -#define WARTHOG_TIME_FORMAT "%FT%T%Z" +#define WARTHOG_TIME_FORMAT "%FT%TZ" namespace warthog::io { - enum class log_level { TRACE = 0, @@ -48,24 +50,52 @@ struct log_sink void* data = nullptr; std::array(log_level::NONE)> call = {}; - void log(log_level level, std::string_view msg) + constexpr void log(log_level level, std::string_view msg) const { - if (static_cast(level) < call.size() && (std::bit_cast(data) | std::bit_cast(call[static_cast(level)])) != 0) + if (can_log(level)) { (*call[static_cast(level)])(data, msg); } } + constexpr bool can_log(log_level level) const noexcept + { + return static_cast(level) < call.size() && data != nullptr && call[static_cast(level)] != nullptr; + } - const log_sink& sink() const noexcept { return *this; } + constexpr const log_sink& sink() const noexcept { return *this; } }; -class log_stream_sink : protected log_sink +/// sink to cout|cerr +/// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. +/// User provided file stream is not thread safe. +struct log_std_sink : log_sink +{ + // cerr = true will sink to cerr, false to cout + log_std_sink(); + log_std_sink(bool cerr = true); + log_std_sink(std::ostream* stream); + static void write_trace(void*, std::string_view msg); + static void write_debug(void*, std::string_view msg); + static void write_information(void*, std::string_view msg); + static void write_warning(void*, std::string_view msg); + static void write_error(void*, std::string_view msg); + static void write_critical(void*, std::string_view msg); +}; + +template +concept LogSink = requires (Sink S, log_level level, std::string_view msg) +{ + S.log(level, msg); + { S.can_log(level) } -> std::same_as; + { std::as_const(S).sink() } -> std::convertible_to; +}; + +class log_stream_sink : public log_sink { public: log_stream_sink(); - log_stream_sink(bool err); log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); - log_stream_sink(std::ostream& stream); + log_stream_sink(std::ostream* stream); ~log_stream_sink() = default; void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); @@ -79,6 +109,8 @@ class log_stream_sink : protected log_sink static void write_error(void*, std::string_view msg); static void write_critical(void*, std::string_view msg); + using log_sink::log; + using log_sink::can_log; using log_sink::sink; protected: @@ -86,7 +118,98 @@ class log_stream_sink : protected log_sink std::unique_ptr owned_file_; std::mutex lock_; }; +static_assert(LogSink, "log_stream_sink must be a log_sink."); + +template (WARTHOG_DEFAULT_LOG_LEVEL)> +struct logger : log_sink +{ + constexpr logger() noexcept = default; + constexpr logger(log_sink sink) : log_sink(sink) + { + for (auto& c : call | std::views::take(static_cast(MinLevel))) { + c = nullptr; + } + } + + constexpr logger& operator=(const logger&) noexcept = default; + constexpr logger& operator=(log_sink sink) noexcept + { + for (auto& c : call | std::views::take(static_cast(MinLevel))) { + c = nullptr; + } + return *this; + } + + constexpr void set_min_level(log_level level) noexcept + { + assert(static_cast(level) <= static_cast(log_level::NONE)); + for (auto& c : call | std::views::take(static_cast(level))) { + c = nullptr; + } + } + + constexpr static log_level min_level = MinLevel; + template + constexpr static bool supports_level = MinLevel != log_level::NONE && static_cast(L) >= static_cast(MinLevel); +}; + +namespace details { +template +struct is_logger : std::false_type +{ }; +template +struct is_logger> : std::true_type +{ }; +}; + +template +concept Logger = details::is_logger::value; +template +concept LoggerLevel = Logger && requires { + requires Log::template supports_level; +}; + +#define WARTHOG_LOG(lg,level,msg) lg.log(msg) + +#define WARTHOG_LOG_LEVEL_(lg,level,msg) \ +{if constexpr (::warthog::io::Logger) { \ + if constexpr (::warthog::io::LoggerLevel) { \ + lg.log(level, msg); \ + } \ +} else { \ + lg.log(level, msg); \ +}} + +#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::TRACE,msg) +#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::DEBUG,msg) +#define WARTHOG_INFORMATION(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::INFORMATION,msg) +#define WARTHOG_WARNING(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::WARNING,msg) +#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::ERROR,msg) +#define WARTHOG_CRITICAL(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::CRITICAL,msg) + +#define WARTHOG_LOG_LEVEL_FMT_(lg,level,...) \ +{if constexpr (::warthog::io::Logger) { \ + if constexpr (::warthog::io::LoggerLevel) { \ + lg.log(level, std::format(__VA_ARGS__)); \ + } \ +} else { \ + lg.log(level, std::format(__VA_ARGS__)); \ +}} + +#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::TRACE,__VA_ARGS__) +#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) +#define WARTHOG_INFORMATION_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) +#define WARTHOG_WARNING_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::WARNING,__VA_ARGS__) +#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::ERROR,__VA_ARGS__) +#define WARTHOG_CRITICAL_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) + +// global logger + +using global_logger = logger<>; +global_logger& glog(); +const log_sink& glogs(); +void set_glog(log_sink log); } // namespace warthog::io -#endif // WARTHOG_IO_OBSERVER_H +#endif // WARTHOG_IO_LOG_H diff --git a/src/io/log.cpp b/src/io/log.cpp index 9b849c9..255beab 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -12,23 +12,16 @@ log_stream_sink::log_stream_sink() : log_sink{nullptr, &log_stream_sink::write_error,&log_stream_sink::write_critical}}} { } -log_stream_sink::log_stream_sink(bool err) -{ - if (err) { - data = this; - log_stream_ = &std::cerr; - } -} log_stream_sink::log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode) { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -log_stream_sink::log_stream_sink(std::ostream& stream) +log_stream_sink::log_stream_sink(std::ostream* stream) { data = this; - log_stream_ = &stream; + log_stream_ = stream; } void log_stream_sink::open(const std::filesystem::path& filename, std::ios_base::openmode mode) @@ -50,47 +43,111 @@ void log_stream_sink::open_stderr() log_stream_ = &std::cerr; } +// log_stream_sink + +log_std_sink::log_std_sink() : log_std_sink(nullptr) +{ } +log_std_sink::log_std_sink(bool cerr) : log_std_sink(cerr ? &std::cerr : &std::cout) +{ } +log_std_sink::log_std_sink(std::ostream* stream) : log_sink{ + stream, + {{&log_std_sink::write_trace,&log_std_sink::write_debug, + &log_std_sink::write_information,&log_std_sink::write_warning, + &log_std_sink::write_error,&log_std_sink::write_critical}}} +{ } + +void log_std_sink::write_trace(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_debug(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_information(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_warning(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_error(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} +void log_std_sink::write_critical(void* logger, std::string_view msg) +{ + std::ostream* stream = static_cast(logger); + assert(stream != nullptr); + *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} + void log_stream_sink::write_trace(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_debug(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_information(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_warning(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_error(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } void log_stream_sink::write_critical(void* logger, std::string_view msg) { log_stream_sink* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}", std::chrono::utc_clock::now(), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); +} + +global_logger global_logger_(log_std_sink(true).sink()); +global_logger& glog() +{ + return global_logger_; +} +const log_sink& glogs() +{ + return global_logger_.sink(); +} +void set_glog(log_sink log) +{ + global_logger_ = log; } } // namespace warthog::io From 7a3fb8775a8662ec3fb8200c6ea6bb3e58c60cb4 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 18 Sep 2025 10:07:09 +1000 Subject: [PATCH 10/33] global logger works with uds --- apps/warthog.cpp | 3 - include/warthog/io/log.h | 120 +++++++++++++----- include/warthog/search/search_node.h | 20 +++ .../warthog/search/unidirectional_search.h | 12 +- include/warthog/util/vec_io.h | 6 +- src/io/log.cpp | 72 +++++------ 6 files changed, 154 insertions(+), 79 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index 9269481..a5d4ef1 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include "cfg.h" #include @@ -112,8 +111,6 @@ run_experiments( warthog::util::scenario_manager& scenmgr, bool verbose, bool checkopt, std::ostream& out) { - WARTHOG_DEBUG(warthog::io::glog(), alg_name); - WARTHOG_WARNING_FMT(warthog::io::glog(), "Search with {} verbose {} and ceckopt {}", alg_name, verbose, checkopt); warthog::search::search_parameters par; warthog::search::solution sol; auto* expander = algo.get_expander(); diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 919ef16..0b51ed1 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -68,12 +68,12 @@ struct log_sink /// sink to cout|cerr /// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. /// User provided file stream is not thread safe. -struct log_std_sink : log_sink +struct log_sink_std : log_sink { // cerr = true will sink to cerr, false to cout - log_std_sink(); - log_std_sink(bool cerr = true); - log_std_sink(std::ostream* stream); + log_sink_std(); + log_sink_std(bool cerr = true); + log_sink_std(std::ostream* stream); static void write_trace(void*, std::string_view msg); static void write_debug(void*, std::string_view msg); static void write_information(void*, std::string_view msg); @@ -90,13 +90,13 @@ concept LogSink = requires (Sink S, log_level level, std::string_view msg) { std::as_const(S).sink() } -> std::convertible_to; }; -class log_stream_sink : public log_sink +class log_sink_stream : public log_sink { public: - log_stream_sink(); - log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); - log_stream_sink(std::ostream* stream); - ~log_stream_sink() = default; + log_sink_stream(); + log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + log_sink_stream(std::ostream* stream); + ~log_sink_stream() = default; void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); void open(std::ostream& stream); @@ -118,7 +118,7 @@ class log_stream_sink : public log_sink std::unique_ptr owned_file_; std::mutex lock_; }; -static_assert(LogSink, "log_stream_sink must be a log_sink."); +static_assert(LogSink, "log_stream_sink must be a log_sink."); template (WARTHOG_DEFAULT_LOG_LEVEL)> struct logger : log_sink @@ -171,45 +171,105 @@ concept LoggerLevel = Logger && requires { #define WARTHOG_LOG(lg,level,msg) lg.log(msg) -#define WARTHOG_LOG_LEVEL_(lg,level,msg) \ +#define WARTHOG_LOG_LEVEL_(cond,lg,level,msg) \ {if constexpr (::warthog::io::Logger) { \ if constexpr (::warthog::io::LoggerLevel) { \ - lg.log(level, msg); \ + if (cond) \ + lg.log(level, msg); \ } \ } else { \ - lg.log(level, msg); \ + if (cond) \ + lg.log(level, msg); \ }} -#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::TRACE,msg) -#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::DEBUG,msg) -#define WARTHOG_INFORMATION(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::INFORMATION,msg) -#define WARTHOG_WARNING(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::WARNING,msg) -#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::ERROR,msg) -#define WARTHOG_CRITICAL(lg,msg) WARTHOG_LOG_LEVEL_(lg,::warthog::io::log_level::CRITICAL,msg) +#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::TRACE,msg) +#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::DEBUG,msg) +#define WARTHOG_INFO(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::INFORMATION,msg) +#define WARTHOG_WARN(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::WARNING,msg) +#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::ERROR,msg) +#define WARTHOG_CRIT(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::CRITICAL,msg) -#define WARTHOG_LOG_LEVEL_FMT_(lg,level,...) \ +#define WARTHOG_TRACE_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::TRACE,msg) +#define WARTHOG_DEBUG_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::DEBUG,msg) +#define WARTHOG_INFO_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::INFORMATION,msg) +#define WARTHOG_WARN_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::WARNING,msg) +#define WARTHOG_ERROR_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::ERROR,msg) +#define WARTHOG_CRIT_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::CRITICAL,msg) + +#define WARTHOG_LOG_LEVEL_FMT_(cond,lg,level,...) \ {if constexpr (::warthog::io::Logger) { \ if constexpr (::warthog::io::LoggerLevel) { \ - lg.log(level, std::format(__VA_ARGS__)); \ + if (cond) \ + lg.log(level, std::format(__VA_ARGS__)); \ } \ } else { \ - lg.log(level, std::format(__VA_ARGS__)); \ + if (cond) \ + lg.log(level, std::format(__VA_ARGS__)); \ }} -#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::TRACE,__VA_ARGS__) -#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) -#define WARTHOG_INFORMATION_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) -#define WARTHOG_WARNING_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::WARNING,__VA_ARGS__) -#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::ERROR,__VA_ARGS__) -#define WARTHOG_CRITICAL_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) +#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) +#define WARTHOG_INFO_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) +#define WARTHOG_WARN_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) +#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) +#define WARTHOG_CRIT_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) + +#define WARTHOG_TRACE_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) +#define WARTHOG_DEBUG_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) +#define WARTHOG_INFO_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) +#define WARTHOG_WARN_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) +#define WARTHOG_ERROR_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) +#define WARTHOG_CRIT_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) // global logger -using global_logger = logger<>; -global_logger& glog(); +using global_logger_type = logger<>; +global_logger_type& glog(); const log_sink& glogs(); void set_glog(log_sink log); +template +struct global_logger : logger +{ + using logger = typename global_logger::logger; + constexpr global_logger() : logger(glog().sink()) + { } + + constexpr global_logger& operator=(const global_logger&) noexcept = default; + + void resync_global() + { + static_cast(*this) = glog().sink(); + } +}; + +// defines to the global logger + +#define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(),msg) +#define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(),msg) +#define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(),msg) +#define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(),msg) +#define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(),msg) +#define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(),msg) +#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(::warthog::io::glog(),msg) +#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(::warthog::io::glog(),msg) +#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(::warthog::io::glog(),msg) +#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(::warthog::io::glog(),msg) +#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(::warthog::io::glog(),msg) +#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(::warthog::io::glog(),msg) +#define WARTHOG_GTRACE_FMT(...) WARTHOG_TRACE_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GDEBUG_FMT(...) WARTHOG_DEBUG_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GINFO_FMT(...) WARTHOG_INFO_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GWARN_FMT(...) WARTHOG_WARN_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GERROR_FMT(...) WARTHOG_ERROR_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GCRIT_FMT(...) WARTHOG_CRIT_FMT(::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GTRACE_FMT_IF(cond,...) WARTHOG_TRACE_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GDEBUG_FMT_IF(cond,...) WARTHOG_DEBUG_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GINFO_FMT_IF(cond,...) WARTHOG_INFO_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GWARN_FMT_IF(cond,...) WARTHOG_WARN_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GERROR_FMT_IF(cond,...) WARTHOG_ERROR_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GCRIT_FMT_IF(cond,...) WARTHOG_CRIT_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) + } // namespace warthog::io #endif // WARTHOG_IO_LOG_H diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index a0cddf9..7ac705f 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -9,6 +9,7 @@ #include #include +#include #include @@ -273,4 +274,23 @@ struct cmp_less_search_node_f_only std::ostream& operator<<(std::ostream& str, const warthog::search::search_node& sn); +template <> +struct std::formatter<::warthog::search::search_node, char> +{ + template + constexpr auto parse(ParseContext& ctx) const + { + return ctx.begin(); + } + + template + FmtContext::iterator format(const ::warthog::search::search_node& s, FmtContext& ctx) const + { + return std::format_to(ctx.out(), "search_node id:{} p_id:{} g:{} f:{} ub:{} expanded:{} search_number:{}", + s.get_id().id, s.get_parent().id, + s.get_g(), s.get_f(), s.get_ub(), + s.get_expanded(), s.get_search_number()); + } +}; + #endif // WARTHOG_SEARCH_SEARCH_NODE_H diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 25b2803..3418490 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -226,7 +226,7 @@ class unidirectional_search open_->push(start); io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); user(pi->verbose_, pi); - trace(pi->verbose_, "Start node:", *start); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Start node: {}", *start); update_ub(start, sol, pi); } @@ -249,7 +249,7 @@ class unidirectional_search sol->met_.nodes_expanded_++; sol->met_.lb_ = current->get_f(); io::observer_expand_node(listeners_, *current); - trace(pi->verbose_, "Expanding:", *current); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Expanding: {}", *current); // Generate successors of the current node search_node* n = nullptr; @@ -268,7 +268,7 @@ class unidirectional_search if(n->get_f() <= sol->sum_of_edge_costs_) { open_->push(n); - trace(pi->verbose_, "Generate:", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Generate: {}", *n); update_ub(current, sol, pi); io::observer_generate_node(listeners_, current, *n, gval, i); continue; @@ -288,7 +288,7 @@ class unidirectional_search if(open_->contains(n)) { open_->decrease_key(n); - trace(pi->verbose_, "Updating;", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Updating: {}", *n); update_ub(current, sol, pi); continue; } @@ -296,14 +296,14 @@ class unidirectional_search if(reopen()) { open_->push(n); - trace(pi->verbose_, "Reopen;", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Reopen: {}", *n); update_ub(current, sol, pi); sol->met_.nodes_reopen_++; continue; } } } - trace(pi->verbose_, "Dominated;", *n); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Dominated: {}", *n); } if constexpr(FC == feasibility_criteria::until_cutoff) { diff --git a/include/warthog/util/vec_io.h b/include/warthog/util/vec_io.h index 6d82c92..8f0e24c 100644 --- a/include/warthog/util/vec_io.h +++ b/include/warthog/util/vec_io.h @@ -1,7 +1,7 @@ #ifndef WARTHOG_UTIL_VEC_IO_H #define WARTHOG_UTIL_VEC_IO_H -#include "log.h" +#include #include #include #include @@ -95,9 +95,7 @@ load_vector(std::FILE* file) size_t stuffRead = std::fread(&v[0], sizeof(T), s, file); if((int)stuffRead != s) { - error( - "we were expecting to read ", s, " but we read", stuffRead, - "elements instead"); + WARTHOG_GERROR_FMT("we were expecting to read {} but we read {} elements instead", s, stuffRead); throw std::runtime_error("std::fread failed"); } diff --git a/src/io/log.cpp b/src/io/log.cpp index 255beab..3326041 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -6,138 +6,138 @@ namespace warthog::io { -log_stream_sink::log_stream_sink() : log_sink{nullptr, - {{&log_stream_sink::write_trace,&log_stream_sink::write_debug, - &log_stream_sink::write_information,&log_stream_sink::write_warning, - &log_stream_sink::write_error,&log_stream_sink::write_critical}}} +log_sink_stream::log_sink_stream() : log_sink{nullptr, + {{&log_sink_stream::write_trace,&log_sink_stream::write_debug, + &log_sink_stream::write_information,&log_sink_stream::write_warning, + &log_sink_stream::write_error,&log_sink_stream::write_critical}}} { } -log_stream_sink::log_stream_sink(const std::filesystem::path& filename, std::ios_base::openmode mode) +log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -log_stream_sink::log_stream_sink(std::ostream* stream) +log_sink_stream::log_sink_stream(std::ostream* stream) { data = this; log_stream_ = stream; } -void log_stream_sink::open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void log_sink_stream::open(const std::filesystem::path& filename, std::ios_base::openmode mode) { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -void log_stream_sink::open(std::ostream& stream) +void log_sink_stream::open(std::ostream& stream) { data = this; owned_file_.release(); log_stream_ = &stream; } -void log_stream_sink::open_stderr() +void log_sink_stream::open_stderr() { data = this; owned_file_.release(); log_stream_ = &std::cerr; } -// log_stream_sink +// log_sink_stream -log_std_sink::log_std_sink() : log_std_sink(nullptr) +log_sink_std::log_sink_std() : log_sink_std(nullptr) { } -log_std_sink::log_std_sink(bool cerr) : log_std_sink(cerr ? &std::cerr : &std::cout) +log_sink_std::log_sink_std(bool cerr) : log_sink_std(cerr ? &std::cerr : &std::cout) { } -log_std_sink::log_std_sink(std::ostream* stream) : log_sink{ +log_sink_std::log_sink_std(std::ostream* stream) : log_sink{ stream, - {{&log_std_sink::write_trace,&log_std_sink::write_debug, - &log_std_sink::write_information,&log_std_sink::write_warning, - &log_std_sink::write_error,&log_std_sink::write_critical}}} + {{&log_sink_std::write_trace,&log_sink_std::write_debug, + &log_sink_std::write_information,&log_sink_std::write_warning, + &log_sink_std::write_error,&log_sink_std::write_critical}}} { } -void log_std_sink::write_trace(void* logger, std::string_view msg) +void log_sink_std::write_trace(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_debug(void* logger, std::string_view msg) +void log_sink_std::write_debug(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_information(void* logger, std::string_view msg) +void log_sink_std::write_information(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_warning(void* logger, std::string_view msg) +void log_sink_std::write_warning(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_error(void* logger, std::string_view msg) +void log_sink_std::write_error(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_std_sink::write_critical(void* logger, std::string_view msg) +void log_sink_std::write_critical(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_trace(void* logger, std::string_view msg) +void log_sink_stream::write_trace(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_debug(void* logger, std::string_view msg) +void log_sink_stream::write_debug(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_information(void* logger, std::string_view msg) +void log_sink_stream::write_information(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_warning(void* logger, std::string_view msg) +void log_sink_stream::write_warning(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_error(void* logger, std::string_view msg) +void log_sink_stream::write_error(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -void log_stream_sink::write_critical(void* logger, std::string_view msg) +void log_sink_stream::write_critical(void* logger, std::string_view msg) { - log_stream_sink* log_stream = static_cast(logger); + log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); } -global_logger global_logger_(log_std_sink(true).sink()); -global_logger& glog() +global_logger_type global_logger_(log_sink_std(true).sink()); +global_logger_type& glog() { return global_logger_; } From 8dd72e449b9d574355b35e54cdcedbca66d19937 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 18 Sep 2025 11:00:03 +1000 Subject: [PATCH 11/33] update time to local time --- include/warthog/io/log.h | 3 ++- src/io/log.cpp | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 0b51ed1..4942bd9 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -28,7 +28,8 @@ #define WARTHOG_DEFAULT_LOG_LEVEL 2 #endif -#define WARTHOG_TIME_FORMAT "%FT%TZ" +#define WARTHOG_LOG_TIME_FORMAT "%F %T" +#define WARTHOG_LOG_NOW (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) namespace warthog::io { diff --git a/src/io/log.cpp b/src/io/log.cpp index 3326041..cae584c 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -60,37 +60,37 @@ void log_sink_std::write_trace(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_debug(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_information(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_warning(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_error(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_std::write_critical(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_trace(void* logger, std::string_view msg) @@ -98,42 +98,42 @@ void log_sink_stream::write_trace(void* logger, std::string_view msg) log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_debug(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_information(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_warning(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_error(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } void log_sink_stream::write_critical(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr && log_stream->log_stream_); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(std::chrono::utc_clock::now()), msg); + *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); } global_logger_type global_logger_(log_sink_std(true).sink()); From 7eb6f450d031e2949ac3dbe5dd6d648b9635d890 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 1 Oct 2025 08:28:00 +1000 Subject: [PATCH 12/33] update log interface --- src/io/log.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/log.cpp b/src/io/log.cpp index cae584c..331d61b 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -12,13 +12,13 @@ log_sink_stream::log_sink_stream() : log_sink{nullptr, &log_sink_stream::write_error,&log_sink_stream::write_critical}}} { } -log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) +log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) : log_sink_stream() { data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -log_sink_stream::log_sink_stream(std::ostream* stream) +log_sink_stream::log_sink_stream(std::ostream* stream) : log_sink_stream() { data = this; log_stream_ = stream; From 798de27177e524f73ad67cce8ca1370d4890c46f Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 1 Oct 2025 13:05:42 +1000 Subject: [PATCH 13/33] ported to new logger --- include/warthog/io/log.h | 29 ++- include/warthog/search/problem_instance.h | 25 +- include/warthog/search/search_metrics.h | 6 +- include/warthog/search/uds_traits.h | 14 +- .../warthog/search/unidirectional_search.h | 31 +-- include/warthog/util/log.h | 237 ------------------ src/io/log.cpp | 38 +-- src/search/problem_instance.cpp | 9 +- src/search/search_metrics.cpp | 4 + src/util/file_utils.cpp | 1 - 10 files changed, 77 insertions(+), 317 deletions(-) delete mode 100644 include/warthog/util/log.h diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 4942bd9..fea3251 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -66,6 +66,14 @@ struct log_sink constexpr const log_sink& sink() const noexcept { return *this; } }; +template +concept LogSink = requires (Sink S, log_level level, std::string_view msg) +{ + S.log(level, msg); + { S.can_log(level) } -> std::same_as; + { std::as_const(S).sink() } -> std::convertible_to; +}; + /// sink to cout|cerr /// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. /// User provided file stream is not thread safe. @@ -74,7 +82,6 @@ struct log_sink_std : log_sink // cerr = true will sink to cerr, false to cout log_sink_std(); log_sink_std(bool cerr = true); - log_sink_std(std::ostream* stream); static void write_trace(void*, std::string_view msg); static void write_debug(void*, std::string_view msg); static void write_information(void*, std::string_view msg); @@ -83,14 +90,6 @@ struct log_sink_std : log_sink static void write_critical(void*, std::string_view msg); }; -template -concept LogSink = requires (Sink S, log_level level, std::string_view msg) -{ - S.log(level, msg); - { S.can_log(level) } -> std::same_as; - { std::as_const(S).sink() } -> std::convertible_to; -}; - class log_sink_stream : public log_sink { public: @@ -252,12 +251,12 @@ struct global_logger : logger #define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(),msg) #define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(),msg) #define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(),msg) -#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(::warthog::io::glog(),msg) -#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(::warthog::io::glog(),msg) -#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(::warthog::io::glog(),msg) -#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(::warthog::io::glog(),msg) -#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(::warthog::io::glog(),msg) -#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(::warthog::io::glog(),msg) +#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(cond,::warthog::io::glog(),msg) +#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(cond,::warthog::io::glog(),msg) #define WARTHOG_GTRACE_FMT(...) WARTHOG_TRACE_FMT(::warthog::io::glog(),__VA_ARGS__) #define WARTHOG_GDEBUG_FMT(...) WARTHOG_DEBUG_FMT(::warthog::io::glog(),__VA_ARGS__) #define WARTHOG_GINFO_FMT(...) WARTHOG_INFO_FMT(::warthog::io::glog(),__VA_ARGS__) diff --git a/include/warthog/search/problem_instance.h b/include/warthog/search/problem_instance.h index 9a7a271..eb16556 100644 --- a/include/warthog/search/problem_instance.h +++ b/include/warthog/search/problem_instance.h @@ -85,12 +85,29 @@ convert_problem_instance_to_search(const problem_instance& pi, Domain& d) start, target, pi.instance_id_, pi.verbose_, pi.extra_params_}; } -} // namespace warthog::search - std::ostream& -operator<<(std::ostream& str, const warthog::search::problem_instance& pi); +operator<<(std::ostream& str, const problem_instance& pi); std::ostream& operator<<( - std::ostream& str, const warthog::search::search_problem_instance& pi); + std::ostream& str, const search_problem_instance& pi); + +} // namespace warthog::search + +template<::warthog::Identity STATE> +struct std::formatter<::warthog::search::problem_instance_base, char> +{ + template + constexpr auto parse(ParseContext& ctx) const + { + return ctx.begin(); + } + + template + FmtContext::iterator format(const ::warthog::search::problem_instance_base& pi, FmtContext& ctx) const + { + return std::format_to(ctx.out(), "problem instance[{}]; start:{} target:{} search_id:{}", + typeid(typename STATE::tag).name(), pi.start_.id, pi.target_.id, pi.instance_id_); + } +}; #endif // WARTHOG_SEARCH_PROBLEM_INSTANCE_H diff --git a/include/warthog/search/search_metrics.h b/include/warthog/search/search_metrics.h index d54108b..76b8a43 100644 --- a/include/warthog/search/search_metrics.h +++ b/include/warthog/search/search_metrics.h @@ -62,9 +62,9 @@ struct search_metrics // warthog::search_metrics& met); }; -} // namespace warthog::search - std::ostream& -operator<<(std::ostream& str, const warthog::search::search_metrics& met); +operator<<(std::ostream& str, const search_metrics& met); + +} // namespace warthog::search #endif // WARTHOG_SEARCH_SEARCH_METRICS_H diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 8e51229..b22378e 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -13,7 +13,7 @@ // #include "search_metrics.h" -#include +#include namespace warthog::search { @@ -99,25 +99,19 @@ feasible( if(next->get_f() > par->get_max_cost_cutoff()) { - info( - par->verbose_, "cost cutoff", next->get_f(), ">", - par->get_max_cost_cutoff()); + WARTHOG_GINFO_FMT_IF(par->verbose_, "cost cutoff {} > {}", next->get_f(), par->get_max_cost_cutoff()); return false; } if(met->nodes_expanded_ >= par->get_max_expansions_cutoff()) { - info( - par->verbose_, "expansions cutoff", met->nodes_expanded_, ">", - par->get_max_expansions_cutoff()); + WARTHOG_GINFO_FMT_IF(par->verbose_, "expansions cutoff {} > {}", met->nodes_expanded_, par->get_max_expansions_cutoff()); return false; } if(met->time_elapsed_nano_ > par->get_max_time_cutoff()) { - info( - par->verbose_, "time cutoff", met->time_elapsed_nano_, ">", - par->get_max_time_cutoff()); + WARTHOG_GINFO_FMT_IF(par->verbose_, "time cutoff {} > {}", met->time_elapsed_nano_, par->get_max_time_cutoff()); return false; } diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 3418490..3ab8c8c 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -95,21 +94,6 @@ class unidirectional_search sol->s_node_->get_id(), spi->target_, &sol->path_); heuristic_->h(&hv); } - - DO_ON_DEBUG_IF(spi->verbose_) - { - for(auto& node_id : sol->path_) - { - int32_t x, y; - expander_->get_xy(node_id, x, y); - std::cerr << "final path: (" << x << ", " << y << ")..."; - search_node* n - = expander_->generate(expander_->unget_state(node_id)); - assert(n->get_search_number() == spi->instance_id_); - n->print(std::cerr); - std::cerr << std::endl; - } - } } L& @@ -198,9 +182,7 @@ class unidirectional_search if(n->get_ub() < sol->met_.ub_) { sol->met_.ub_ = n->get_ub(); - debug( - pi->verbose_, "NEW UB:", "Incumbent Cost", - sol->sum_of_edge_costs_); + WARTHOG_GDEBUG_FMT_IF(pi->verbose_, "NEW UB: Incumbent Cost {}", sol->sum_of_edge_costs_); } } @@ -225,7 +207,7 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); - user(pi->verbose_, pi); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "{}", *pi); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Start node: {}", *start); update_ub(start, sol, pi); } @@ -317,14 +299,7 @@ class unidirectional_search sol->met_.nodes_surplus_ = open_->size(); sol->met_.heap_ops_ = open_->get_heap_ops(); - DO_ON_DEBUG_IF(pi->verbose_) - { - if(sol->sum_of_edge_costs_ == warthog::COST_MAX) - { - warning(pi->verbose_, "Search failed; no solution exists."); - } - else { user(pi->verbose_, "Solution found", *sol->s_node_); } - } + WARTHOG_GINFO_IF(pi->verbose_ && sol->sum_of_edge_costs_ == warthog::COST_MAX, "Search failed; no solution exists."); } }; diff --git a/include/warthog/util/log.h b/include/warthog/util/log.h deleted file mode 100644 index 90dfcae..0000000 --- a/include/warthog/util/log.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - * @file - * - * Simple log utility. Can work both on preprocessor time or at compile time - * - * All this function works only if: - * @li the macro @c DEBUG is active OR; - * @li the macro @c NDEBUG is not active; - * - * Otherwise, they will become just empty (or can be easily optimized away) - * - * @date Oct 1, 2018 - * @author Massimo Bono - */ - -#ifndef WARTHOG_UTIL_LOG_H -#define WARTHOG_UTIL_LOG_H - -#include "file_utils.h" -#include "macros.h" -#include -#include - -#ifndef QUICK_LOG -#define QUICK_LOG 0 -#endif - -#if QUICK_LOG <= 0 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define debug(p, ...) _abstractLog("DEBUG", p, __VA_ARGS__) -#else -#define debug(...) {}; -#endif - -#if QUICK_LOG <= 1 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define trace(p, ...) _abstractLog("TRACE", p, __VA_ARGS__) -#else -#define trace(...) {}; -#endif - -#if QUICK_LOG <= 4 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define info(p, ...) _abstractLog("INFO ", p, __VA_ARGS__) -#else -#define info(...) {}; -#endif - -#if QUICK_LOG <= 5 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define user(p, ...) _abstractLog("USER ", p, __VA_ARGS__) -#else -#define user(...) {}; -#endif - -#if QUICK_LOG <= 6 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define warning(p, ...) _abstractLog("WARN ", p, __VA_ARGS__) -#else -#define warning(...) {}; -#endif - -#if QUICK_LOG <= 7 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define error(...) _abstractLog("ERROR", true, __VA_ARGS__) -#else -#define error(...) {}; -#endif - -#if QUICK_LOG <= 8 -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] ... the entries to put on stream - */ -#define critical(...) _abstractLog("CRTCL", true, __VA_ARGS__) -#else -#define critical(...) {}; -#endif - -/** - * like ::log but will log only if at runtime the @c expr will evaluate to true - * - * Will impact performances (even if by little) even if the log is turned off - * - * @code - * clog(true)("hello ", this->name); - * @endcode - * - * @param[in] expr the expression to be evaluated - * @param[in] ... input for ::log - */ -#define clog(expr) _clog1(expr) - -#define _clog1(expr) \ - if(expr) \ - { \ - _clog2 -#define _clog2(...) \ - debug(__VA_ARGS__); \ - } - -/** - * Condition Statement Log utility allows to perform some previous statement - * before logging - * - * This logging macro is useful when your logging needs some local variable - * - * @code - * cslog(true)(int a = 5)("a is %d", a); - * @endcode - * - * @param[in] expr the condition to check if the log is enabled - */ -#define cslog(expr) _cslog1(expr) -#define _cslog1(expr) \ - if(expr) \ - { \ - _cslog2 -#define _cslog2(...) \ - __VA_ARGS__; \ - _cslog3 -#define _cslog3(...) \ - debug(__VA_ARGS__); \ - } - -#define _LOG_OP(context, index, x) x -#define _LOG_COMB(context, x, y) x << " " << y - -#define _debug(...) FOR_EACH(, _LOG_OP, _LOG_COMB, __VA_ARGS__) - -#if defined(DEBUG) || !defined(NDEBUG) -/** - * Perform some work if debug has been enabled - * - * - */ -#define DO_ON_DEBUG_IF(expr) if(expr) -#define DO_ON_DEBUG if(true) - -/** - * always log an entry via std cio - * - * Does no impact performances whatsoever - * - * @code - * debug("hello", person->name, "!"); - * @endcode - * - * @param[in] level the name of the log level - * @param[in] ... the entries to put on stream - */ -#define _abstractLog(level, p, ...) \ - { \ - constexpr auto src = std::source_location::current(); \ - if(p) \ - std::cerr << "[" << level << "] " \ - << warthog::util::getBaseName_(src.file_name()) << "@" \ - << __func__ << ":" << src.line() << " " \ - << _debug(__VA_ARGS__) << std::endl; \ - } - -#else - -#define DO_ON_DEBUG_IF(expr) if(false) -#define DO_ON_DEBUG if(false) -#define _abstractLog(level, ...) {}; - -#endif - -#endif // WARTHOG_UTIL_LOG_H diff --git a/src/io/log.cpp b/src/io/log.cpp index 331d61b..d265548 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -45,12 +45,14 @@ void log_sink_stream::open_stderr() // log_sink_stream -log_sink_std::log_sink_std() : log_sink_std(nullptr) -{ } -log_sink_std::log_sink_std(bool cerr) : log_sink_std(cerr ? &std::cerr : &std::cout) +log_sink_std::log_sink_std() : log_sink{ + nullptr, + {{&log_sink_std::write_trace,&log_sink_std::write_debug, + &log_sink_std::write_information,&log_sink_std::write_warning, + &log_sink_std::write_error,&log_sink_std::write_critical}}} { } -log_sink_std::log_sink_std(std::ostream* stream) : log_sink{ - stream, +log_sink_std::log_sink_std(bool cerr) : log_sink{ + cerr ? &std::cerr : &std::cout, {{&log_sink_std::write_trace,&log_sink_std::write_debug, &log_sink_std::write_information,&log_sink_std::write_warning, &log_sink_std::write_error,&log_sink_std::write_critical}}} @@ -96,47 +98,49 @@ void log_sink_std::write_critical(void* logger, std::string_view msg) void log_sink_stream::write_trace(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_trace(log_stream->log_stream_, msg); } void log_sink_stream::write_debug(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_debug(log_stream->log_stream_, msg); } void log_sink_stream::write_information(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_information(log_stream->log_stream_, msg); } void log_sink_stream::write_warning(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_warning(log_stream->log_stream_, msg); } void log_sink_stream::write_error(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_error(log_stream->log_stream_, msg); } void log_sink_stream::write_critical(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); - assert(log_stream != nullptr && log_stream->log_stream_); + assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); - *log_stream->log_stream_ << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + log_sink_std::write_critical(log_stream->log_stream_, msg); } +namespace { global_logger_type global_logger_(log_sink_std(true).sink()); +} global_logger_type& glog() { return global_logger_; diff --git a/src/search/problem_instance.cpp b/src/search/problem_instance.cpp index 2239a7f..7022a1a 100644 --- a/src/search/problem_instance.cpp +++ b/src/search/problem_instance.cpp @@ -1,7 +1,10 @@ #include +namespace warthog::search +{ + std::ostream& -operator<<(std::ostream& str, const warthog::search::problem_instance& pi) +operator<<(std::ostream& str, const problem_instance& pi) { pi.print(str); @@ -10,9 +13,11 @@ operator<<(std::ostream& str, const warthog::search::problem_instance& pi) std::ostream& operator<<( - std::ostream& str, const warthog::search::search_problem_instance& pi) + std::ostream& str, const search_problem_instance& pi) { pi.print(str); return str; } + +} diff --git a/src/search/search_metrics.cpp b/src/search/search_metrics.cpp index a2a9fb7..910ac13 100644 --- a/src/search/search_metrics.cpp +++ b/src/search/search_metrics.cpp @@ -1,5 +1,7 @@ #include +namespace warthog::search { + std::ostream& operator<<(std::ostream& str, const warthog::search::search_metrics& met) { @@ -11,3 +13,5 @@ operator<<(std::ostream& str, const warthog::search::search_metrics& met) << " lb=" << met.lb_ << " ub=" << met.ub_; return str; } + +} diff --git a/src/util/file_utils.cpp b/src/util/file_utils.cpp index 1944804..006f472 100644 --- a/src/util/file_utils.cpp +++ b/src/util/file_utils.cpp @@ -9,7 +9,6 @@ #include #include #include -#include namespace warthog::util { From 47bf302de38763329e9a43bb6337e15161074950 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 11:34:18 +1000 Subject: [PATCH 14/33] added logger comments --- cmake/headers.cmake | 2 +- include/warthog/io/log.h | 88 +++++++++++++++++++++++++++++++++++----- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/cmake/headers.cmake b/cmake/headers.cmake index b7d60c5..46e1ba6 100644 --- a/cmake/headers.cmake +++ b/cmake/headers.cmake @@ -27,6 +27,7 @@ include/warthog/heuristic/octile_heuristic.h include/warthog/heuristic/zero_heuristic.h include/warthog/io/grid.h +include/warthog/io/log.h include/warthog/memory/arraylist.h include/warthog/memory/bittable.h @@ -56,7 +57,6 @@ include/warthog/util/file_utils.h include/warthog/util/gm_parser.h include/warthog/util/helpers.h include/warthog/util/intrin.h -include/warthog/util/log.h include/warthog/util/macros.h include/warthog/util/pqueue.h include/warthog/util/scenario_manager.h diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index fea3251..7569d38 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -3,7 +3,36 @@ // io/log.h // -// Logger class. +// Logging utility. +// +// The log_sink is a non-owning copyable struct that points to the data and logging function calls. +// All logging is performed through log_sink. +// All classes here are thread safe, follow comments for outlaying cases. +// +// Special classes inherit log_sink to provide default functionality. +// log_sink_std should be used to write to std::cout and std::cerr. +// log_sink_stream pipe to a stream or open a file stream. +// +// The logs are made to be logged to a certain log_level. +// The WARTHOG_TRACE and others in this header provide interfaces to log to a special +// logger<> class. This logger<> class knows at compile time the minimum level to log, +// and with the use of a macro will be compiled out if that level was not set at compile time +// when used with the logging macros. +// +// The global logger can be acquired with the glog() (logger<>) or glogs() (log_sink), and set with +// set_glog(log_sink). The log level of the global logger is set though the WARTHOG_LOG definition, +// with a default of 1(debug) for debug builds, and 2(information) for release (NDEBUG defined). +// Macros like WARTHOG_GTRACE will automatically write to the global logger. +// +// The _IF will only log if a runtime if true. +// The _FMT uses the std::format from C++20 to format the output messages. +// The logging utilities uses dynamic memory std::strings to produce the final log message strings, +// thus is better to be disabled for release builds through the log level. +// +// Log messages produce a messaged as "[TIME LEVEL] msg". +// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be overridden with +// define cstring WARTHOG_LOG_TIME. +// Clock is in local system time, but can be set to UTC by definiong WARTHOG_LOG_CLOCK_UTC. // // @author: Ryan Hechenberger // @created: 2025-09-09 @@ -20,6 +49,7 @@ #include #include +// default log levels, also for global logger #ifdef WARTHOG_LOG #define WARTHOG_DEFAULT_LOG_LEVEL WARTHOG_LOG #elif !defined(NDEBUG) @@ -28,8 +58,19 @@ #define WARTHOG_DEFAULT_LOG_LEVEL 2 #endif +// specify the log time format +#ifdef WARTHOG_LOG_TIME +#define WARTHOG_LOG_TIME_FORMAT WARTHOG_LOG_TIME +#else #define WARTHOG_LOG_TIME_FORMAT "%F %T" +#endif + +// set to use utc time instead of local +#ifdef WARTHOG_LOG_CLOCK_UTC +#define WARTHOG_LOG_NOW (std::chrono::utc_clock::now()) +#else #define WARTHOG_LOG_NOW (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) +#endif namespace warthog::io { @@ -45,6 +86,11 @@ enum class log_level NONE = 6 }; +/// @brief the log class used to write out to a log. +/// +/// Takes a pointer for data (must not be null to write). +/// The array call is the logger for each level. +/// log will write if able (data != null && call[level] != null). struct log_sink { using call_type = void (void*, std::string_view msg); @@ -66,6 +112,7 @@ struct log_sink constexpr const log_sink& sink() const noexcept { return *this; } }; +/// @brief concept that check if class is shaped with a log_sink. template concept LogSink = requires (Sink S, log_level level, std::string_view msg) { @@ -74,9 +121,10 @@ concept LogSink = requires (Sink S, log_level level, std::string_view msg) { std::as_const(S).sink() } -> std::convertible_to; }; -/// sink to cout|cerr -/// Is thread safe as long as std::ios_base::sync_with_stdio(false) for cout and cerr. -/// User provided file stream is not thread safe. +/// @brief sink to cout|cerr +/// +/// Is thread safe as long as std::ios_base::sync_with_stdio(false) has not been called. +/// Can copy go log_sink and then be destructed without issue. struct log_sink_std : log_sink { // cerr = true will sink to cerr, false to cout @@ -90,6 +138,10 @@ struct log_sink_std : log_sink static void write_critical(void*, std::string_view msg); }; +/// @brief sink to a passed stream, or open and own a filestream. +/// +/// This class is thread-safe through use of a mutex. +/// Must be kept in scope at all times while its log_sink is in use. class log_sink_stream : public log_sink { public: @@ -109,10 +161,6 @@ class log_sink_stream : public log_sink static void write_error(void*, std::string_view msg); static void write_critical(void*, std::string_view msg); - using log_sink::log; - using log_sink::can_log; - using log_sink::sink; - protected: std::ostream* log_stream_ = nullptr; std::unique_ptr owned_file_; @@ -120,6 +168,15 @@ class log_sink_stream : public log_sink }; static_assert(LogSink, "log_stream_sink must be a log_sink."); +/// @brief the logger class that manipulates a log_sink. +/// @tparam MinLevel the minimum logging level, derived at compile time. +/// Defaults to the programs default log level. +/// +/// A wrapper class for a log_sink that allows to control the log_level. +/// If just using a log_sink, it will always log at every level. +/// This class is handled specially by the logging macros to only log if log_level +/// is a min_level. +/// When setting a long_sink, will clear all pointers below MinLevel. template (WARTHOG_DEFAULT_LOG_LEVEL)> struct logger : log_sink { @@ -140,6 +197,8 @@ struct logger : log_sink return *this; } + /// @brief Overrides MinLevel to level. Can only raise, not lower it. + /// @param level constexpr void set_min_level(log_level level) noexcept { assert(static_cast(level) <= static_cast(log_level::NONE)); @@ -182,6 +241,7 @@ concept LoggerLevel = Logger && requires { lg.log(level, msg); \ }} +// Write msg (string_view) to log lg. #define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::TRACE,msg) #define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::DEBUG,msg) #define WARTHOG_INFO(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::INFORMATION,msg) @@ -189,6 +249,7 @@ concept LoggerLevel = Logger && requires { #define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::ERROR,msg) #define WARTHOG_CRIT(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::CRITICAL,msg) +// Write msg (string_view) to log lg if runtime cond is true. #define WARTHOG_TRACE_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::TRACE,msg) #define WARTHOG_DEBUG_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::DEBUG,msg) #define WARTHOG_INFO_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::INFORMATION,msg) @@ -207,6 +268,7 @@ concept LoggerLevel = Logger && requires { lg.log(level, std::format(__VA_ARGS__)); \ }} +// Write formatted message to log, pass as format,args... #define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) #define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) #define WARTHOG_INFO_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) @@ -214,6 +276,7 @@ concept LoggerLevel = Logger && requires { #define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) #define WARTHOG_CRIT_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +// Write formatted message to log if cond is true, pass as format,args... #define WARTHOG_TRACE_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) #define WARTHOG_DEBUG_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) #define WARTHOG_INFO_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) @@ -224,10 +287,15 @@ concept LoggerLevel = Logger && requires { // global logger using global_logger_type = logger<>; +/// @brief get the global logger, default is set to std::cerr through log_sink_std global_logger_type& glog(); +/// @brief get the global log_sink const log_sink& glogs(); +/// @brief sets the global log_sink void set_glog(log_sink log); +/// @brief A logger that sets itself to glog() +/// @tparam MinLevel template struct global_logger : logger { @@ -237,14 +305,14 @@ struct global_logger : logger constexpr global_logger& operator=(const global_logger&) noexcept = default; + /// @brief Updates the sink to the new glog sink. Must be called if set_glog was used. void resync_global() { static_cast(*this) = glog().sink(); } }; -// defines to the global logger - +// the global version of the marcos, omits the passing of a logger. #define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(),msg) #define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(),msg) #define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(),msg) From ecfc59db60c714e279d04ee2f1d6f91a8f7c44fd Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 15:23:47 +1000 Subject: [PATCH 15/33] update posthoc trace with comments and trace rendering; added support in warthog-app --- apps/cfg.cpp | 2 +- apps/cfg.h | 2 +- apps/warthog.cpp | 93 +++++++++++++++++----------- cmake/config.h.in | 1 + include/warthog/io/grid_trace.h | 14 ++--- include/warthog/io/observer.h | 17 +++-- include/warthog/io/posthoc_trace.h | 10 ++- include/warthog/io/stream_observer.h | 27 +++++--- src/io/traces.cpp | 36 ++++++++--- test.cpp | 21 +++++++ 10 files changed, 154 insertions(+), 69 deletions(-) create mode 100644 test.cpp diff --git a/apps/cfg.cpp b/apps/cfg.cpp index 12ff66b..a9d9f52 100644 --- a/apps/cfg.cpp +++ b/apps/cfg.cpp @@ -39,7 +39,7 @@ warthog::util::cfg::parse_args( std::string warthog::util::cfg::get_param_value(std::string param_name) { - std::string ret(""); + std::string ret; std::map>::iterator it = params_.find(param_name); if(it != params_.end()) diff --git a/apps/cfg.h b/apps/cfg.h index 3f804d9..8a1e8c0 100644 --- a/apps/cfg.h +++ b/apps/cfg.h @@ -1,7 +1,7 @@ #ifndef WARTHOG_APP_CFG_H #define WARTHOG_APP_CFG_H -#include "getopt.h" +#include #include #include diff --git a/apps/warthog.cpp b/apps/warthog.cpp index a5d4ef1..d3a7d40 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -20,6 +20,9 @@ #include #include #include +#ifdef WARTHOG_POSTHOC +#include +#endif #include "cfg.h" #include @@ -44,6 +47,16 @@ int checkopt = 0; int verbose = 0; // display program help on startup int print_help = 0; +// run only this query, or -1 for all +int filter_id = -1; +#ifdef WARTHOG_POSTHOC +// write trace to file, empty string to disable +std::string trace_file; +using listener_grid = ::warthog::io::grid_trace; +using listener_type = std::tuple; +#else +using listener_type = std::tuple<>; +#endif void help(std::ostream& out) @@ -66,6 +79,10 @@ help(std::ostream& out) "values in the scen file)\n" << "\t--verbose (optional; prints debugging info when compiled " "with debug symbols)\n" + << "\t--filter [id] (optional; run only query [id])\n" +#ifdef WARTHOG_POSTHOC + << "\t--trace [.trace.yaml file] (optional; write posthoc trace for first query to [file])\n" +#endif << "Invoking the program this way solves all instances in [scen " "file] with algorithm [alg]\n" << "Currently recognised values for [alg]:\n" @@ -111,13 +128,28 @@ run_experiments( warthog::util::scenario_manager& scenmgr, bool verbose, bool checkopt, std::ostream& out) { + WARTHOG_GINFO_FMT("start search with algorithm {}", alg_name); warthog::search::search_parameters par; warthog::search::solution sol; auto* expander = algo.get_expander(); if(expander == nullptr) return 1; + +#ifdef WARTHOG_POSTHOC + if constexpr (std::same_as>) { + if (!trace_file.empty()) { + listener_grid& l = std::get(algo.get_listeners()); + l.stream_open(trace_file); + l.search_id(0); + l.set_grid(expander->get_map()); + } + } +#endif + out << "id\talg\texpanded\tgenerated\treopen\tsurplus\theapops" << "\tnanos\tplen\tpcost\tscost\tmap\n"; - for(unsigned int i = 0; i < scenmgr.num_experiments(); i++) + for(uint32_t i = filter_id >= 0 ? static_cast(filter_id) : 0, + ie = filter_id >= 0 ? i+1 : static_cast(scenmgr.num_experiments()); + i < ie; i++) { warthog::util::experiment* exp = scenmgr.get_experiment(i); @@ -141,10 +173,14 @@ run_experiments( if(checkopt) { - if(!check_optimality(sol, exp)) return 4; + if(!check_optimality(sol, exp)) { + WARTHOG_GCRIT("search error: failed suboptimal 4"); + return 4; + } } } + WARTHOG_GINFO_FMT("search complete; total memory: {}", algo.mem() + scenmgr.mem()); return 0; } @@ -158,17 +194,11 @@ run_astar( warthog::heuristic::octile_heuristic heuristic(map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open); + warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } int @@ -182,17 +212,11 @@ run_astar4c( map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open); + warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } int @@ -205,17 +229,11 @@ run_dijkstra( warthog::heuristic::zero_heuristic heuristic; warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open); + warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } int @@ -242,13 +260,7 @@ run_wgm_astar( int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); - if(ret != 0) - { - std::cerr << "run_experiments error code " << ret << std::endl; - return ret; - } - std::cerr << "done. total memory: " << astar.mem() + scenmgr.mem() << "\n"; - return 0; + return ret; } } // namespace @@ -258,13 +270,17 @@ main(int argc, char** argv) { // parse arguments warthog::util::param valid_args[] - = {{"alg", required_argument, 0, 1}, + = {{"alg", required_argument, 0, 0}, {"scen", required_argument, 0, 0}, - {"map", required_argument, 0, 1}, + {"map", required_argument, 0, 0}, // {"gen", required_argument, 0, 3}, {"help", no_argument, &print_help, 1}, {"checkopt", no_argument, &checkopt, 1}, {"verbose", no_argument, &verbose, 1}, + {"filter", required_argument, &filter_id, 1}, +#ifdef WARTHOG_POSTHOC + {"trace", required_argument, 0, 0}, +#endif {"costs", required_argument, 0, 1}, {0, 0, 0, 0}}; @@ -283,6 +299,13 @@ main(int argc, char** argv) std::string mapfile = cfg.get_param_value("map"); std::string costfile = cfg.get_param_value("costs"); + if (filter_id == 1) { + filter_id = std::stoi(cfg.get_param_value("filter")); + } +#ifdef WARTHOG_POSTHOC + trace_file = cfg.get_param_value("trace"); +#endif + // if(gen != "") // { // warthog::util::scenario_manager sm; diff --git a/cmake/config.h.in b/cmake/config.h.in index b2c1f33..85e3736 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -5,6 +5,7 @@ #define WARTHOG_VERSION_MINOR @CMAKE_PROJECT_VERSION_MINOR@ #define WARTHOG_VERSION_REVISON @CMAKE_PROJECT_VERSION_PATCH@ +#cmakedefine WARTHOG_POSTHOC #cmakedefine WARTHOG_INT128 #cmakedefine WARTHOG_INTRIN_ALL #cmakedefine WARTHOG_BMI diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index a95b7a7..7a9f8cb 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -1,7 +1,9 @@ #ifndef WARTHOG_IO_GRID_TRACE_H #define WARTHOG_IO_GRID_TRACE_H -// listener/grid_trace.h +// io/grid_trace.h +// +// Basic posthoc_trace for use with gridmap. // // @author: Ryan Hechenberger // @created: 2025-08-07 @@ -16,7 +18,7 @@ namespace warthog::io { -/// @brief class that produces a posthoc trace for the gridmap domain +/// @brief class that produces a posthoc trace for the gridmap domain, grid must be set. class grid_trace : public posthoc_trace { public: @@ -30,14 +32,6 @@ class grid_trace : public posthoc_trace void print_posthoc_header() override; - /// @brief Checks that grid_ != nullptr - void event(const char* name) const - { - if (grid_ == nullptr) { - throw std::logic_error("grid_trace::grid_ is null"); - } - } - void begin_search(int id, const search::search_problem_instance& pi); void diff --git a/include/warthog/io/observer.h b/include/warthog/io/observer.h index f2e9e57..b14b856 100644 --- a/include/warthog/io/observer.h +++ b/include/warthog/io/observer.h @@ -9,7 +9,7 @@ // These function names must be registered before use, common ones registered here. // // To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). -// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observer. +// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observers. // This will run through each element in tuple (i) and call i.[function](args...) if able. // If i.event([function],args...) is a valid callable, calls this function first, also tries i.event([function]). // @@ -19,28 +19,37 @@ #include +/// @brief Creates a concept observer_has_[func_name] that checks of function Listener.func_name(args...) is callable. #define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ template \ concept observer_has_##func_name = requires(Listener L, Args&&... args) \ { \ { L.func_name(std::forward(args)...) }; \ }; +/// @brief Creates function observer_[func_name] that calls Listener.[func_name] is callable. +/// WARTHOG_OBSERVER_DEFINE_HAS([func_name]) must be defined. +/// +/// The created function observer_[func_name] loops through all listeners in tuple L. +/// For each it will try to call: +/// - event([func_name], args...) if able otherwise event([func_name]) if able +/// - [func_name](args...) if able #define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ template \ void observer_##func_name(Listeners& L, Args&&... args) \ { \ if constexpr (I < std::tuple_size_v) { \ using T = std::tuple_element_t; \ - constexpr bool has_event = observer_has_##func_name ; \ + constexpr bool has_func = observer_has_##func_name ; \ if constexpr (observer_has_event) { std::get(L).event( #func_name , std::forward(args)... ); } \ else if constexpr (observer_has_event) { std::get(L).event( #func_name ); } \ - if constexpr (has_event) { \ + if constexpr (has_func) { \ std::get(L).func_name(std::forward(args)...); \ } \ - listener_##func_name (L, std::forward(args)...); \ + observer_##func_name (L, std::forward(args)...); \ } \ } +/// @brief Defines a observer_[func_name] function and observer_has_[func_name] concept. #define WARTHOG_OBSERVER_DEFINE(func_name) \ WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ WARTHOG_OBSERVER_DEFINE_CALL(func_name) diff --git a/include/warthog/io/posthoc_trace.h b/include/warthog/io/posthoc_trace.h index 04f8627..c1e9c4c 100644 --- a/include/warthog/io/posthoc_trace.h +++ b/include/warthog/io/posthoc_trace.h @@ -17,10 +17,11 @@ namespace warthog::io /// @brief base posthoc observer class. /// +/// Set the search_id to set which search the trace is printed for, by default will not trace. /// event begin_search and end_search will setup the trace to print only a specified. /// Inherit to create new trace format by overriding print_posthoc_header for custom header. /// Add own events to print posthoc event to stream() if (*this) holds true, -/// (*this) holds true iff id is on search_id between begin_search and end_search the first time only. +/// (*this) holds true iff id == search_id for event begin_search once, and ends at end_search. class posthoc_trace : public stream_observer { public: @@ -32,6 +33,9 @@ class posthoc_trace : public stream_observer int search_id() const noexcept { return search_id_; } void search_id(int sid) noexcept { search_id_ = sid; } + /// @brief begin_search event, if overridden then must still be called first (only id argument is needed), + /// then checked if can write to stream. + /// @param id the search id, (*this) will become true if id == search_id_ && done_trace_ == false template void begin_search(int id, Args&&...) { @@ -43,19 +47,21 @@ class posthoc_trace : public stream_observer print_posthoc_header(); } } + /// @brief end_search event, if overridden then must still be called first (no args needed). template void end_search(Args&&...) { do_trace_ = false; } + /// @brief returns true if trace can be outputted, false otherwise. operator bool() const noexcept { return do_trace_; } protected: - int search_id_ = 0; + int search_id_ = -1; bool do_trace_ = false; bool done_trace_ = false; }; diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index 9229b07..afb8216 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -1,15 +1,14 @@ #ifndef WARTHOG_IO_STEAM_OBSERVER_H #define WARTHOG_IO_STEAM_OBSERVER_H -// listener/stream_listener.h +// io/stream_observer.h // -// A search listener is a callback class that executes specialised -// code for partiular search events, such as when: -// - a node is generated -// - a node is expanded -// - a node is relaxed +// The stream observer is a base class for observers that can open and own a filestream, +// or pass another filestream. // -// This class implements dummy listener with empty event handlers. +// Is designed to be used with observer tuples. +// Inherited class will call stream() to get the current stream for output. +// Using the observer methodology, event functions will be given that will write to output in certain ways. // // @author: Ryan Hechenberger // @created: 2025-08-01 @@ -24,6 +23,10 @@ namespace warthog::io { +/// @brief A base-stream observer. +/// +/// Inherit and give event functions for observers to call. +/// shared_stream_t is a shared_ptr to an std::ostream, and can be passed around to other stream_observer. class stream_observer { public: @@ -34,16 +37,24 @@ class stream_observer stream_observer(const shared_stream_t& stream); ~stream_observer(); + /// @brief open a file stream, store in a shared_stream_t void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + /// @brief pass an existing stream, must remain in void stream(std::ostream& stream); + /// @brief pass a shared stream void stream_share(const shared_stream_t& stream); + /// @brief copy stream from another stream_observer void stream_share(const stream_observer& stream); + /// @brief sets to std::cout void stream_stdout(); + /// @brief sets to std::cerr void stream_stderr(); + /// @brief unsets the stream void clear_stream(); operator bool() const noexcept { return stream_ != nullptr; } + /// @brief undefined behaviour if no stream is open (asserts on debug) std::ostream& stream() const noexcept { assert(stream_ != nullptr); return *stream_; } const shared_stream_t& shared_stream() const noexcept { return shared_stream_; } @@ -52,8 +63,6 @@ class stream_observer shared_stream_t shared_stream_; }; -using void_listener = stream_observer; - } // namespace warthog::io #endif // WARTHOG_IO_STEAM_OBSERVER_H diff --git a/src/io/traces.cpp b/src/io/traces.cpp index fb58898..0a78304 100644 --- a/src/io/traces.cpp +++ b/src/io/traces.cpp @@ -28,10 +28,18 @@ void grid_trace::print_posthoc_header() $if: ${{ $.type == 'destination' }} fill: red clear: false + - $: cell + $if: ${{ $.type == 'expand' }} + fill: cyan + clear: false - $: cell $if: ${{ $.type == 'expand' }} fill: blue clear: close + - $: cell + $if: ${{ $.type == 'generate' }} + fill: purple + clear: false - $: cell $if: ${{ $.type == 'generate' }} fill: orange @@ -49,7 +57,9 @@ void grid_trace::begin_search(int id, const search::search_problem_instance& pi) { posthoc_trace::begin_search(id); if (*this) { - assert(grid_ != nullptr); + if (grid_ == nullptr) { + throw std::logic_error("grid_trace::grid_ is null"); + } uint32_t x, y; grid_->to_unpadded_xy(pi.start_, x, y); stream() << std::format(" - {{ type: source, id: {}, x: {}, y: {} }}\n", @@ -67,10 +77,14 @@ grid_trace::expand_node(const node& current) const { if (*this) { assert(grid_ != nullptr); + std::string pid; + if (auto cpid = current.get_parent(); !cpid.is_none()) { + pid = std::format(", pId: {}", cpid.id); + } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, x, y, current.get_f(), current.get_g() + stream() << std::format(" - {{ type: expand, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g() ); } } @@ -80,10 +94,14 @@ grid_trace::relax_node(const node& current) const { if (*this) { assert(grid_ != nullptr); + std::string pid; + if (auto cpid = current.get_parent(); !cpid.is_none()) { + pid = std::format(", pId: {}", cpid.id); + } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: expand, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, x, y, current.get_f(), current.get_g() + stream() << std::format(" - {{ type: relax, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g() ); } } @@ -94,10 +112,14 @@ grid_trace::close_node(const node& current) const { if (*this) { assert(grid_ != nullptr); + std::string pid; + if (auto cpid = current.get_parent(); !cpid.is_none()) { + pid = std::format(", pId: {}", cpid.id); + } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: close, id: {}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, x, y, current.get_f(), current.get_g() + stream() << std::format(" - {{ type: close, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g() ); } } diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..ee3de6e --- /dev/null +++ b/test.cpp @@ -0,0 +1,21 @@ +#include + +enum class X { + a, + b, + c +}; + +template +concept is_c = a == X::c; + +int main() +{ + int a; + std::cin >> a; + if constexpr (is_c(a)>) { + std::cout << "done\n"; + } else { + std::cout << "bad\n"; + } +} From 10bdd85c414876cc821c852b9250c3830e90ba8f Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 15:24:11 +1000 Subject: [PATCH 16/33] added expanded verbose message --- include/warthog/search/unidirectional_search.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 3ab8c8c..cb41aaa 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -293,6 +293,7 @@ class unidirectional_search sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); } io::observer_close_node(listeners_, *current); + WARTHOG_GINFO_FMT_IF(pi->verbose_, "Expanded: {}", *current); } sol->met_.time_elapsed_nano_ = mytimer.elapsed_time_nano(); From 904dfb2c3aaf554724f7f5f1c4dbb016fabc8291 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 3 Oct 2025 15:24:25 +1000 Subject: [PATCH 17/33] missing commit --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a564afe..e24eb04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set_property(GLOBAL PROPERTY WARTHOG_warthog-core ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +option(WARTHOG_POSTHOC "Compile trace support generation for posthoc" OFF) option(WARTHOG_INT128 "Enable support for __int128 on gcc and clang" OFF) option(WARTHOG_BMI "Enable support cpu BMI for WARTHOG_INTRIN_HAS(BMI)" OFF) option(WARTHOG_BMI2 "Enable support cpu BMI2 for WARTHOG_INTRIN_HAS(BMI2), use for Zen 3+" OFF) From 141532f7647aec4275db06b9e933a57da12829ea Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Sun, 5 Oct 2025 14:41:19 +1100 Subject: [PATCH 18/33] removed accidental file from git --- test.cpp | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 test.cpp diff --git a/test.cpp b/test.cpp deleted file mode 100644 index ee3de6e..0000000 --- a/test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -enum class X { - a, - b, - c -}; - -template -concept is_c = a == X::c; - -int main() -{ - int a; - std::cin >> a; - if constexpr (is_c(a)>) { - std::cout << "done\n"; - } else { - std::cout << "bad\n"; - } -} From 885ac3199a006cb403ffdce26d2f1db910912526 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 03:31:33 +0000 Subject: [PATCH 19/33] auto clang-format action --- apps/warthog.cpp | 39 +- include/warthog/io/grid_trace.h | 18 +- include/warthog/io/log.h | 441 +++++++++++------- include/warthog/io/observer.h | 83 ++-- include/warthog/io/posthoc_trace.h | 62 ++- include/warthog/io/stream_observer.h | 58 ++- include/warthog/search/problem_instance.h | 19 +- include/warthog/search/search_node.h | 24 +- include/warthog/search/uds_traits.h | 12 +- .../warthog/search/unidirectional_search.h | 39 +- include/warthog/util/vec_io.h | 6 +- src/io/log.cpp | 129 +++-- src/io/observer.cpp | 53 ++- src/io/traces.cpp | 83 ++-- src/search/problem_instance.cpp | 3 +- src/search/search_metrics.cpp | 3 +- 16 files changed, 668 insertions(+), 404 deletions(-) diff --git a/apps/warthog.cpp b/apps/warthog.cpp index d3a7d40..0db3479 100644 --- a/apps/warthog.cpp +++ b/apps/warthog.cpp @@ -79,9 +79,10 @@ help(std::ostream& out) "values in the scen file)\n" << "\t--verbose (optional; prints debugging info when compiled " "with debug symbols)\n" - << "\t--filter [id] (optional; run only query [id])\n" + << "\t--filter [id] (optional; run only query [id])\n" #ifdef WARTHOG_POSTHOC - << "\t--trace [.trace.yaml file] (optional; write posthoc trace for first query to [file])\n" + << "\t--trace [.trace.yaml file] (optional; write posthoc trace for " + "first query to [file])\n" #endif << "Invoking the program this way solves all instances in [scen " "file] with algorithm [alg]\n" @@ -135,8 +136,12 @@ run_experiments( if(expander == nullptr) return 1; #ifdef WARTHOG_POSTHOC - if constexpr (std::same_as>) { - if (!trace_file.empty()) { + if constexpr(std::same_as< + listener_type, + std::remove_cvref_t>) + { + if(!trace_file.empty()) + { listener_grid& l = std::get(algo.get_listeners()); l.stream_open(trace_file); l.search_id(0); @@ -147,9 +152,11 @@ run_experiments( out << "id\talg\texpanded\tgenerated\treopen\tsurplus\theapops" << "\tnanos\tplen\tpcost\tscost\tmap\n"; - for(uint32_t i = filter_id >= 0 ? static_cast(filter_id) : 0, - ie = filter_id >= 0 ? i+1 : static_cast(scenmgr.num_experiments()); - i < ie; i++) + for(uint32_t i = filter_id >= 0 ? static_cast(filter_id) : 0, + ie = filter_id >= 0 + ? i + 1 + : static_cast(scenmgr.num_experiments()); + i < ie; i++) { warthog::util::experiment* exp = scenmgr.get_experiment(i); @@ -173,14 +180,16 @@ run_experiments( if(checkopt) { - if(!check_optimality(sol, exp)) { + if(!check_optimality(sol, exp)) + { WARTHOG_GCRIT("search error: failed suboptimal 4"); return 4; } } } - WARTHOG_GINFO_FMT("search complete; total memory: {}", algo.mem() + scenmgr.mem()); + WARTHOG_GINFO_FMT( + "search complete; total memory: {}", algo.mem() + scenmgr.mem()); return 0; } @@ -194,7 +203,8 @@ run_astar( warthog::heuristic::octile_heuristic heuristic(map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); + warthog::search::unidirectional_search astar( + &heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); @@ -212,7 +222,8 @@ run_astar4c( map.width(), map.height()); warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); + warthog::search::unidirectional_search astar( + &heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); @@ -229,7 +240,8 @@ run_dijkstra( warthog::heuristic::zero_heuristic heuristic; warthog::util::pqueue_min open; - warthog::search::unidirectional_search astar(&heuristic, &expander, &open, listener_type()); + warthog::search::unidirectional_search astar( + &heuristic, &expander, &open, listener_type()); int ret = run_experiments( astar, alg_name, scenmgr, verbose, checkopt, std::cout); @@ -299,7 +311,8 @@ main(int argc, char** argv) std::string mapfile = cfg.get_param_value("map"); std::string costfile = cfg.get_param_value("costs"); - if (filter_id == 1) { + if(filter_id == 1) + { filter_id = std::stoi(cfg.get_param_value("filter")); } #ifdef WARTHOG_POSTHOC diff --git a/include/warthog/io/grid_trace.h b/include/warthog/io/grid_trace.h index 7a9f8cb..500a375 100644 --- a/include/warthog/io/grid_trace.h +++ b/include/warthog/io/grid_trace.h @@ -10,29 +10,33 @@ // #include "posthoc_trace.h" -#include -#include -#include #include +#include +#include +#include namespace warthog::io { -/// @brief class that produces a posthoc trace for the gridmap domain, grid must be set. +/// @brief class that produces a posthoc trace for the gridmap domain, grid +/// must be set. class grid_trace : public posthoc_trace { public: using node = search::search_node; using posthoc_trace::posthoc_trace; - void set_grid(domain::gridmap* grid) noexcept + void + set_grid(domain::gridmap* grid) noexcept { grid_ = grid; } - void print_posthoc_header() override; + void + print_posthoc_header() override; - void begin_search(int id, const search::search_problem_instance& pi); + void + begin_search(int id, const search::search_problem_instance& pi); void expand_node(const node& current) const; diff --git a/include/warthog/io/log.h b/include/warthog/io/log.h index 7569d38..66320bc 100644 --- a/include/warthog/io/log.h +++ b/include/warthog/io/log.h @@ -5,49 +5,51 @@ // // Logging utility. // -// The log_sink is a non-owning copyable struct that points to the data and logging function calls. -// All logging is performed through log_sink. -// All classes here are thread safe, follow comments for outlaying cases. +// The log_sink is a non-owning copyable struct that points to the data and +// logging function calls. All logging is performed through log_sink. All +// classes here are thread safe, follow comments for outlaying cases. // // Special classes inherit log_sink to provide default functionality. // log_sink_std should be used to write to std::cout and std::cerr. // log_sink_stream pipe to a stream or open a file stream. // // The logs are made to be logged to a certain log_level. -// The WARTHOG_TRACE and others in this header provide interfaces to log to a special -// logger<> class. This logger<> class knows at compile time the minimum level to log, -// and with the use of a macro will be compiled out if that level was not set at compile time -// when used with the logging macros. +// The WARTHOG_TRACE and others in this header provide interfaces to log to a +// special logger<> class. This logger<> class knows at compile time the +// minimum level to log, and with the use of a macro will be compiled out if +// that level was not set at compile time when used with the logging macros. // -// The global logger can be acquired with the glog() (logger<>) or glogs() (log_sink), and set with -// set_glog(log_sink). The log level of the global logger is set though the WARTHOG_LOG definition, -// with a default of 1(debug) for debug builds, and 2(information) for release (NDEBUG defined). -// Macros like WARTHOG_GTRACE will automatically write to the global logger. +// The global logger can be acquired with the glog() (logger<>) or glogs() +// (log_sink), and set with set_glog(log_sink). The log level of the global +// logger is set though the WARTHOG_LOG definition, with a default of 1(debug) +// for debug builds, and 2(information) for release (NDEBUG defined). Macros +// like WARTHOG_GTRACE will automatically write to the global logger. // // The _IF will only log if a runtime if true. // The _FMT uses the std::format from C++20 to format the output messages. -// The logging utilities uses dynamic memory std::strings to produce the final log message strings, -// thus is better to be disabled for release builds through the log level. +// The logging utilities uses dynamic memory std::strings to produce the final +// log message strings, thus is better to be disabled for release builds +// through the log level. // // Log messages produce a messaged as "[TIME LEVEL] msg". -// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be overridden with -// define cstring WARTHOG_LOG_TIME. -// Clock is in local system time, but can be set to UTC by definiong WARTHOG_LOG_CLOCK_UTC. +// The time is formatted in ISO with space (yyyy-mm-dd hh:mm:ss), but can be +// overridden with define cstring WARTHOG_LOG_TIME. Clock is in local system +// time, but can be set to UTC by definiong WARTHOG_LOG_CLOCK_UTC. // // @author: Ryan Hechenberger // @created: 2025-09-09 // -#include -#include -#include -#include #include #include +#include +#include +#include #include #include +#include #include -#include +#include // default log levels, also for global logger #ifdef WARTHOG_LOG @@ -69,7 +71,8 @@ #ifdef WARTHOG_LOG_CLOCK_UTC #define WARTHOG_LOG_NOW (std::chrono::utc_clock::now()) #else -#define WARTHOG_LOG_NOW (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) +#define WARTHOG_LOG_NOW \ + (std::chrono::current_zone()->to_local(std::chrono::system_clock::now())) #endif namespace warthog::io @@ -77,13 +80,13 @@ namespace warthog::io enum class log_level { - TRACE = 0, - DEBUG = 1, + TRACE = 0, + DEBUG = 1, INFORMATION = 2, - WARNING = 3, - ERROR = 4, - CRITICAL = 5, - NONE = 6 + WARNING = 3, + ERROR = 4, + CRITICAL = 5, + NONE = 6 }; /// @brief the log class used to write out to a log. @@ -93,29 +96,32 @@ enum class log_level /// log will write if able (data != null && call[level] != null). struct log_sink { - using call_type = void (void*, std::string_view msg); - void* data = nullptr; + using call_type = void(void*, std::string_view msg); + void* data = nullptr; std::array(log_level::NONE)> call = {}; - constexpr void log(log_level level, std::string_view msg) const + constexpr void + log(log_level level, std::string_view msg) const { - if (can_log(level)) - { - (*call[static_cast(level)])(data, msg); - } + if(can_log(level)) { (*call[static_cast(level)])(data, msg); } } - constexpr bool can_log(log_level level) const noexcept + constexpr bool + can_log(log_level level) const noexcept { - return static_cast(level) < call.size() && data != nullptr && call[static_cast(level)] != nullptr; + return static_cast(level) < call.size() && data != nullptr + && call[static_cast(level)] != nullptr; } - constexpr const log_sink& sink() const noexcept { return *this; } + constexpr const log_sink& + sink() const noexcept + { + return *this; + } }; /// @brief concept that check if class is shaped with a log_sink. -template -concept LogSink = requires (Sink S, log_level level, std::string_view msg) -{ +template +concept LogSink = requires(Sink S, log_level level, std::string_view msg) { S.log(level, msg); { S.can_log(level) } -> std::same_as; { std::as_const(S).sink() } -> std::convertible_to; @@ -123,19 +129,25 @@ concept LogSink = requires (Sink S, log_level level, std::string_view msg) /// @brief sink to cout|cerr /// -/// Is thread safe as long as std::ios_base::sync_with_stdio(false) has not been called. -/// Can copy go log_sink and then be destructed without issue. +/// Is thread safe as long as std::ios_base::sync_with_stdio(false) has not +/// been called. Can copy go log_sink and then be destructed without issue. struct log_sink_std : log_sink { // cerr = true will sink to cerr, false to cout log_sink_std(); log_sink_std(bool cerr = true); - static void write_trace(void*, std::string_view msg); - static void write_debug(void*, std::string_view msg); - static void write_information(void*, std::string_view msg); - static void write_warning(void*, std::string_view msg); - static void write_error(void*, std::string_view msg); - static void write_critical(void*, std::string_view msg); + static void + write_trace(void*, std::string_view msg); + static void + write_debug(void*, std::string_view msg); + static void + write_information(void*, std::string_view msg); + static void + write_warning(void*, std::string_view msg); + static void + write_error(void*, std::string_view msg); + static void + write_critical(void*, std::string_view msg); }; /// @brief sink to a passed stream, or open and own a filestream. @@ -146,20 +158,35 @@ class log_sink_stream : public log_sink { public: log_sink_stream(); - log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); + log_sink_stream( + const std::filesystem::path& filename, + std::ios_base::openmode mode + = std::ios_base::out | std::ios_base::app); log_sink_stream(std::ostream* stream); ~log_sink_stream() = default; - void open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out | std::ios_base::app); - void open(std::ostream& stream); - void open_stderr(); - - static void write_trace(void*, std::string_view msg); - static void write_debug(void*, std::string_view msg); - static void write_information(void*, std::string_view msg); - static void write_warning(void*, std::string_view msg); - static void write_error(void*, std::string_view msg); - static void write_critical(void*, std::string_view msg); + void + open( + const std::filesystem::path& filename, + std::ios_base::openmode mode + = std::ios_base::out | std::ios_base::app); + void + open(std::ostream& stream); + void + open_stderr(); + + static void + write_trace(void*, std::string_view msg); + static void + write_debug(void*, std::string_view msg); + static void + write_information(void*, std::string_view msg); + static void + write_warning(void*, std::string_view msg); + static void + write_error(void*, std::string_view msg); + static void + write_critical(void*, std::string_view msg); protected: std::ostream* log_stream_ = nullptr; @@ -174,169 +201,249 @@ static_assert(LogSink, "log_stream_sink must be a log_sink."); /// /// A wrapper class for a log_sink that allows to control the log_level. /// If just using a log_sink, it will always log at every level. -/// This class is handled specially by the logging macros to only log if log_level -/// is a min_level. -/// When setting a long_sink, will clear all pointers below MinLevel. -template (WARTHOG_DEFAULT_LOG_LEVEL)> +/// This class is handled specially by the logging macros to only log if +/// log_level is a min_level. When setting a long_sink, will clear all pointers +/// below MinLevel. +template< + log_level MinLevel = static_cast(WARTHOG_DEFAULT_LOG_LEVEL)> struct logger : log_sink { constexpr logger() noexcept = default; constexpr logger(log_sink sink) : log_sink(sink) { - for (auto& c : call | std::views::take(static_cast(MinLevel))) { + for(auto& c : call | std::views::take(static_cast(MinLevel))) + { c = nullptr; } } - - constexpr logger& operator=(const logger&) noexcept = default; - constexpr logger& operator=(log_sink sink) noexcept + + constexpr logger& + operator=(const logger&) noexcept + = default; + constexpr logger& + operator=(log_sink sink) noexcept { - for (auto& c : call | std::views::take(static_cast(MinLevel))) { + for(auto& c : call | std::views::take(static_cast(MinLevel))) + { c = nullptr; } return *this; } /// @brief Overrides MinLevel to level. Can only raise, not lower it. - /// @param level - constexpr void set_min_level(log_level level) noexcept + /// @param level + constexpr void + set_min_level(log_level level) noexcept { - assert(static_cast(level) <= static_cast(log_level::NONE)); - for (auto& c : call | std::views::take(static_cast(level))) { + assert( + static_cast(level) + <= static_cast(log_level::NONE)); + for(auto& c : call | std::views::take(static_cast(level))) + { c = nullptr; } } constexpr static log_level min_level = MinLevel; - template - constexpr static bool supports_level = MinLevel != log_level::NONE && static_cast(L) >= static_cast(MinLevel); + template + constexpr static bool supports_level = MinLevel != log_level::NONE + && static_cast(L) >= static_cast(MinLevel); }; -namespace details { -template +namespace details +{ +template struct is_logger : std::false_type { }; -template +template struct is_logger> : std::true_type { }; }; -template +template concept Logger = details::is_logger::value; -template -concept LoggerLevel = Logger && requires { - requires Log::template supports_level; -}; - -#define WARTHOG_LOG(lg,level,msg) lg.log(msg) - -#define WARTHOG_LOG_LEVEL_(cond,lg,level,msg) \ -{if constexpr (::warthog::io::Logger) { \ - if constexpr (::warthog::io::LoggerLevel) { \ - if (cond) \ - lg.log(level, msg); \ - } \ -} else { \ - if (cond) \ - lg.log(level, msg); \ -}} +template +concept LoggerLevel + = Logger && requires { requires Log::template supports_level; }; + +#define WARTHOG_LOG(lg, level, msg) lg.log(msg) + +#define WARTHOG_LOG_LEVEL_(cond, lg, level, msg) \ + { \ + if constexpr(::warthog::io::Logger) \ + { \ + if constexpr(::warthog::io::LoggerLevel) \ + { \ + if(cond) lg.log(level, msg); \ + } \ + } \ + else \ + { \ + if(cond) lg.log(level, msg); \ + } \ + } // Write msg (string_view) to log lg. -#define WARTHOG_TRACE(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::TRACE,msg) -#define WARTHOG_DEBUG(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::DEBUG,msg) -#define WARTHOG_INFO(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::INFORMATION,msg) -#define WARTHOG_WARN(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::WARNING,msg) -#define WARTHOG_ERROR(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::ERROR,msg) -#define WARTHOG_CRIT(lg,msg) WARTHOG_LOG_LEVEL_(true,lg,::warthog::io::log_level::CRITICAL,msg) +#define WARTHOG_TRACE(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::TRACE, msg) +#define WARTHOG_DEBUG(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::DEBUG, msg) +#define WARTHOG_INFO(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::INFORMATION, msg) +#define WARTHOG_WARN(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::WARNING, msg) +#define WARTHOG_ERROR(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::ERROR, msg) +#define WARTHOG_CRIT(lg, msg) \ + WARTHOG_LOG_LEVEL_(true, lg, ::warthog::io::log_level::CRITICAL, msg) // Write msg (string_view) to log lg if runtime cond is true. -#define WARTHOG_TRACE_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::TRACE,msg) -#define WARTHOG_DEBUG_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::DEBUG,msg) -#define WARTHOG_INFO_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::INFORMATION,msg) -#define WARTHOG_WARN_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::WARNING,msg) -#define WARTHOG_ERROR_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::ERROR,msg) -#define WARTHOG_CRIT_IF(cond,lg,msg) WARTHOG_LOG_LEVEL_(cond,lg,::warthog::io::log_level::CRITICAL,msg) - -#define WARTHOG_LOG_LEVEL_FMT_(cond,lg,level,...) \ -{if constexpr (::warthog::io::Logger) { \ - if constexpr (::warthog::io::LoggerLevel) { \ - if (cond) \ - lg.log(level, std::format(__VA_ARGS__)); \ - } \ -} else { \ - if (cond) \ - lg.log(level, std::format(__VA_ARGS__)); \ -}} +#define WARTHOG_TRACE_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::TRACE, msg) +#define WARTHOG_DEBUG_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::DEBUG, msg) +#define WARTHOG_INFO_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::INFORMATION, msg) +#define WARTHOG_WARN_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::WARNING, msg) +#define WARTHOG_ERROR_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::ERROR, msg) +#define WARTHOG_CRIT_IF(cond, lg, msg) \ + WARTHOG_LOG_LEVEL_(cond, lg, ::warthog::io::log_level::CRITICAL, msg) + +#define WARTHOG_LOG_LEVEL_FMT_(cond, lg, level, ...) \ + { \ + if constexpr(::warthog::io::Logger) \ + { \ + if constexpr(::warthog::io::LoggerLevel) \ + { \ + if(cond) lg.log(level, std::format(__VA_ARGS__)); \ + } \ + } \ + else \ + { \ + if(cond) lg.log(level, std::format(__VA_ARGS__)); \ + } \ + } // Write formatted message to log, pass as format,args... -#define WARTHOG_TRACE_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) -#define WARTHOG_DEBUG_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) -#define WARTHOG_INFO_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) -#define WARTHOG_WARN_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) -#define WARTHOG_ERROR_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) -#define WARTHOG_CRIT_FMT(lg,...) WARTHOG_LOG_LEVEL_FMT_(true,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +#define WARTHOG_TRACE_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::TRACE, __VA_ARGS__) +#define WARTHOG_DEBUG_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::DEBUG, __VA_ARGS__) +#define WARTHOG_INFO_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::INFORMATION, __VA_ARGS__) +#define WARTHOG_WARN_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::WARNING, __VA_ARGS__) +#define WARTHOG_ERROR_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::ERROR, __VA_ARGS__) +#define WARTHOG_CRIT_FMT(lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + true, lg, ::warthog::io::log_level::CRITICAL, __VA_ARGS__) // Write formatted message to log if cond is true, pass as format,args... -#define WARTHOG_TRACE_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::TRACE,__VA_ARGS__) -#define WARTHOG_DEBUG_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::DEBUG,__VA_ARGS__) -#define WARTHOG_INFO_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::INFORMATION,__VA_ARGS__) -#define WARTHOG_WARN_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::WARNING,__VA_ARGS__) -#define WARTHOG_ERROR_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::ERROR,__VA_ARGS__) -#define WARTHOG_CRIT_FMT_IF(cond,lg,...) WARTHOG_LOG_LEVEL_FMT_(cond,lg,::warthog::io::log_level::CRITICAL,__VA_ARGS__) +#define WARTHOG_TRACE_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::TRACE, __VA_ARGS__) +#define WARTHOG_DEBUG_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::DEBUG, __VA_ARGS__) +#define WARTHOG_INFO_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::INFORMATION, __VA_ARGS__) +#define WARTHOG_WARN_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::WARNING, __VA_ARGS__) +#define WARTHOG_ERROR_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::ERROR, __VA_ARGS__) +#define WARTHOG_CRIT_FMT_IF(cond, lg, ...) \ + WARTHOG_LOG_LEVEL_FMT_( \ + cond, lg, ::warthog::io::log_level::CRITICAL, __VA_ARGS__) // global logger using global_logger_type = logger<>; -/// @brief get the global logger, default is set to std::cerr through log_sink_std -global_logger_type& glog(); +/// @brief get the global logger, default is set to std::cerr through +/// log_sink_std +global_logger_type& +glog(); /// @brief get the global log_sink -const log_sink& glogs(); +const log_sink& +glogs(); /// @brief sets the global log_sink -void set_glog(log_sink log); +void +set_glog(log_sink log); /// @brief A logger that sets itself to glog() -/// @tparam MinLevel -template +/// @tparam MinLevel +template struct global_logger : logger { using logger = typename global_logger::logger; - constexpr global_logger() : logger(glog().sink()) - { } - - constexpr global_logger& operator=(const global_logger&) noexcept = default; + constexpr global_logger() : logger(glog().sink()) { } + + constexpr global_logger& + operator=(const global_logger&) noexcept + = default; - /// @brief Updates the sink to the new glog sink. Must be called if set_glog was used. - void resync_global() + /// @brief Updates the sink to the new glog sink. Must be called if + /// set_glog was used. + void + resync_global() { static_cast(*this) = glog().sink(); } }; // the global version of the marcos, omits the passing of a logger. -#define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(),msg) -#define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(),msg) -#define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(),msg) -#define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(),msg) -#define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(),msg) -#define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(),msg) -#define WARTHOG_GTRACE_IF(cond,msg) WARTHOG_TRACE_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GDEBUG_IF(cond,msg) WARTHOG_DEBUG_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GINFO_IF(cond,msg) WARTHOG_INFO_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GWARN_IF(cond,msg) WARTHOG_WARN_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GERROR_IF(cond,msg) WARTHOG_ERROR_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GCRIT_IF(cond,msg) WARTHOG_CRIT_IF(cond,::warthog::io::glog(),msg) -#define WARTHOG_GTRACE_FMT(...) WARTHOG_TRACE_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GDEBUG_FMT(...) WARTHOG_DEBUG_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GINFO_FMT(...) WARTHOG_INFO_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GWARN_FMT(...) WARTHOG_WARN_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GERROR_FMT(...) WARTHOG_ERROR_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GCRIT_FMT(...) WARTHOG_CRIT_FMT(::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GTRACE_FMT_IF(cond,...) WARTHOG_TRACE_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GDEBUG_FMT_IF(cond,...) WARTHOG_DEBUG_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GINFO_FMT_IF(cond,...) WARTHOG_INFO_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GWARN_FMT_IF(cond,...) WARTHOG_WARN_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GERROR_FMT_IF(cond,...) WARTHOG_ERROR_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) -#define WARTHOG_GCRIT_FMT_IF(cond,...) WARTHOG_CRIT_FMT_IF(cond,::warthog::io::glog(),__VA_ARGS__) +#define WARTHOG_GTRACE(msg) WARTHOG_TRACE(::warthog::io::glog(), msg) +#define WARTHOG_GDEBUG(msg) WARTHOG_DEBUG(::warthog::io::glog(), msg) +#define WARTHOG_GINFO(msg) WARTHOG_INFO(::warthog::io::glog(), msg) +#define WARTHOG_GWARN(msg) WARTHOG_WARN(::warthog::io::glog(), msg) +#define WARTHOG_GERROR(msg) WARTHOG_ERROR(::warthog::io::glog(), msg) +#define WARTHOG_GCRIT(msg) WARTHOG_CRIT(::warthog::io::glog(), msg) +#define WARTHOG_GTRACE_IF(cond, msg) \ + WARTHOG_TRACE_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GDEBUG_IF(cond, msg) \ + WARTHOG_DEBUG_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GINFO_IF(cond, msg) \ + WARTHOG_INFO_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GWARN_IF(cond, msg) \ + WARTHOG_WARN_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GERROR_IF(cond, msg) \ + WARTHOG_ERROR_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GCRIT_IF(cond, msg) \ + WARTHOG_CRIT_IF(cond, ::warthog::io::glog(), msg) +#define WARTHOG_GTRACE_FMT(...) \ + WARTHOG_TRACE_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GDEBUG_FMT(...) \ + WARTHOG_DEBUG_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GINFO_FMT(...) \ + WARTHOG_INFO_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GWARN_FMT(...) \ + WARTHOG_WARN_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GERROR_FMT(...) \ + WARTHOG_ERROR_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GCRIT_FMT(...) \ + WARTHOG_CRIT_FMT(::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GTRACE_FMT_IF(cond, ...) \ + WARTHOG_TRACE_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GDEBUG_FMT_IF(cond, ...) \ + WARTHOG_DEBUG_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GINFO_FMT_IF(cond, ...) \ + WARTHOG_INFO_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GWARN_FMT_IF(cond, ...) \ + WARTHOG_WARN_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GERROR_FMT_IF(cond, ...) \ + WARTHOG_ERROR_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) +#define WARTHOG_GCRIT_FMT_IF(cond, ...) \ + WARTHOG_CRIT_FMT_IF(cond, ::warthog::io::glog(), __VA_ARGS__) } // namespace warthog::io diff --git a/include/warthog/io/observer.h b/include/warthog/io/observer.h index b14b856..b8e3402 100644 --- a/include/warthog/io/observer.h +++ b/include/warthog/io/observer.h @@ -3,15 +3,19 @@ // io/observer.h // -// The observer pattern defines methods to tightly-bind user provided observers to certain event patterns. -// Provide a tuple of observers, where some event is triggered will notifiy all observers with function of event name that is callable. +// The observer pattern defines methods to tightly-bind user provided observers +// to certain event patterns. Provide a tuple of observers, where some event is +// triggered will notifiy all observers with function of event name that is +// callable. // -// These function names must be registered before use, common ones registered here. +// These function names must be registered before use, common ones registered +// here. // // To register a new function name, use WARTHOG_OBSERVER_DEFINE([function]). -// Invoke event with observer_[function](listeners, args...) where listeners are tuple of observers. -// This will run through each element in tuple (i) and call i.[function](args...) if able. -// If i.event([function],args...) is a valid callable, calls this function first, also tries i.event([function]). +// Invoke event with observer_[function](listeners, args...) where listeners +// are tuple of observers. This will run through each element in tuple (i) and +// call i.[function](args...) if able. If i.event([function],args...) is a +// valid callable, calls this function first, also tries i.event([function]). // // @author: Ryan Hechenberger // @created: 2025-08-06 @@ -19,39 +23,50 @@ #include -/// @brief Creates a concept observer_has_[func_name] that checks of function Listener.func_name(args...) is callable. -#define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ -template \ -concept observer_has_##func_name = requires(Listener L, Args&&... args) \ -{ \ - { L.func_name(std::forward(args)...) }; \ -}; -/// @brief Creates function observer_[func_name] that calls Listener.[func_name] is callable. +/// @brief Creates a concept observer_has_[func_name] that checks of function +/// Listener.func_name(args...) is callable. +#define WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ + template \ + concept observer_has_##func_name = requires(Listener L, Args&&... args) { \ + { L.func_name(std::forward(args)...) }; \ + }; +/// @brief Creates function observer_[func_name] that calls +/// Listener.[func_name] is callable. /// WARTHOG_OBSERVER_DEFINE_HAS([func_name]) must be defined. /// -/// The created function observer_[func_name] loops through all listeners in tuple L. -/// For each it will try to call: +/// The created function observer_[func_name] loops through all listeners in +/// tuple L. For each it will try to call: /// - event([func_name], args...) if able otherwise event([func_name]) if able /// - [func_name](args...) if able -#define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ -template \ -void observer_##func_name(Listeners& L, Args&&... args) \ -{ \ - if constexpr (I < std::tuple_size_v) { \ - using T = std::tuple_element_t; \ - constexpr bool has_func = observer_has_##func_name ; \ - if constexpr (observer_has_event) { std::get(L).event( #func_name , std::forward(args)... ); } \ - else if constexpr (observer_has_event) { std::get(L).event( #func_name ); } \ - if constexpr (has_func) { \ - std::get(L).func_name(std::forward(args)...); \ - } \ - observer_##func_name (L, std::forward(args)...); \ - } \ -} +#define WARTHOG_OBSERVER_DEFINE_CALL(func_name) \ + template \ + void observer_##func_name(Listeners& L, Args&&... args) \ + { \ + if constexpr(I < std::tuple_size_v) \ + { \ + using T = std::tuple_element_t; \ + constexpr bool has_func = observer_has_##func_name; \ + if constexpr(observer_has_event) \ + { \ + std::get(L).event( \ + #func_name, std::forward(args)...); \ + } \ + else if constexpr(observer_has_event) \ + { \ + std::get(L).event(#func_name); \ + } \ + if constexpr(has_func) \ + { \ + std::get(L).func_name(std::forward(args)...); \ + } \ + observer_##func_name(L, std::forward(args)...); \ + } \ + } -/// @brief Defines a observer_[func_name] function and observer_has_[func_name] concept. -#define WARTHOG_OBSERVER_DEFINE(func_name) \ - WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ +/// @brief Defines a observer_[func_name] function and observer_has_[func_name] +/// concept. +#define WARTHOG_OBSERVER_DEFINE(func_name) \ + WARTHOG_OBSERVER_DEFINE_HAS(func_name) \ WARTHOG_OBSERVER_DEFINE_CALL(func_name) namespace warthog::io diff --git a/include/warthog/io/posthoc_trace.h b/include/warthog/io/posthoc_trace.h index c1e9c4c..a36a1c3 100644 --- a/include/warthog/io/posthoc_trace.h +++ b/include/warthog/io/posthoc_trace.h @@ -17,52 +17,66 @@ namespace warthog::io /// @brief base posthoc observer class. /// -/// Set the search_id to set which search the trace is printed for, by default will not trace. -/// event begin_search and end_search will setup the trace to print only a specified. -/// Inherit to create new trace format by overriding print_posthoc_header for custom header. -/// Add own events to print posthoc event to stream() if (*this) holds true, -/// (*this) holds true iff id == search_id for event begin_search once, and ends at end_search. +/// Set the search_id to set which search the trace is printed for, by default +/// will not trace. event begin_search and end_search will setup the trace to +/// print only a specified. Inherit to create new trace format by overriding +/// print_posthoc_header for custom header. Add own events to print posthoc +/// event to stream() if (*this) holds true, +/// (*this) holds true iff id == search_id for event begin_search once, and +/// ends at end_search. class posthoc_trace : public stream_observer { public: using stream_observer::stream_observer; // override print the header if not already printed - virtual void print_posthoc_header(); + virtual void + print_posthoc_header(); - int search_id() const noexcept { return search_id_; } - void search_id(int sid) noexcept { search_id_ = sid; } + int + search_id() const noexcept + { + return search_id_; + } + void + search_id(int sid) noexcept + { + search_id_ = sid; + } - /// @brief begin_search event, if overridden then must still be called first (only id argument is needed), + /// @brief begin_search event, if overridden then must still be called + /// first (only id argument is needed), /// then checked if can write to stream. - /// @param id the search id, (*this) will become true if id == search_id_ && done_trace_ == false - template - void begin_search(int id, Args&&...) + /// @param id the search id, (*this) will become true if id == search_id_ + /// && done_trace_ == false + template + void + begin_search(int id, Args&&...) { do_trace_ = false; - if (done_trace_) return; // do not repeat a trace - if (stream_observer::operator bool() && id == search_id_) { - do_trace_ = true; + if(done_trace_) return; // do not repeat a trace + if(stream_observer::operator bool() && id == search_id_) + { + do_trace_ = true; done_trace_ = true; print_posthoc_header(); } } - /// @brief end_search event, if overridden then must still be called first (no args needed). - template - void end_search(Args&&...) + /// @brief end_search event, if overridden then must still be called first + /// (no args needed). + template + void + end_search(Args&&...) { do_trace_ = false; } /// @brief returns true if trace can be outputted, false otherwise. - operator bool() const noexcept - { - return do_trace_; - } + operator bool() const noexcept { return do_trace_; } protected: - int search_id_ = -1; - bool do_trace_ = false; + int search_id_ = -1; + bool do_trace_ = false; bool done_trace_ = false; }; diff --git a/include/warthog/io/stream_observer.h b/include/warthog/io/stream_observer.h index afb8216..10471bc 100644 --- a/include/warthog/io/stream_observer.h +++ b/include/warthog/io/stream_observer.h @@ -3,22 +3,23 @@ // io/stream_observer.h // -// The stream observer is a base class for observers that can open and own a filestream, -// or pass another filestream. +// The stream observer is a base class for observers that can open and own a +// filestream, or pass another filestream. // // Is designed to be used with observer tuples. // Inherited class will call stream() to get the current stream for output. -// Using the observer methodology, event functions will be given that will write to output in certain ways. +// Using the observer methodology, event functions will be given that will +// write to output in certain ways. // // @author: Ryan Hechenberger // @created: 2025-08-01 // -#include -#include -#include #include +#include #include +#include +#include namespace warthog::io { @@ -26,37 +27,58 @@ namespace warthog::io /// @brief A base-stream observer. /// /// Inherit and give event functions for observers to call. -/// shared_stream_t is a shared_ptr to an std::ostream, and can be passed around to other stream_observer. +/// shared_stream_t is a shared_ptr to an std::ostream, and can be passed +/// around to other stream_observer. class stream_observer { public: using shared_stream_t = std::shared_ptr; - stream_observer() = default; - stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + stream_observer() = default; + stream_observer( + const std::filesystem::path& filename, + std::ios_base::openmode mode = std::ios_base::out); stream_observer(std::ostream& stream); stream_observer(const shared_stream_t& stream); ~stream_observer(); /// @brief open a file stream, store in a shared_stream_t - void stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode = std::ios_base::out); + void + stream_open( + const std::filesystem::path& filename, + std::ios_base::openmode mode = std::ios_base::out); /// @brief pass an existing stream, must remain in - void stream(std::ostream& stream); + void + stream(std::ostream& stream); /// @brief pass a shared stream - void stream_share(const shared_stream_t& stream); + void + stream_share(const shared_stream_t& stream); /// @brief copy stream from another stream_observer - void stream_share(const stream_observer& stream); + void + stream_share(const stream_observer& stream); /// @brief sets to std::cout - void stream_stdout(); + void + stream_stdout(); /// @brief sets to std::cerr - void stream_stderr(); + void + stream_stderr(); /// @brief unsets the stream - void clear_stream(); + void + clear_stream(); operator bool() const noexcept { return stream_ != nullptr; } /// @brief undefined behaviour if no stream is open (asserts on debug) - std::ostream& stream() const noexcept { assert(stream_ != nullptr); return *stream_; } - const shared_stream_t& shared_stream() const noexcept { return shared_stream_; } + std::ostream& + stream() const noexcept + { + assert(stream_ != nullptr); + return *stream_; + } + const shared_stream_t& + shared_stream() const noexcept + { + return shared_stream_; + } private: std::ostream* stream_ = nullptr; diff --git a/include/warthog/search/problem_instance.h b/include/warthog/search/problem_instance.h index eb16556..9f66875 100644 --- a/include/warthog/search/problem_instance.h +++ b/include/warthog/search/problem_instance.h @@ -88,25 +88,30 @@ convert_problem_instance_to_search(const problem_instance& pi, Domain& d) std::ostream& operator<<(std::ostream& str, const problem_instance& pi); std::ostream& -operator<<( - std::ostream& str, const search_problem_instance& pi); +operator<<(std::ostream& str, const search_problem_instance& pi); } // namespace warthog::search template<::warthog::Identity STATE> struct std::formatter<::warthog::search::problem_instance_base, char> { - template - constexpr auto parse(ParseContext& ctx) const + template + constexpr auto + parse(ParseContext& ctx) const { return ctx.begin(); } template - FmtContext::iterator format(const ::warthog::search::problem_instance_base& pi, FmtContext& ctx) const + FmtContext::iterator + format( + const ::warthog::search::problem_instance_base& pi, + FmtContext& ctx) const { - return std::format_to(ctx.out(), "problem instance[{}]; start:{} target:{} search_id:{}", - typeid(typename STATE::tag).name(), pi.start_.id, pi.target_.id, pi.instance_id_); + return std::format_to( + ctx.out(), "problem instance[{}]; start:{} target:{} search_id:{}", + typeid(typename STATE::tag).name(), pi.start_.id, pi.target_.id, + pi.instance_id_); } }; diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 7ac705f..90b9eba 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -8,8 +8,8 @@ // #include -#include #include +#include #include @@ -274,22 +274,26 @@ struct cmp_less_search_node_f_only std::ostream& operator<<(std::ostream& str, const warthog::search::search_node& sn); -template <> +template<> struct std::formatter<::warthog::search::search_node, char> { - template - constexpr auto parse(ParseContext& ctx) const + template + constexpr auto + parse(ParseContext& ctx) const { return ctx.begin(); } template - FmtContext::iterator format(const ::warthog::search::search_node& s, FmtContext& ctx) const - { - return std::format_to(ctx.out(), "search_node id:{} p_id:{} g:{} f:{} ub:{} expanded:{} search_number:{}", - s.get_id().id, s.get_parent().id, - s.get_g(), s.get_f(), s.get_ub(), - s.get_expanded(), s.get_search_number()); + FmtContext::iterator + format(const ::warthog::search::search_node& s, FmtContext& ctx) const + { + return std::format_to( + ctx.out(), + "search_node id:{} p_id:{} g:{} f:{} ub:{} expanded:{} " + "search_number:{}", + s.get_id().id, s.get_parent().id, s.get_g(), s.get_f(), s.get_ub(), + s.get_expanded(), s.get_search_number()); } }; diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index b22378e..bfaac52 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -99,19 +99,25 @@ feasible( if(next->get_f() > par->get_max_cost_cutoff()) { - WARTHOG_GINFO_FMT_IF(par->verbose_, "cost cutoff {} > {}", next->get_f(), par->get_max_cost_cutoff()); + WARTHOG_GINFO_FMT_IF( + par->verbose_, "cost cutoff {} > {}", next->get_f(), + par->get_max_cost_cutoff()); return false; } if(met->nodes_expanded_ >= par->get_max_expansions_cutoff()) { - WARTHOG_GINFO_FMT_IF(par->verbose_, "expansions cutoff {} > {}", met->nodes_expanded_, par->get_max_expansions_cutoff()); + WARTHOG_GINFO_FMT_IF( + par->verbose_, "expansions cutoff {} > {}", met->nodes_expanded_, + par->get_max_expansions_cutoff()); return false; } if(met->time_elapsed_nano_ > par->get_max_time_cutoff()) { - WARTHOG_GINFO_FMT_IF(par->verbose_, "time cutoff {} > {}", met->time_elapsed_nano_, par->get_max_time_cutoff()); + WARTHOG_GINFO_FMT_IF( + par->verbose_, "time cutoff {} > {}", met->time_elapsed_nano_, + par->get_max_time_cutoff()); return false; } diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index cb41aaa..6b75ced 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -15,9 +15,9 @@ #include "search_parameters.h" #include "solution.h" #include "uds_traits.h" -#include #include #include +#include #include #include #include @@ -40,7 +40,8 @@ namespace warthog::search // used determine if a search should continue or terminate. // (default: search for any solution, until OPEN is exhausted) template< - typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>, + typename H, typename E, typename Q = util::pqueue_min, + typename L = std::tuple<>, admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> @@ -49,7 +50,8 @@ class unidirectional_search public: unidirectional_search( H* heuristic, E* expander, Q* queue, L listeners = L{}) - : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) + : heuristic_(heuristic), expander_(expander), open_(queue), + listeners_(listeners) { } ~unidirectional_search() { } @@ -182,7 +184,9 @@ class unidirectional_search if(n->get_ub() < sol->met_.ub_) { sol->met_.ub_ = n->get_ub(); - WARTHOG_GDEBUG_FMT_IF(pi->verbose_, "NEW UB: Incumbent Cost {}", sol->sum_of_edge_costs_); + WARTHOG_GDEBUG_FMT_IF( + pi->verbose_, "NEW UB: Incumbent Cost {}", + sol->sum_of_edge_costs_); } } @@ -193,7 +197,8 @@ class unidirectional_search mytimer.start(); open_->clear(); - io::observer_begin_search(listeners_, static_cast(pi->instance_id_), *pi); + io::observer_begin_search( + listeners_, static_cast(pi->instance_id_), *pi); // initialise the start node and push to OPEN { @@ -206,7 +211,8 @@ class unidirectional_search initialise_node_(start, pad_id::max(), 0, pi, par, sol); open_->push(start); - io::observer_generate_node(listeners_, nullptr, *start, 0, UINT32_MAX); + io::observer_generate_node( + listeners_, nullptr, *start, 0, UINT32_MAX); WARTHOG_GINFO_FMT_IF(pi->verbose_, "{}", *pi); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Start node: {}", *start); update_ub(start, sol, pi); @@ -252,7 +258,8 @@ class unidirectional_search open_->push(n); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Generate: {}", *n); update_ub(current, sol, pi); - io::observer_generate_node(listeners_, current, *n, gval, i); + io::observer_generate_node( + listeners_, current, *n, gval, i); continue; } } @@ -270,7 +277,8 @@ class unidirectional_search if(open_->contains(n)) { open_->decrease_key(n); - WARTHOG_GINFO_FMT_IF(pi->verbose_, "Updating: {}", *n); + WARTHOG_GINFO_FMT_IF( + pi->verbose_, "Updating: {}", *n); update_ub(current, sol, pi); continue; } @@ -278,7 +286,8 @@ class unidirectional_search if(reopen()) { open_->push(n); - WARTHOG_GINFO_FMT_IF(pi->verbose_, "Reopen: {}", *n); + WARTHOG_GINFO_FMT_IF( + pi->verbose_, "Reopen: {}", *n); update_ub(current, sol, pi); sol->met_.nodes_reopen_++; continue; @@ -300,15 +309,17 @@ class unidirectional_search sol->met_.nodes_surplus_ = open_->size(); sol->met_.heap_ops_ = open_->get_heap_ops(); - WARTHOG_GINFO_IF(pi->verbose_ && sol->sum_of_edge_costs_ == warthog::COST_MAX, "Search failed; no solution exists."); + WARTHOG_GINFO_IF( + pi->verbose_ && sol->sum_of_edge_costs_ == warthog::COST_MAX, + "Search failed; no solution exists."); } }; template< - typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>> -unidirectional_search( - H* heuristic, E* expander, Q* queue, - L listeners = L{}) -> unidirectional_search; + typename H, typename E, typename Q = util::pqueue_min, + typename L = std::tuple<>> +unidirectional_search(H* heuristic, E* expander, Q* queue, L listeners = L{}) + -> unidirectional_search; } // namespace warthog::search diff --git a/include/warthog/util/vec_io.h b/include/warthog/util/vec_io.h index 8f0e24c..8383c8a 100644 --- a/include/warthog/util/vec_io.h +++ b/include/warthog/util/vec_io.h @@ -1,7 +1,6 @@ #ifndef WARTHOG_UTIL_VEC_IO_H #define WARTHOG_UTIL_VEC_IO_H -#include #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include template [[deprecated("TDB")]] @@ -95,7 +95,9 @@ load_vector(std::FILE* file) size_t stuffRead = std::fread(&v[0], sizeof(T), s, file); if((int)stuffRead != s) { - WARTHOG_GERROR_FMT("we were expecting to read {} but we read {} elements instead", s, stuffRead); + WARTHOG_GERROR_FMT( + "we were expecting to read {} but we read {} elements instead", s, + stuffRead); throw std::runtime_error("std::fread failed"); } diff --git a/src/io/log.cpp b/src/io/log.cpp index d265548..423cc74 100644 --- a/src/io/log.cpp +++ b/src/io/log.cpp @@ -1,42 +1,51 @@ -#include -#include #include +#include #include +#include namespace warthog::io { -log_sink_stream::log_sink_stream() : log_sink{nullptr, - {{&log_sink_stream::write_trace,&log_sink_stream::write_debug, - &log_sink_stream::write_information,&log_sink_stream::write_warning, - &log_sink_stream::write_error,&log_sink_stream::write_critical}}} +log_sink_stream::log_sink_stream() + : log_sink{ + nullptr, + {{&log_sink_stream::write_trace, &log_sink_stream::write_debug, + &log_sink_stream::write_information, + &log_sink_stream::write_warning, &log_sink_stream::write_error, + &log_sink_stream::write_critical}}} { } -log_sink_stream::log_sink_stream(const std::filesystem::path& filename, std::ios_base::openmode mode) : log_sink_stream() +log_sink_stream::log_sink_stream( + const std::filesystem::path& filename, std::ios_base::openmode mode) + : log_sink_stream() { - data = this; + data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } log_sink_stream::log_sink_stream(std::ostream* stream) : log_sink_stream() { - data = this; + data = this; log_stream_ = stream; } -void log_sink_stream::open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void +log_sink_stream::open( + const std::filesystem::path& filename, std::ios_base::openmode mode) { - data = this; + data = this; owned_file_ = std::make_unique(filename, mode); log_stream_ = owned_file_.get(); } -void log_sink_stream::open(std::ostream& stream) +void +log_sink_stream::open(std::ostream& stream) { data = this; owned_file_.release(); log_stream_ = &stream; } -void log_sink_stream::open_stderr() +void +log_sink_stream::open_stderr() { data = this; owned_file_.release(); @@ -45,92 +54,118 @@ void log_sink_stream::open_stderr() // log_sink_stream -log_sink_std::log_sink_std() : log_sink{ - nullptr, - {{&log_sink_std::write_trace,&log_sink_std::write_debug, - &log_sink_std::write_information,&log_sink_std::write_warning, - &log_sink_std::write_error,&log_sink_std::write_critical}}} +log_sink_std::log_sink_std() + : log_sink{ + nullptr, + {{&log_sink_std::write_trace, &log_sink_std::write_debug, + &log_sink_std::write_information, &log_sink_std::write_warning, + &log_sink_std::write_error, &log_sink_std::write_critical}}} { } -log_sink_std::log_sink_std(bool cerr) : log_sink{ - cerr ? &std::cerr : &std::cout, - {{&log_sink_std::write_trace,&log_sink_std::write_debug, - &log_sink_std::write_information,&log_sink_std::write_warning, - &log_sink_std::write_error,&log_sink_std::write_critical}}} +log_sink_std::log_sink_std(bool cerr) + : log_sink{ + cerr ? &std::cerr : &std::cout, + {{&log_sink_std::write_trace, &log_sink_std::write_debug, + &log_sink_std::write_information, &log_sink_std::write_warning, + &log_sink_std::write_error, &log_sink_std::write_critical}}} { } -void log_sink_std::write_trace(void* logger, std::string_view msg) +void +log_sink_std::write_trace(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} TRACE] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_debug(void* logger, std::string_view msg) +void +log_sink_std::write_debug(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} DEBUG] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_information(void* logger, std::string_view msg) +void +log_sink_std::write_information(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} INFO] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_warning(void* logger, std::string_view msg) +void +log_sink_std::write_warning(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} WARN] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_error(void* logger, std::string_view msg) +void +log_sink_std::write_error(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} ERROR] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_std::write_critical(void* logger, std::string_view msg) +void +log_sink_std::write_critical(void* logger, std::string_view msg) { std::ostream* stream = static_cast(logger); assert(stream != nullptr); - *stream << std::format("[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", std::chrono::floor(WARTHOG_LOG_NOW), msg); + *stream << std::format( + "[{:" WARTHOG_LOG_TIME_FORMAT "} CRIT] {}\n", + std::chrono::floor(WARTHOG_LOG_NOW), msg); } -void log_sink_stream::write_trace(void* logger, std::string_view msg) +void +log_sink_stream::write_trace(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_trace(log_stream->log_stream_, msg); } -void log_sink_stream::write_debug(void* logger, std::string_view msg) +void +log_sink_stream::write_debug(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_debug(log_stream->log_stream_, msg); } -void log_sink_stream::write_information(void* logger, std::string_view msg) +void +log_sink_stream::write_information(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_information(log_stream->log_stream_, msg); } -void log_sink_stream::write_warning(void* logger, std::string_view msg) +void +log_sink_stream::write_warning(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_warning(log_stream->log_stream_, msg); } -void log_sink_stream::write_error(void* logger, std::string_view msg) +void +log_sink_stream::write_error(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); std::lock_guard lock(log_stream->lock_); log_sink_std::write_error(log_stream->log_stream_, msg); } -void log_sink_stream::write_critical(void* logger, std::string_view msg) +void +log_sink_stream::write_critical(void* logger, std::string_view msg) { log_sink_stream* log_stream = static_cast(logger); assert(log_stream != nullptr); @@ -138,18 +173,22 @@ void log_sink_stream::write_critical(void* logger, std::string_view msg) log_sink_std::write_critical(log_stream->log_stream_, msg); } -namespace { +namespace +{ global_logger_type global_logger_(log_sink_std(true).sink()); } -global_logger_type& glog() +global_logger_type& +glog() { return global_logger_; } -const log_sink& glogs() +const log_sink& +glogs() { return global_logger_.sink(); } -void set_glog(log_sink log) +void +set_glog(log_sink log) { global_logger_ = log; } diff --git a/src/io/observer.cpp b/src/io/observer.cpp index fd8567f..92e1316 100644 --- a/src/io/observer.cpp +++ b/src/io/observer.cpp @@ -1,16 +1,17 @@ -#include -#include #include #include #include +#include +#include namespace warthog::io { -stream_observer::stream_observer(const std::filesystem::path& filename, std::ios_base::openmode mode) +stream_observer::stream_observer( + const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } stream_observer::stream_observer(std::ostream& stream) { @@ -19,49 +20,59 @@ stream_observer::stream_observer(std::ostream& stream) stream_observer::stream_observer(const shared_stream_t& stream) { shared_stream_ = stream; - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } stream_observer::~stream_observer() = default; -void stream_observer::stream_open(const std::filesystem::path& filename, std::ios_base::openmode mode) +void +stream_observer::stream_open( + const std::filesystem::path& filename, std::ios_base::openmode mode) { shared_stream_ = std::make_shared(filename, mode); - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } -void stream_observer::stream(std::ostream& stream) +void +stream_observer::stream(std::ostream& stream) { - stream_ = &stream; + stream_ = &stream; shared_stream_ = nullptr; } -void stream_observer::stream_share(const shared_stream_t& stream) +void +stream_observer::stream_share(const shared_stream_t& stream) { shared_stream_ = stream; - stream_ = shared_stream_.get(); + stream_ = shared_stream_.get(); } -void stream_observer::stream_share(const stream_observer& stream) +void +stream_observer::stream_share(const stream_observer& stream) { shared_stream_ = stream.shared_stream_; - stream_ = stream.stream_; + stream_ = stream.stream_; } -void stream_observer::stream_stdout() +void +stream_observer::stream_stdout() { shared_stream_ = nullptr; - stream_ = &std::cout; + stream_ = &std::cout; } -void stream_observer::stream_stderr() +void +stream_observer::stream_stderr() { shared_stream_ = nullptr; - stream_ = &std::cerr; + stream_ = &std::cerr; } -void stream_observer::clear_stream() +void +stream_observer::clear_stream() { shared_stream_ = nullptr; - stream_ = nullptr; + stream_ = nullptr; } -void posthoc_trace::print_posthoc_header() +void +posthoc_trace::print_posthoc_header() { - if (*this) { + if(*this) + { stream() << R"posthoc(version: 1.4.0 events: )posthoc"; diff --git a/src/io/traces.cpp b/src/io/traces.cpp index 0a78304..47e8e21 100644 --- a/src/io/traces.cpp +++ b/src/io/traces.cpp @@ -1,14 +1,15 @@ -#include #include #include +#include namespace warthog::io { - -void grid_trace::print_posthoc_header() +void +grid_trace::print_posthoc_header() { - if (*this) { + if(*this) + { stream() << R"posthoc(version: 1.4.0 views: cell: @@ -53,92 +54,102 @@ void grid_trace::print_posthoc_header() } } -void grid_trace::begin_search(int id, const search::search_problem_instance& pi) +void +grid_trace::begin_search(int id, const search::search_problem_instance& pi) { posthoc_trace::begin_search(id); - if (*this) { - if (grid_ == nullptr) { + if(*this) + { + if(grid_ == nullptr) + { throw std::logic_error("grid_trace::grid_ is null"); } uint32_t x, y; grid_->to_unpadded_xy(pi.start_, x, y); - stream() << std::format(" - {{ type: source, id: {}, x: {}, y: {} }}\n", - pi.start_.id, x, y - ); + stream() << std::format( + " - {{ type: source, id: {}, x: {}, y: {} }}\n", pi.start_.id, x, + y); grid_->to_unpadded_xy(pi.target_, x, y); - stream() << std::format(" - {{ type: destination, id: {}, x: {}, y: {} }}\n", - pi.target_.id, x, y - ); + stream() << std::format( + " - {{ type: destination, id: {}, x: {}, y: {} }}\n", + pi.target_.id, x, y); } } void grid_trace::expand_node(const node& current) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (auto cpid = current.get_parent(); !cpid.is_none()) { + if(auto cpid = current.get_parent(); !cpid.is_none()) + { pid = std::format(", pId: {}", cpid.id); } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: expand, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g() - ); + stream() << std::format( + " - {{ type: expand, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g()); } } void grid_trace::relax_node(const node& current) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (auto cpid = current.get_parent(); !cpid.is_none()) { + if(auto cpid = current.get_parent(); !cpid.is_none()) + { pid = std::format(", pId: {}", cpid.id); } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: relax, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g() - ); + stream() << std::format( + " - {{ type: relax, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g()); } } - void grid_trace::close_node(const node& current) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (auto cpid = current.get_parent(); !cpid.is_none()) { + if(auto cpid = current.get_parent(); !cpid.is_none()) + { pid = std::format(", pId: {}", cpid.id); } uint32_t x, y; grid_->to_unpadded_xy(current.get_id(), x, y); - stream() << std::format(" - {{ type: close, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - current.get_id().id, pid, x, y, current.get_f(), current.get_g() - ); + stream() << std::format( + " - {{ type: close, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + current.get_id().id, pid, x, y, current.get_f(), current.get_g()); } } - void -grid_trace::generate_node(const node* parent, const node& child, cost_t, uint32_t) const +grid_trace::generate_node( + const node* parent, const node& child, cost_t, uint32_t) const { - if (*this) { + if(*this) + { assert(grid_ != nullptr); std::string pid; - if (parent != nullptr) { + if(parent != nullptr) + { pid = std::format(", pId: {}", parent->get_id().id); } uint32_t x, y; grid_->to_unpadded_xy(child.get_id(), x, y); - stream() << std::format(" - {{ type: generate, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", - child.get_id().id, pid, x, y, child.get_f(), child.get_g() - ); + stream() << std::format( + " - {{ type: generate, id: {}{}, x: {}, y: {}, f: {}, g: {} }}\n", + child.get_id().id, pid, x, y, child.get_f(), child.get_g()); } } diff --git a/src/search/problem_instance.cpp b/src/search/problem_instance.cpp index 7022a1a..602f767 100644 --- a/src/search/problem_instance.cpp +++ b/src/search/problem_instance.cpp @@ -12,8 +12,7 @@ operator<<(std::ostream& str, const problem_instance& pi) } std::ostream& -operator<<( - std::ostream& str, const search_problem_instance& pi) +operator<<(std::ostream& str, const search_problem_instance& pi) { pi.print(str); diff --git a/src/search/search_metrics.cpp b/src/search/search_metrics.cpp index 910ac13..64ed2d2 100644 --- a/src/search/search_metrics.cpp +++ b/src/search/search_metrics.cpp @@ -1,6 +1,7 @@ #include -namespace warthog::search { +namespace warthog::search +{ std::ostream& operator<<(std::ostream& str, const warthog::search::search_metrics& met) From 9622516a2a12415498b5ba5252488939ef8b8025 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Tue, 21 Oct 2025 15:38:35 +1100 Subject: [PATCH 20/33] added template node_pool --- include/warthog/memory/node_pool.h | 8 +- include/warthog/memory/typed_pool.h | 169 ++++++++++++++++++++++++++++ src/memory/node_pool.cpp | 6 +- 3 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 include/warthog/memory/typed_pool.h diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index cc5f5e8..12ffa90 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -25,8 +25,8 @@ #include "cpool.h" #include - -#include +#include +#include namespace warthog::memory { @@ -63,8 +63,8 @@ class node_pool init(size_t nblocks); size_t num_blocks_; - search::search_node** blocks_; - cpool* blockspool_; + std::unique_ptr blocks_; + std::unique_ptr blockspool_; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/memory/typed_pool.h b/include/warthog/memory/typed_pool.h new file mode 100644 index 0000000..5dbf247 --- /dev/null +++ b/include/warthog/memory/typed_pool.h @@ -0,0 +1,169 @@ +#ifndef WARTHOG_MEMORY_TYPED_POOL_H +#define WARTHOG_MEMORY_TYPED_POOL_H + +// memory/typed_pool.h +// +// A memory pool for nodes of a templated type. +// +// This in intended as a temperary pool before an allocation +// framework is added. +// +// @author: Ryan Hechenberger +// @created: 2025-10-25 +// + +#include "node_pool.h" + +namespace warthog::memory +{ + +template +class typed_pool +{ +public: + using value_type = T; + + typed_pool(size_t num_nodes); + ~typed_pool(); + + // return a warthog::search_node object corresponding to the given id. + // if the node has already been generated, return a pointer to the + // previous instance; otherwise allocate memory for a new object. + value_type* + generate(pad_id node_id); + + // return a pre-allocated pointer. if the corresponding node has not + // been allocated yet, return null + value_type* + get_ptr(pad_id node_id); + + size_t + mem(); + +private: + void + init(size_t nblocks); + + size_t num_blocks_ = 0; + std::unique_ptr blocks_; + std::unique_ptr blockspool_; + // uint64_t* node_init_; + // uint64_t node_init_sz_; +}; + +template +typed_pool::typed_pool(size_t num_nodes) +{ + init(num_nodes); +} + +template +void +typed_pool::init(size_t num_nodes) +{ + num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; + blocks_ = std::make_unique(num_blocks_); + for(size_t i = 0; i < num_blocks_; i++) + { + blocks_[i] = 0; + } + + // by default: + // allocate one chunk of memory of size + // DEFAULT_CHUNK_SIZE and assign addresses + // from that pool in order to generate blocks of nodes. when the pool is + // full, cpool pre-allocates more, one chunk at a time. + size_t block_sz = node_pool_ns::NBS * sizeof(value_type); + blockspool_ = std::make_unique(block_sz, 1); +} + +template +typed_pool::~typed_pool() +{ + // delete [] node_init_; + + blockspool_->reclaim(); + delete blockspool_; + + for(size_t i = 0; i < num_blocks_; i++) + { + if(blocks_[i] != 0) + { + // std::cerr << "deleting block: "< +value_type* +typed_pool::generate(pad_id node_id) +{ + sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; + + // id outside the pool address range + if(block_id > num_blocks_) { return 0; } + + // add a new block of nodes if necessary + if(!blocks_[block_id]) + { + // std::cerr << "generating block: "<allocate()) + value_type[node_pool_ns::NBS]; + + // initialise memory + sn_id_t current_id = sn_id_t{node_id} - list_id; + for(uint32_t i = 0; i < node_pool_ns::NBS; i += 8) + { + new(&blocks_[block_id][i]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 1]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 2]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 3]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 4]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 5]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 6]) + value_type(pad_id{current_id++}); + new(&blocks_[block_id][i + 7]) + value_type(pad_id{current_id++}); + } + } + + // return the node from its position in the assocated block + return &(blocks_[block_id][list_id]); +} + +template +value_type* +typed_pool::get_ptr(pad_id node_id) +{ + sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; + + // id outside the pool address range + if(block_id > num_blocks_) { return 0; } + + if(!blocks_[block_id]) { return 0; } + return &(blocks_[block_id][list_id]); +} + +template +size_t +typed_pool::mem() +{ + size_t bytes + = sizeof(*this) + blockspool_->mem() + num_blocks_ * sizeof(void*); + + return bytes; +} + +} // namespace warthog::memory + +#endif // WARTHOG_MEMORY_NODE_POOL_H diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index d2df7a1..f81232a 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -14,7 +14,7 @@ void node_pool::init(size_t num_nodes) { num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = new search::search_node*[num_blocks_]; + blocks_ = std::make_unique(num_blocks_); for(size_t i = 0; i < num_blocks_; i++) { blocks_[i] = 0; @@ -26,7 +26,7 @@ node_pool::init(size_t num_nodes) // from that pool in order to generate blocks of nodes. when the pool is // full, cpool pre-allocates more, one chunk at a time. size_t block_sz = node_pool_ns::NBS * sizeof(search::search_node); - blockspool_ = new cpool(block_sz, 1); + blockspool_ = std::make_unique(block_sz, 1); } node_pool::~node_pool() @@ -34,7 +34,6 @@ node_pool::~node_pool() // delete [] node_init_; blockspool_->reclaim(); - delete blockspool_; for(size_t i = 0; i < num_blocks_; i++) { @@ -44,7 +43,6 @@ node_pool::~node_pool() blocks_[i] = 0; } } - delete[] blocks_; } search::search_node* From 01f6f2c78027506aeb13a2e6b7e796ba534bef37 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 22 Oct 2025 15:54:10 +1100 Subject: [PATCH 21/33] added traits to unidirectional_search --- include/warthog/search/uds_traits.h | 90 ++++++++++++++++++- .../warthog/search/unidirectional_search.h | 48 ++++++---- 2 files changed, 122 insertions(+), 16 deletions(-) diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index bfaac52..34f339a 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -133,7 +133,7 @@ enum class reopen_policy // decide whether to renopen nodes already expanded (when their g-value // can be improved). we handle the positive case via specialisation. -template +template inline bool reopen() { @@ -147,6 +147,94 @@ reopen() return true; } +struct uds_default_traits +{ + // using node = std::tuple<>; + // using observer = std::tuple<>; + static constexpr admissibility_criteria ac = admissibility_criteria::any; + static constexpr feasibility_criteria fc = feasibility_criteria::until_exhaustion; + static constexpr reopen_policy rp = reopen_policy::no; +}; + +template < + typename N = search_node, + typename L = std::tuple<>, + admissibility_criteria AC = admissibility_criteria::any, + feasibility_criteria FC = feasibility_criteria::until_exhaustion, + reopen_policy RP = reopen_policy::no> +struct uds_traits +{ + using node = N; + using observer = L; + static constexpr admissibility_criteria ac = AC; + static constexpr feasibility_criteria fc = FC; + static constexpr reopen_policy rp = RP; +}; + +namespace details { + +template +struct uds_trait_node +{ + using type = search_node; +}; +template + requires requires { typename Traits::node; } +struct uds_trait_node +{ + using type = typename Traits::node; +}; + +template +struct uds_trait_observer +{ + using type = search_node; +}; +template + requires requires { typename Traits::observer; } +struct uds_trait_observer +{ + using type = typename Traits::observer; +}; + +} // namespace details + +template +using uds_trait_node = typename details::uds_trait_node::type; + +template +using uds_trait_observer = typename details::uds_trait_observer::type; + +template +inline consteval admissibility_criteria uds_trait_ac() noexcept +{ + if constexpr (requires { { Traits::ac } -> std::same_as; }) { + return Traits::ac; + } else { + return admissibility_criteria::any; + } +} + +template +inline consteval feasibility_criteria uds_trait_fc() noexcept +{ + if constexpr (requires { { Traits::fc } -> std::same_as; }) { + return Traits::fc; + } else { + return feasibility_criteria::until_exhaustion; + } +} + +template +inline consteval reopen_policy uds_trait_rp() noexcept +{ + if constexpr (requires { { Traits::rp } -> std::same_as; }) { + return Traits::rp; + } else { + return reopen_policy::no; + } +} + } // namespace warthog::search #endif // WARTHOG_SEARCH_UDS_TRAITS_H diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 6b75ced..ec9edf2 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -41,20 +41,28 @@ namespace warthog::search // (default: search for any solution, until OPEN is exhausted) template< typename H, typename E, typename Q = util::pqueue_min, - typename L = std::tuple<>, - admissibility_criteria AC = admissibility_criteria::any, - feasibility_criteria FC = feasibility_criteria::until_exhaustion, - reopen_policy RP = reopen_policy::no> -class unidirectional_search + typename Traits = uds_default_traits> +class unidirectional_search_full { public: - unidirectional_search( + using traits = Traits; + using search_node = uds_trait_node; + using L = uds_trait_observer; + + static constexpr admissibility_criteria AC = uds_trait_ac(); + static constexpr feasibility_criteria FC = uds_trait_fc(); + static constexpr reopen_policy RP = uds_trait_rp(); + + unidirectional_search_full( H* heuristic, E* expander, Q* queue, L listeners = L{}) : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) { } + unidirectional_search_full(const unidirectional_search_full& other) = delete; + ~unidirectional_search_full() = default; - ~unidirectional_search() { } + unidirectional_search_full& + operator=(const unidirectional_search_full& other) = delete; void get_pathcost(problem_instance* pi, search_parameters* par, solution* sol) @@ -138,14 +146,6 @@ class unidirectional_search Q* open_; [[no_unique_address]] L listeners_; - // no copy ctor - unidirectional_search(const unidirectional_search& other) { } - unidirectional_search& - operator=(const unidirectional_search& other) - { - return *this; - } - /** * Initialise a new 'search_node' for the ongoing search given the parent * node (@param current). @@ -315,6 +315,24 @@ class unidirectional_search } }; +// Keep for backward compatibility. +// Done as class instead of using to support user-defined deduction +template< + typename H, typename E, typename Q = util::pqueue_min, + typename L = std::tuple<>, + admissibility_criteria AC = admissibility_criteria::any, + feasibility_criteria FC = feasibility_criteria::until_exhaustion, + reopen_policy RP = reopen_policy::no> +class unidirectional_search : public unidirectional_search_full > +{ +public: + using unidirectional_search_full = typename unidirectional_search_full >::unidirectional_search_full; + + using unidirectional_search_full::unidirectional_search_full; +}; + template< typename H, typename E, typename Q = util::pqueue_min, typename L = std::tuple<>> From 75863a43f5dabc351b15e685a5cb1e421525ae7b Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 6 Nov 2025 15:17:10 +1100 Subject: [PATCH 22/33] added custom search_node to node_pool --- include/warthog/memory/node_pool.h | 41 +++++++++++++-- include/warthog/search/search_node.h | 36 ++++++------- src/memory/node_pool.cpp | 76 ++++++++-------------------- 3 files changed, 73 insertions(+), 80 deletions(-) diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index 12ffa90..ef1433d 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -27,23 +27,51 @@ #include #include #include +#include namespace warthog::memory { namespace node_pool_ns { -static const uint64_t NBS = 8; // node block size; set this >= 8 -static const uint64_t LOG2_NBS = 3; -static const uint64_t NBS_MASK = 7; +constexpr uint64_t LOG2_NBS = 6; // node block size = 2^n, n >= 3 +constexpr uint64_t NBS = 1 << LOG2_NBS; +constexpr uint64_t NBS_MASK = NBS - 1; +static_assert(LOG2_NBS >= 3, "must be at least 3 for size of 8"); } class node_pool { public: + node_pool(); node_pool(size_t num_nodes); ~node_pool(); + template T = search::search_node> + void set_type() noexcept + { + size_t block_sz = node_pool_ns::NBS * sizeof(T); + blockspool_ = std::make_unique(block_sz, 1); + create_block_ = [](void* block_p, sn_id_t block_id) noexcept + { + assert(block_p != nullptr); + T* block = reinterpret_cast(block_p); + pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; + for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) + { + std::construct_at(block + i, current_id); + } + }; + get_ptr_ = [](void** blocks, pad_id node_id) noexcept -> search::search_node* + { + sn_id_t block_id = static_cast(node_id) >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; + assert(blocks[block_id] != nullptr); + T* block = reinterpret_cast(blocks[block_id]); + return static_cast(block + list_id); + }; + } + // return a warthog::search_node object corresponding to the given id. // if the node has already been generated, return a pointer to the // previous instance; otherwise allocate memory for a new object. @@ -61,10 +89,13 @@ class node_pool private: void init(size_t nblocks); + void release(); - size_t num_blocks_; - std::unique_ptr blocks_; + size_t num_blocks_ = 0; + std::unique_ptr blocks_; std::unique_ptr blockspool_; + void (*create_block_)(void* block, sn_id_t bock_id) noexcept = nullptr; + search::search_node* (*get_ptr_)(void** blocks, pad_id start_id) noexcept = nullptr; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 90b9eba..c91e2ff 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -16,13 +16,10 @@ namespace warthog::search { -class search_node +struct search_node { -public: search_node(pad_id id = pad_id::max()) - : id_(id), parent_id_(warthog::SN_ID_MAX), g_(warthog::COST_MAX), - f_(warthog::COST_MAX), ub_(warthog::COST_MAX), status_(0), - priority_(warthog::INF32), search_number_(UINT32_MAX) + : id_(id) { refcount_++; } @@ -32,12 +29,12 @@ class search_node inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, - cost_t ub = warthog::COST_MAX) + cost_t h = warthog::COST_MAX) { parent_id_ = parent_id; f_ = f; g_ = g; - ub_ = ub; + h_ = h; search_number_ = search_number; status_ = false; } @@ -129,13 +126,13 @@ class search_node inline cost_t get_ub() const { - return ub_; + return h_; } inline void set_ub(cost_t ub) { - ub_ = ub; + h_ = ub; } inline void @@ -144,7 +141,7 @@ class search_node assert(g < g_); f_ = (f_ - g_) + g; g_ = g; - if(ub_ < warthog::COST_MAX) { ub_ = (ub_ - g_) + g; } + if(h_ < warthog::COST_MAX) { h_ = (h_ - g_) + g; } parent_id_ = parent_id; } @@ -209,7 +206,7 @@ class search_node out << "search_node id:" << get_id().id; out << " p_id: "; out << parent_id_.id; - out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ + out << " g: " << g_ << " f: " << this->get_f() << " ub: " << h_ << " expanded: " << get_expanded() << " " << " search_number_: " << search_number_; } @@ -226,19 +223,18 @@ class search_node return refcount_; } -private: - pad_id id_; - pad_id parent_id_; + pad_id id_ = pad_id(warthog::SN_ID_MAX); + pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); - cost_t g_; - cost_t f_; - cost_t ub_; + cost_t g_ = warthog::COST_MAX; + cost_t f_ = warthog::COST_MAX; + cost_t h_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? - uint8_t status_; // open or closed - uint32_t priority_; // expansion priority + uint8_t status_ = 0; // open or closed + uint32_t priority_ = warthog::INF32; // expansion priority - uint32_t search_number_; + uint32_t search_number_ = UINT32_MAX; static uint32_t refcount_; }; diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index f81232a..2011bb5 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -5,16 +5,19 @@ namespace warthog::memory { -node_pool::node_pool(size_t num_nodes) : blocks_(0) +node_pool::node_pool() +{ } +node_pool::node_pool(size_t num_nodes) { init(num_nodes); + set_type(); } void node_pool::init(size_t num_nodes) { num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = std::make_unique(num_blocks_); + blocks_ = std::make_unique(num_blocks_); for(size_t i = 0; i < num_blocks_; i++) { blocks_[i] = 0; @@ -25,80 +28,43 @@ node_pool::init(size_t num_nodes) // DEFAULT_CHUNK_SIZE and assign addresses // from that pool in order to generate blocks of nodes. when the pool is // full, cpool pre-allocates more, one chunk at a time. - size_t block_sz = node_pool_ns::NBS * sizeof(search::search_node); - blockspool_ = std::make_unique(block_sz, 1); } -node_pool::~node_pool() -{ - // delete [] node_init_; - - blockspool_->reclaim(); - - for(size_t i = 0; i < num_blocks_; i++) - { - if(blocks_[i] != 0) - { - // std::cerr << "deleting block: "<> node_pool_ns::LOG2_NBS; sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } + assert(block_id < num_blocks_); // add a new block of nodes if necessary if(!blocks_[block_id]) { // std::cerr << "generating block: "<allocate()) - search::search_node[node_pool_ns::NBS]; - - // initialise memory - sn_id_t current_id = sn_id_t{node_id} - list_id; - for(uint32_t i = 0; i < node_pool_ns::NBS; i += 8) - { - new(&blocks_[block_id][i]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 1]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 2]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 3]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 4]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 5]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 6]) - search::search_node(pad_id{current_id++}); - new(&blocks_[block_id][i + 7]) - search::search_node(pad_id{current_id++}); - } + blocks_[block_id] = blockspool_->allocate(); + create_block_(blocks_[block_id], block_id); } // return the node from its position in the assocated block - return &(blocks_[block_id][list_id]); + return get_ptr_(blocks_.get(), node_id); +} + +void node_pool::release() +{ + num_blocks_ = 0; + blocks_ = nullptr; + blockspool_ = nullptr; + create_block_ = nullptr; + get_ptr_ = nullptr; } search::search_node* node_pool::get_ptr(pad_id node_id) { - sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } - - if(!blocks_[block_id]) { return 0; } - return &(blocks_[block_id][list_id]); + assert((sn_id_t{node_id} >> node_pool_ns::LOG2_NBS) < num_blocks_); + return get_ptr_(blocks_.get(), node_id); } size_t From 92616fec1fac49bd331c0b42d9c990efae67be77 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Thu, 6 Nov 2025 15:26:10 +1100 Subject: [PATCH 23/33] improve node_pool get_ptr efficency --- include/warthog/memory/node_pool.h | 13 +++---------- src/memory/node_pool.cpp | 13 ++++++++----- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index ef1433d..ddf1297 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -50,6 +50,7 @@ class node_pool template T = search::search_node> void set_type() noexcept { + block_type_sizes_ = sizeof(search::search_node); size_t block_sz = node_pool_ns::NBS * sizeof(T); blockspool_ = std::make_unique(block_sz, 1); create_block_ = [](void* block_p, sn_id_t block_id) noexcept @@ -62,14 +63,6 @@ class node_pool std::construct_at(block + i, current_id); } }; - get_ptr_ = [](void** blocks, pad_id node_id) noexcept -> search::search_node* - { - sn_id_t block_id = static_cast(node_id) >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; - assert(blocks[block_id] != nullptr); - T* block = reinterpret_cast(blocks[block_id]); - return static_cast(block + list_id); - }; } // return a warthog::search_node object corresponding to the given id. @@ -92,10 +85,10 @@ class node_pool void release(); size_t num_blocks_ = 0; - std::unique_ptr blocks_; + std::unique_ptr blocks_; + size_t block_type_sizes_ = 0; std::unique_ptr blockspool_; void (*create_block_)(void* block, sn_id_t bock_id) noexcept = nullptr; - search::search_node* (*get_ptr_)(void** blocks, pad_id start_id) noexcept = nullptr; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index 2011bb5..05545ea 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -17,7 +17,7 @@ void node_pool::init(size_t num_nodes) { num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = std::make_unique(num_blocks_); + blocks_ = std::make_unique(num_blocks_); for(size_t i = 0; i < num_blocks_; i++) { blocks_[i] = 0; @@ -43,12 +43,12 @@ node_pool::generate(pad_id node_id) if(!blocks_[block_id]) { // std::cerr << "generating block: "<allocate(); + blocks_[block_id] = reinterpret_cast(blockspool_->allocate()); create_block_(blocks_[block_id], block_id); } // return the node from its position in the assocated block - return get_ptr_(blocks_.get(), node_id); + return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); } void node_pool::release() @@ -57,14 +57,17 @@ void node_pool::release() blocks_ = nullptr; blockspool_ = nullptr; create_block_ = nullptr; - get_ptr_ = nullptr; } search::search_node* node_pool::get_ptr(pad_id node_id) { assert((sn_id_t{node_id} >> node_pool_ns::LOG2_NBS) < num_blocks_); - return get_ptr_(blocks_.get(), node_id); + sn_id_t block_id = static_cast(node_id) >> node_pool_ns::LOG2_NBS; + sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; + assert(block_id < num_blocks_); + assert(blocks_[block_id] != nullptr); + return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); } size_t From 18950eba5e4bd3a0f554bcaf71ebaa8b50683c2d Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 12:11:03 +1100 Subject: [PATCH 24/33] fix bugs to allow dynamic typed node_pool --- include/warthog/memory/node_pool.h | 63 ++++++++++- include/warthog/memory/typed_pool.h | 169 ---------------------------- src/memory/node_pool.cpp | 21 +++- 3 files changed, 75 insertions(+), 178 deletions(-) delete mode 100644 include/warthog/memory/typed_pool.h diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index ddf1297..1a2be5e 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace warthog::memory { @@ -43,24 +44,69 @@ static_assert(LOG2_NBS >= 3, "must be at least 3 for size of 8"); class node_pool { public: + struct data_deleter_ptr + { + void (*del)(void*) = nullptr; + + constexpr data_deleter_ptr() noexcept = default; + constexpr data_deleter_ptr(void (*p)(void*)) noexcept : del(p) + { } + + void operator()(void *data) const noexcept + { + (*del)(data); + } + }; node_pool(); node_pool(size_t num_nodes); ~node_pool(); - template T = search::search_node> - void set_type() noexcept + template T = search::search_node, typename... NodeArgs> + void set_type(NodeArgs... node_args) noexcept { - block_type_sizes_ = sizeof(search::search_node); + clear(); + using arg_type = std::tuple; + if constexpr (sizeof...(NodeArgs) != 0) { + create_block_data_ = static_cast(new arg_type(std::forward(node_args)...)); + } + block_type_sizes_ = sizeof(T); size_t block_sz = node_pool_ns::NBS * sizeof(T); blockspool_ = std::make_unique(block_sz, 1); - create_block_ = [](void* block_p, sn_id_t block_id) noexcept + create_block_ = [](void* block_p, sn_id_t block_id, void* data) noexcept { assert(block_p != nullptr); T* block = reinterpret_cast(block_p); pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) { - std::construct_at(block + i, current_id); + if constexpr (sizeof...(NodeArgs) == 0) { + std::construct_at(block + i, current_id); + } else { + assert(data != nullptr); + std::apply([block_i=block + i, current_id](NodeX&&... args) { + std::construct_at(block_i, current_id, args...); + }, *static_cast(data) + ); + } + } + }; + clear_ = [](node_pool& np) noexcept + { + if (np.blocks_) { + for (size_t i = 0; i < np.num_blocks_; ++i) { + T* nodes = reinterpret_cast(np.blocks_[i]); + if (nodes != nullptr) { + for(uint32_t j = 0; j < node_pool_ns::NBS; ++j) { + std::destroy_at(nodes + j); + } + } + } + } + if constexpr (sizeof...(NodeArgs) == 0) { + if (!np.create_block_data_) { + arg_type* args = static_cast(np.create_block_data_); + delete args; + } } }; } @@ -79,6 +125,9 @@ class node_pool size_t mem(); + // reset nodes + void clear(); + private: void init(size_t nblocks); @@ -88,7 +137,9 @@ class node_pool std::unique_ptr blocks_; size_t block_type_sizes_ = 0; std::unique_ptr blockspool_; - void (*create_block_)(void* block, sn_id_t bock_id) noexcept = nullptr; + void (*create_block_)(void* block, sn_id_t bock_id, void* data) noexcept = nullptr; + void (*clear_)(node_pool& np) = nullptr; + void *create_block_data_; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/memory/typed_pool.h b/include/warthog/memory/typed_pool.h deleted file mode 100644 index 5dbf247..0000000 --- a/include/warthog/memory/typed_pool.h +++ /dev/null @@ -1,169 +0,0 @@ -#ifndef WARTHOG_MEMORY_TYPED_POOL_H -#define WARTHOG_MEMORY_TYPED_POOL_H - -// memory/typed_pool.h -// -// A memory pool for nodes of a templated type. -// -// This in intended as a temperary pool before an allocation -// framework is added. -// -// @author: Ryan Hechenberger -// @created: 2025-10-25 -// - -#include "node_pool.h" - -namespace warthog::memory -{ - -template -class typed_pool -{ -public: - using value_type = T; - - typed_pool(size_t num_nodes); - ~typed_pool(); - - // return a warthog::search_node object corresponding to the given id. - // if the node has already been generated, return a pointer to the - // previous instance; otherwise allocate memory for a new object. - value_type* - generate(pad_id node_id); - - // return a pre-allocated pointer. if the corresponding node has not - // been allocated yet, return null - value_type* - get_ptr(pad_id node_id); - - size_t - mem(); - -private: - void - init(size_t nblocks); - - size_t num_blocks_ = 0; - std::unique_ptr blocks_; - std::unique_ptr blockspool_; - // uint64_t* node_init_; - // uint64_t node_init_sz_; -}; - -template -typed_pool::typed_pool(size_t num_nodes) -{ - init(num_nodes); -} - -template -void -typed_pool::init(size_t num_nodes) -{ - num_blocks_ = ((num_nodes) >> node_pool_ns::LOG2_NBS) + 1; - blocks_ = std::make_unique(num_blocks_); - for(size_t i = 0; i < num_blocks_; i++) - { - blocks_[i] = 0; - } - - // by default: - // allocate one chunk of memory of size - // DEFAULT_CHUNK_SIZE and assign addresses - // from that pool in order to generate blocks of nodes. when the pool is - // full, cpool pre-allocates more, one chunk at a time. - size_t block_sz = node_pool_ns::NBS * sizeof(value_type); - blockspool_ = std::make_unique(block_sz, 1); -} - -template -typed_pool::~typed_pool() -{ - // delete [] node_init_; - - blockspool_->reclaim(); - delete blockspool_; - - for(size_t i = 0; i < num_blocks_; i++) - { - if(blocks_[i] != 0) - { - // std::cerr << "deleting block: "< -value_type* -typed_pool::generate(pad_id node_id) -{ - sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } - - // add a new block of nodes if necessary - if(!blocks_[block_id]) - { - // std::cerr << "generating block: "<allocate()) - value_type[node_pool_ns::NBS]; - - // initialise memory - sn_id_t current_id = sn_id_t{node_id} - list_id; - for(uint32_t i = 0; i < node_pool_ns::NBS; i += 8) - { - new(&blocks_[block_id][i]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 1]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 2]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 3]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 4]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 5]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 6]) - value_type(pad_id{current_id++}); - new(&blocks_[block_id][i + 7]) - value_type(pad_id{current_id++}); - } - } - - // return the node from its position in the assocated block - return &(blocks_[block_id][list_id]); -} - -template -value_type* -typed_pool::get_ptr(pad_id node_id) -{ - sn_id_t block_id = sn_id_t{node_id} >> node_pool_ns::LOG2_NBS; - sn_id_t list_id = sn_id_t{node_id} & node_pool_ns::NBS_MASK; - - // id outside the pool address range - if(block_id > num_blocks_) { return 0; } - - if(!blocks_[block_id]) { return 0; } - return &(blocks_[block_id][list_id]); -} - -template -size_t -typed_pool::mem() -{ - size_t bytes - = sizeof(*this) + blockspool_->mem() + num_blocks_ * sizeof(void*); - - return bytes; -} - -} // namespace warthog::memory - -#endif // WARTHOG_MEMORY_NODE_POOL_H diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index 05545ea..9f176c1 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -13,6 +13,11 @@ node_pool::node_pool(size_t num_nodes) set_type(); } +node_pool::~node_pool() +{ + clear(); +} + void node_pool::init(size_t num_nodes) { @@ -30,8 +35,6 @@ node_pool::init(size_t num_nodes) // full, cpool pre-allocates more, one chunk at a time. } -node_pool::~node_pool() = default; - search::search_node* node_pool::generate(pad_id node_id) { @@ -44,7 +47,7 @@ node_pool::generate(pad_id node_id) { // std::cerr << "generating block: "<(blockspool_->allocate()); - create_block_(blocks_[block_id], block_id); + create_block_(blocks_[block_id], block_id, create_block_data_); } // return the node from its position in the assocated block @@ -79,4 +82,16 @@ node_pool::mem() return bytes; } + +void node_pool::clear() +{ + if (clear_) { + (*clear_)(*this); + } + blockspool_ = nullptr; + create_block_ = nullptr; + clear_ = nullptr; + create_block_data_ = nullptr; +} + } // namespace warthog::memory From 645317408456bfc17b7086b096999633a218e466 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 12:11:17 +1100 Subject: [PATCH 25/33] udi bugfix for template type --- include/warthog/search/uds_traits.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 34f339a..3f2b466 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -208,7 +208,7 @@ using uds_trait_observer = typename details::uds_trait_observer::type; template inline consteval admissibility_criteria uds_trait_ac() noexcept { - if constexpr (requires { { Traits::ac } -> std::same_as; }) { + if constexpr (requires { { Traits::ac } -> std::convertible_to; }) { return Traits::ac; } else { return admissibility_criteria::any; @@ -218,7 +218,7 @@ inline consteval admissibility_criteria uds_trait_ac() noexcept template inline consteval feasibility_criteria uds_trait_fc() noexcept { - if constexpr (requires { { Traits::fc } -> std::same_as; }) { + if constexpr (requires { { Traits::fc } -> std::convertible_to; }) { return Traits::fc; } else { return feasibility_criteria::until_exhaustion; @@ -228,7 +228,7 @@ inline consteval feasibility_criteria uds_trait_fc() noexcept template inline consteval reopen_policy uds_trait_rp() noexcept { - if constexpr (requires { { Traits::rp } -> std::same_as; }) { + if constexpr (requires { { Traits::rp } -> std::convertible_to; }) { return Traits::rp; } else { return reopen_policy::no; From ac432fffea6dfe53276a33c47cf62fb3cd1a95d8 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 12:14:48 +1100 Subject: [PATCH 26/33] udi search traits must be same type but do not check reference --- include/warthog/search/uds_traits.h | 7 ++++--- include/warthog/util/template.h | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 3f2b466..0c93a5c 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -14,6 +14,7 @@ #include "search_metrics.h" #include +#include namespace warthog::search { @@ -208,7 +209,7 @@ using uds_trait_observer = typename details::uds_trait_observer::type; template inline consteval admissibility_criteria uds_trait_ac() noexcept { - if constexpr (requires { { Traits::ac } -> std::convertible_to; }) { + if constexpr (requires { { Traits::ac } -> util::same_as_rmref; }) { return Traits::ac; } else { return admissibility_criteria::any; @@ -218,7 +219,7 @@ inline consteval admissibility_criteria uds_trait_ac() noexcept template inline consteval feasibility_criteria uds_trait_fc() noexcept { - if constexpr (requires { { Traits::fc } -> std::convertible_to; }) { + if constexpr (requires { { Traits::fc } -> util::same_as_rmref; }) { return Traits::fc; } else { return feasibility_criteria::until_exhaustion; @@ -228,7 +229,7 @@ inline consteval feasibility_criteria uds_trait_fc() noexcept template inline consteval reopen_policy uds_trait_rp() noexcept { - if constexpr (requires { { Traits::rp } -> std::convertible_to; }) { + if constexpr (requires { { Traits::rp } -> util::same_as_rmref; }) { return Traits::rp; } else { return reopen_policy::no; diff --git a/include/warthog/util/template.h b/include/warthog/util/template.h index ce94e3d..d0a0b0b 100644 --- a/include/warthog/util/template.h +++ b/include/warthog/util/template.h @@ -108,6 +108,11 @@ choose_integer_sequence(auto value, TemplateFunc&& tfunc) value, std::forward(tfunc)); } +template +concept same_as_rmref = std::same_as, std::remove_reference_t>; +template +concept same_as_rmcvref = std::same_as, std::remove_cvref_t>; + } // namespace warthog::util #endif // WARTHOG_UTIL_CAST_H From b196854a192679b2b6c3707c78493d4fb86df66f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 03:51:45 +0000 Subject: [PATCH 27/33] auto clang-format action --- include/warthog/memory/node_pool.h | 105 +++++++++++------- include/warthog/search/search_node.h | 10 +- include/warthog/search/uds_traits.h | 76 +++++++------ .../warthog/search/unidirectional_search.h | 22 ++-- include/warthog/util/template.h | 10 +- src/memory/node_pool.cpp | 35 +++--- 6 files changed, 147 insertions(+), 111 deletions(-) diff --git a/include/warthog/memory/node_pool.h b/include/warthog/memory/node_pool.h index 1a2be5e..4d780ff 100644 --- a/include/warthog/memory/node_pool.h +++ b/include/warthog/memory/node_pool.h @@ -24,11 +24,11 @@ // #include "cpool.h" -#include -#include -#include #include +#include +#include #include +#include namespace warthog::memory { @@ -36,7 +36,7 @@ namespace warthog::memory namespace node_pool_ns { constexpr uint64_t LOG2_NBS = 6; // node block size = 2^n, n >= 3 -constexpr uint64_t NBS = 1 << LOG2_NBS; +constexpr uint64_t NBS = 1 << LOG2_NBS; constexpr uint64_t NBS_MASK = NBS - 1; static_assert(LOG2_NBS >= 3, "must be at least 3 for size of 8"); } @@ -49,10 +49,10 @@ class node_pool void (*del)(void*) = nullptr; constexpr data_deleter_ptr() noexcept = default; - constexpr data_deleter_ptr(void (*p)(void*)) noexcept : del(p) - { } + constexpr data_deleter_ptr(void (*p)(void*)) noexcept : del(p) { } - void operator()(void *data) const noexcept + void + operator()(void* data) const noexcept { (*del)(data); } @@ -61,50 +61,66 @@ class node_pool node_pool(size_t num_nodes); ~node_pool(); - template T = search::search_node, typename... NodeArgs> - void set_type(NodeArgs... node_args) noexcept + template< + std::derived_from T = search::search_node, + typename... NodeArgs> + void + set_type(NodeArgs... node_args) noexcept { clear(); using arg_type = std::tuple; - if constexpr (sizeof...(NodeArgs) != 0) { - create_block_data_ = static_cast(new arg_type(std::forward(node_args)...)); + if constexpr(sizeof...(NodeArgs) != 0) + { + create_block_data_ = static_cast( + new arg_type(std::forward(node_args)...)); } block_type_sizes_ = sizeof(T); - size_t block_sz = node_pool_ns::NBS * sizeof(T); - blockspool_ = std::make_unique(block_sz, 1); - create_block_ = [](void* block_p, sn_id_t block_id, void* data) noexcept - { - assert(block_p != nullptr); - T* block = reinterpret_cast(block_p); - pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; - for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) - { - if constexpr (sizeof...(NodeArgs) == 0) { - std::construct_at(block + i, current_id); - } else { - assert(data != nullptr); - std::apply([block_i=block + i, current_id](NodeX&&... args) { - std::construct_at(block_i, current_id, args...); - }, *static_cast(data) - ); - } - } + size_t block_sz = node_pool_ns::NBS * sizeof(T); + blockspool_ = std::make_unique(block_sz, 1); + create_block_ = [](void* block_p, sn_id_t block_id, + void* data) noexcept { + assert(block_p != nullptr); + T* block = reinterpret_cast(block_p); + pad_id current_id = pad_id{block_id << node_pool_ns::LOG2_NBS}; + for(uint32_t i = 0; i < node_pool_ns::NBS; ++i, ++current_id.id) + { + if constexpr(sizeof...(NodeArgs) == 0) + { + std::construct_at(block + i, current_id); + } + else + { + assert(data != nullptr); + std::apply( + [block_i = block + i, + current_id](NodeX&&... args) { + std::construct_at(block_i, current_id, args...); + }, + *static_cast(data)); + } + } }; - clear_ = [](node_pool& np) noexcept - { - if (np.blocks_) { - for (size_t i = 0; i < np.num_blocks_; ++i) { + clear_ = [](node_pool& np) noexcept { + if(np.blocks_) + { + for(size_t i = 0; i < np.num_blocks_; ++i) + { T* nodes = reinterpret_cast(np.blocks_[i]); - if (nodes != nullptr) { - for(uint32_t j = 0; j < node_pool_ns::NBS; ++j) { + if(nodes != nullptr) + { + for(uint32_t j = 0; j < node_pool_ns::NBS; ++j) + { std::destroy_at(nodes + j); } } } } - if constexpr (sizeof...(NodeArgs) == 0) { - if (!np.create_block_data_) { - arg_type* args = static_cast(np.create_block_data_); + if constexpr(sizeof...(NodeArgs) == 0) + { + if(!np.create_block_data_) + { + arg_type* args + = static_cast(np.create_block_data_); delete args; } } @@ -126,20 +142,23 @@ class node_pool mem(); // reset nodes - void clear(); + void + clear(); private: void init(size_t nblocks); - void release(); + void + release(); size_t num_blocks_ = 0; std::unique_ptr blocks_; size_t block_type_sizes_ = 0; std::unique_ptr blockspool_; - void (*create_block_)(void* block, sn_id_t bock_id, void* data) noexcept = nullptr; + void (*create_block_)(void* block, sn_id_t bock_id, void* data) noexcept + = nullptr; void (*clear_)(node_pool& np) = nullptr; - void *create_block_data_; + void* create_block_data_; // uint64_t* node_init_; // uint64_t node_init_sz_; }; diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index c91e2ff..505f809 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -18,11 +18,7 @@ namespace warthog::search struct search_node { - search_node(pad_id id = pad_id::max()) - : id_(id) - { - refcount_++; - } + search_node(pad_id id = pad_id::max()) : id_(id) { refcount_++; } ~search_node() { refcount_--; } @@ -223,7 +219,7 @@ struct search_node return refcount_; } - pad_id id_ = pad_id(warthog::SN_ID_MAX); + pad_id id_ = pad_id(warthog::SN_ID_MAX); pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); cost_t g_ = warthog::COST_MAX; @@ -231,7 +227,7 @@ struct search_node cost_t h_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? - uint8_t status_ = 0; // open or closed + uint8_t status_ = 0; // open or closed uint32_t priority_ = warthog::INF32; // expansion priority uint32_t search_number_ = UINT32_MAX; diff --git a/include/warthog/search/uds_traits.h b/include/warthog/search/uds_traits.h index 0c93a5c..9d65c8b 100644 --- a/include/warthog/search/uds_traits.h +++ b/include/warthog/search/uds_traits.h @@ -153,46 +153,47 @@ struct uds_default_traits // using node = std::tuple<>; // using observer = std::tuple<>; static constexpr admissibility_criteria ac = admissibility_criteria::any; - static constexpr feasibility_criteria fc = feasibility_criteria::until_exhaustion; - static constexpr reopen_policy rp = reopen_policy::no; + static constexpr feasibility_criteria fc + = feasibility_criteria::until_exhaustion; + static constexpr reopen_policy rp = reopen_policy::no; }; -template < - typename N = search_node, - typename L = std::tuple<>, +template< + typename N = search_node, typename L = std::tuple<>, admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> struct uds_traits { - using node = N; - using observer = L; + using node = N; + using observer = L; static constexpr admissibility_criteria ac = AC; static constexpr feasibility_criteria fc = FC; static constexpr reopen_policy rp = RP; }; -namespace details { +namespace details +{ -template +template struct uds_trait_node { using type = search_node; }; -template - requires requires { typename Traits::node; } +template + requires requires { typename Traits::node; } struct uds_trait_node { using type = typename Traits::node; }; -template +template struct uds_trait_observer { using type = search_node; }; -template - requires requires { typename Traits::observer; } +template + requires requires { typename Traits::observer; } struct uds_trait_observer { using type = typename Traits::observer; @@ -200,40 +201,53 @@ struct uds_trait_observer } // namespace details -template +template using uds_trait_node = typename details::uds_trait_node::type; -template +template using uds_trait_observer = typename details::uds_trait_observer::type; -template -inline consteval admissibility_criteria uds_trait_ac() noexcept +template +inline consteval admissibility_criteria +uds_trait_ac() noexcept { - if constexpr (requires { { Traits::ac } -> util::same_as_rmref; }) { + if constexpr(requires { + { + Traits::ac + } -> util::same_as_rmref; + }) + { return Traits::ac; - } else { - return admissibility_criteria::any; } + else { return admissibility_criteria::any; } } -template -inline consteval feasibility_criteria uds_trait_fc() noexcept +template +inline consteval feasibility_criteria +uds_trait_fc() noexcept { - if constexpr (requires { { Traits::fc } -> util::same_as_rmref; }) { + if constexpr(requires { + { + Traits::fc + } -> util::same_as_rmref; + }) + { return Traits::fc; - } else { - return feasibility_criteria::until_exhaustion; } + else { return feasibility_criteria::until_exhaustion; } } -template -inline consteval reopen_policy uds_trait_rp() noexcept +template +inline consteval reopen_policy +uds_trait_rp() noexcept { - if constexpr (requires { { Traits::rp } -> util::same_as_rmref; }) { + if constexpr(requires { + { Traits::rp } -> util::same_as_rmref; + }) + { return Traits::rp; - } else { - return reopen_policy::no; } + else { return reopen_policy::no; } } } // namespace warthog::search diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index ec9edf2..7bb0d5c 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -45,24 +45,26 @@ template< class unidirectional_search_full { public: - using traits = Traits; + using traits = Traits; using search_node = uds_trait_node; - using L = uds_trait_observer; + using L = uds_trait_observer; static constexpr admissibility_criteria AC = uds_trait_ac(); static constexpr feasibility_criteria FC = uds_trait_fc(); static constexpr reopen_policy RP = uds_trait_rp(); - + unidirectional_search_full( H* heuristic, E* expander, Q* queue, L listeners = L{}) : heuristic_(heuristic), expander_(expander), open_(queue), listeners_(listeners) { } - unidirectional_search_full(const unidirectional_search_full& other) = delete; + unidirectional_search_full(const unidirectional_search_full& other) + = delete; ~unidirectional_search_full() = default; unidirectional_search_full& - operator=(const unidirectional_search_full& other) = delete; + operator=(const unidirectional_search_full& other) + = delete; void get_pathcost(problem_instance* pi, search_parameters* par, solution* sol) @@ -323,12 +325,14 @@ template< admissibility_criteria AC = admissibility_criteria::any, feasibility_criteria FC = feasibility_criteria::until_exhaustion, reopen_policy RP = reopen_policy::no> -class unidirectional_search : public unidirectional_search_full > +class unidirectional_search + : public unidirectional_search_full< + H, E, Q, uds_traits> { public: - using unidirectional_search_full = typename unidirectional_search_full >::unidirectional_search_full; + using unidirectional_search_full = typename unidirectional_search_full< + H, E, Q, + uds_traits>::unidirectional_search_full; using unidirectional_search_full::unidirectional_search_full; }; diff --git a/include/warthog/util/template.h b/include/warthog/util/template.h index d0a0b0b..47213c2 100644 --- a/include/warthog/util/template.h +++ b/include/warthog/util/template.h @@ -108,10 +108,12 @@ choose_integer_sequence(auto value, TemplateFunc&& tfunc) value, std::forward(tfunc)); } -template -concept same_as_rmref = std::same_as, std::remove_reference_t>; -template -concept same_as_rmcvref = std::same_as, std::remove_cvref_t>; +template +concept same_as_rmref + = std::same_as, std::remove_reference_t>; +template +concept same_as_rmcvref + = std::same_as, std::remove_cvref_t>; } // namespace warthog::util diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index 9f176c1..92c0d58 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -5,8 +5,7 @@ namespace warthog::memory { -node_pool::node_pool() -{ } +node_pool::node_pool() { } node_pool::node_pool(size_t num_nodes) { init(num_nodes); @@ -46,19 +45,22 @@ node_pool::generate(pad_id node_id) if(!blocks_[block_id]) { // std::cerr << "generating block: "<(blockspool_->allocate()); + blocks_[block_id] + = reinterpret_cast(blockspool_->allocate()); create_block_(blocks_[block_id], block_id, create_block_data_); } // return the node from its position in the assocated block - return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); + return reinterpret_cast( + blocks_[block_id] + list_id * block_type_sizes_); } -void node_pool::release() +void +node_pool::release() { - num_blocks_ = 0; - blocks_ = nullptr; - blockspool_ = nullptr; + num_blocks_ = 0; + blocks_ = nullptr; + blockspool_ = nullptr; create_block_ = nullptr; } @@ -70,7 +72,8 @@ node_pool::get_ptr(pad_id node_id) sn_id_t list_id = static_cast(node_id) & node_pool_ns::NBS_MASK; assert(block_id < num_blocks_); assert(blocks_[block_id] != nullptr); - return reinterpret_cast(blocks_[block_id] + list_id * block_type_sizes_); + return reinterpret_cast( + blocks_[block_id] + list_id * block_type_sizes_); } size_t @@ -82,15 +85,13 @@ node_pool::mem() return bytes; } - -void node_pool::clear() +void +node_pool::clear() { - if (clear_) { - (*clear_)(*this); - } - blockspool_ = nullptr; - create_block_ = nullptr; - clear_ = nullptr; + if(clear_) { (*clear_)(*this); } + blockspool_ = nullptr; + create_block_ = nullptr; + clear_ = nullptr; create_block_data_ = nullptr; } From 8cd576a373b4ee702205027bc5c4357d4f22da9b Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 14:58:16 +1100 Subject: [PATCH 28/33] revert search_node h_ -> ub_ change --- include/warthog/search/search_node.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 505f809..b1dca2f 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -25,12 +25,12 @@ struct search_node inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, - cost_t h = warthog::COST_MAX) + cost_t ub = warthog::COST_MAX) { parent_id_ = parent_id; f_ = f; g_ = g; - h_ = h; + ub_ = ub; search_number_ = search_number; status_ = false; } @@ -122,13 +122,13 @@ struct search_node inline cost_t get_ub() const { - return h_; + return ub_; } inline void set_ub(cost_t ub) { - h_ = ub; + ub_ = ub; } inline void @@ -137,7 +137,7 @@ struct search_node assert(g < g_); f_ = (f_ - g_) + g; g_ = g; - if(h_ < warthog::COST_MAX) { h_ = (h_ - g_) + g; } + if(ub_ < warthog::COST_MAX) { ub_ = (ub_ - g_) + g; } parent_id_ = parent_id; } @@ -202,7 +202,7 @@ struct search_node out << "search_node id:" << get_id().id; out << " p_id: "; out << parent_id_.id; - out << " g: " << g_ << " f: " << this->get_f() << " ub: " << h_ + out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ << " expanded: " << get_expanded() << " " << " search_number_: " << search_number_; } @@ -224,7 +224,7 @@ struct search_node cost_t g_ = warthog::COST_MAX; cost_t f_ = warthog::COST_MAX; - cost_t h_ = warthog::COST_MAX; + cost_t ub_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? uint8_t status_ = 0; // open or closed From 080b9fbe927a143da0e95e03f4b8ded6586a9e27 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Fri, 13 Mar 2026 14:59:53 +1100 Subject: [PATCH 29/33] manual format --- include/warthog/search/search_node.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index b1dca2f..0c5279b 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -222,8 +222,8 @@ struct search_node pad_id id_ = pad_id(warthog::SN_ID_MAX); pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); - cost_t g_ = warthog::COST_MAX; - cost_t f_ = warthog::COST_MAX; + cost_t g_ = warthog::COST_MAX; + cost_t f_ = warthog::COST_MAX; cost_t ub_ = warthog::COST_MAX; // TODO steal the high-bit from priority instead of ::status_ ? From bca97acdf11a554246bc8e7fd055085e35afe5bc Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Tue, 17 Mar 2026 13:54:28 +1100 Subject: [PATCH 30/33] update serach_node with noexcept and removed static refcount --- include/warthog/search/search_node.h | 71 +++++++++++----------------- src/search/search_node.cpp | 15 +++++- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 0c5279b..1ebb9fd 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -18,14 +18,14 @@ namespace warthog::search struct search_node { - search_node(pad_id id = pad_id::max()) : id_(id) { refcount_++; } + search_node() noexcept = default; + search_node(pad_id id = pad_id::max()) noexcept : id_(id) { } - ~search_node() { refcount_--; } inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, - cost_t ub = warthog::COST_MAX) + cost_t ub = warthog::COST_MAX) noexcept { parent_id_ = parent_id; f_ = f; @@ -36,103 +36,103 @@ struct search_node } inline uint32_t - get_search_number() const + get_search_number() const noexcept { return search_number_; } inline void - set_search_number(uint32_t search_number) + set_search_number(uint32_t search_number) noexcept { search_number_ = search_number; } inline pad_id - get_id() const + get_id() const noexcept { return id_; } inline void - set_id(pad_id id) + set_id(pad_id id) noexcept { id_ = id; } inline bool - get_expanded() const + get_expanded() const noexcept { return status_; } inline void - set_expanded(bool expanded) + set_expanded(bool expanded) noexcept { status_ = expanded; } inline pad_id - get_parent() const + get_parent() const noexcept { return parent_id_; } inline void - set_parent(pad_id parent_id) + set_parent(pad_id parent_id) noexcept { parent_id_ = parent_id; } inline uint32_t - get_priority() const + get_priority() const noexcept { return priority_; } inline void - set_priority(uint32_t priority) + set_priority(uint32_t priority) noexcept { priority_ = priority; } inline cost_t - get_g() const + get_g() const noexcept { return g_; } inline void - set_g(cost_t g) + set_g(cost_t g) noexcept { g_ = g; } inline cost_t - get_f() const + get_f() const noexcept { return f_; } inline void - set_f(cost_t f) + set_f(cost_t f) noexcept { f_ = f; } inline cost_t - get_ub() const + get_ub() const noexcept { return ub_; } inline void - set_ub(cost_t ub) + set_ub(cost_t ub) noexcept { ub_ = ub; } inline void - relax(cost_t g, pad_id parent_id) + relax(cost_t g, pad_id parent_id) noexcept { assert(g < g_); f_ = (f_ - g_) + g; @@ -142,7 +142,7 @@ struct search_node } inline bool - operator<(const search_node& other) const + operator<(const search_node& other) const noexcept { // static uint64_t SIGN_MASK = UINT64_MAX & (1ULL<<63); // cost_t result = this->f_ - other.f_; @@ -163,7 +163,7 @@ struct search_node } inline bool - operator>(const search_node& other) const + operator>(const search_node& other) const noexcept { if(f_ > other.f_) { return true; } if(f_ < other.f_) { return false; } @@ -174,14 +174,14 @@ struct search_node } inline bool - operator==(const search_node& other) const + operator==(const search_node& other) const noexcept { if(!(*this < other) && !(*this > other)) { return true; } return false; } inline bool - operator<=(const search_node& other) const + operator<=(const search_node& other) const noexcept { if(*this < other) { return true; } if(!(*this > other)) { return true; } @@ -189,36 +189,22 @@ struct search_node } inline bool - operator>=(const search_node& other) const + operator>=(const search_node& other) const noexcept { if(*this > other) { return true; } if(!(*this < other)) { return true; } return false; } - inline void - print(std::ostream& out) const - { - out << "search_node id:" << get_id().id; - out << " p_id: "; - out << parent_id_.id; - out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ - << " expanded: " << get_expanded() << " " - << " search_number_: " << search_number_; - } + void + print(std::ostream& out) const; uint32_t - mem() + mem() noexcept { return sizeof(*this); } - static uint32_t - get_refcount() - { - return refcount_; - } - pad_id id_ = pad_id(warthog::SN_ID_MAX); pad_id parent_id_ = pad_id(warthog::SN_ID_MAX); @@ -231,7 +217,6 @@ struct search_node uint32_t priority_ = warthog::INF32; // expansion priority uint32_t search_number_ = UINT32_MAX; - static uint32_t refcount_; }; struct cmp_less_search_node diff --git a/src/search/search_node.cpp b/src/search/search_node.cpp index 4e90feb..6ce1187 100644 --- a/src/search/search_node.cpp +++ b/src/search/search_node.cpp @@ -1,6 +1,17 @@ #include -unsigned int warthog::search::search_node::refcount_ = 0; +namespace warthog::search { + +void +search_node::print(std::ostream& out) const +{ + out << "search_node id:" << get_id().id; + out << " p_id: "; + out << parent_id_.id; + out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ + << " expanded: " << get_expanded() << " " + << " search_number_: " << search_number_; +} std::ostream& operator<<(std::ostream& str, const warthog::search::search_node& sn) @@ -9,3 +20,5 @@ operator<<(std::ostream& str, const warthog::search::search_node& sn) return str; } + +} // namespace warthog::search From 56ff38f23a69cd537cde82067de2301a93ee4df5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:29:10 +0000 Subject: [PATCH 31/33] auto clang-format action --- include/warthog/search/search_node.h | 1 - src/search/search_node.cpp | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/warthog/search/search_node.h b/include/warthog/search/search_node.h index 1ebb9fd..b091c9a 100644 --- a/include/warthog/search/search_node.h +++ b/include/warthog/search/search_node.h @@ -21,7 +21,6 @@ struct search_node search_node() noexcept = default; search_node(pad_id id = pad_id::max()) noexcept : id_(id) { } - inline void init( uint32_t search_number, pad_id parent_id, cost_t g, cost_t f, diff --git a/src/search/search_node.cpp b/src/search/search_node.cpp index 6ce1187..cae2985 100644 --- a/src/search/search_node.cpp +++ b/src/search/search_node.cpp @@ -1,6 +1,7 @@ #include -namespace warthog::search { +namespace warthog::search +{ void search_node::print(std::ostream& out) const @@ -9,8 +10,8 @@ search_node::print(std::ostream& out) const out << " p_id: "; out << parent_id_.id; out << " g: " << g_ << " f: " << this->get_f() << " ub: " << ub_ - << " expanded: " << get_expanded() << " " - << " search_number_: " << search_number_; + << " expanded: " << get_expanded() << " " + << " search_number_: " << search_number_; } std::ostream& From efd1487daf90a43bf1d2b57ea653d62f7a0792d7 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 25 Mar 2026 13:04:39 +1100 Subject: [PATCH 32/33] revert dominated change --- include/warthog/search/unidirectional_search.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/warthog/search/unidirectional_search.h b/include/warthog/search/unidirectional_search.h index 7bb0d5c..e8e871d 100644 --- a/include/warthog/search/unidirectional_search.h +++ b/include/warthog/search/unidirectional_search.h @@ -255,7 +255,7 @@ class unidirectional_search_full if(n->get_search_number() != current->get_search_number()) { initialise_node_(n, current->get_id(), gval, pi, par, sol); - if(n->get_f() <= sol->sum_of_edge_costs_) + if(n->get_f() < sol->sum_of_edge_costs_) { open_->push(n); WARTHOG_GINFO_FMT_IF(pi->verbose_, "Generate: {}", *n); From f55e53cd28f889d5245480285d1b710337edbd59 Mon Sep 17 00:00:00 2001 From: Ryan Hechenberger Date: Wed, 25 Mar 2026 13:20:11 +1100 Subject: [PATCH 33/33] change node_pool constructor to be default --- src/memory/node_pool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/memory/node_pool.cpp b/src/memory/node_pool.cpp index 92c0d58..147bdae 100644 --- a/src/memory/node_pool.cpp +++ b/src/memory/node_pool.cpp @@ -5,7 +5,7 @@ namespace warthog::memory { -node_pool::node_pool() { } +node_pool::node_pool() = default; node_pool::node_pool(size_t num_nodes) { init(num_nodes);