File progress_tracker.hpp
File List > lib > utilities > progress_tracker.hpp
Go to the documentation of this file
#pragma once
#include <chrono>
#include <cstdint>
#include <memory>
#include <optional>
#include <source_location>
#include <string>
#include <utility>
class ProgressTracker;
class ProgressObserver {
ProgressTracker* m_child;
protected:
virtual void update_progress(double progress) = 0;
virtual void text_update(const std::string& text, int depth = 0) = 0;
ProgressObserver() : m_child(nullptr) {}
public:
ProgressTracker* child() { return m_child; }
virtual ~ProgressObserver();
friend class ProgressTracker;
};
// CLI terminal progress: ProgressTracker updates propagate immediately to all observers;
// only stdout printing is rate-limited here. GUI observers (ProgressBox, etc.) are unaffected.
class ProgressBar : public ProgressObserver {
static constexpr std::chrono::milliseconds PRINT_INTERVAL{500};
double m_last_printed_progress = -1;
double m_latest_progress = -1;
std::chrono::steady_clock::time_point m_last_print_time{};
void print_progress(double progress);
void maybe_print_progress(double progress);
protected:
virtual void update_progress(double progress) override;
virtual void text_update(const std::string& text, int depth = 0) override;
public:
~ProgressBar() override;
};
class ProgressTracker : public ProgressObserver {
double m_proportion;
ProgressObserver* m_observer;
std::optional<std::pair<double, double>> m_subtracker_range;
bool m_visible = true;
uint64_t m_trace_scope_id = 0;
void _set_proportion(double proportion);
protected:
virtual void update_progress(double progress) override;
public:
explicit ProgressTracker(ProgressObserver* observer = nullptr, std::string name = "",
std::source_location location = std::source_location::current(),
double range_start = 0.0, double range_end = 1.0);
virtual void text_update(const std::string& text, int depth = 0) override;
ProgressTracker(const ProgressTracker& other) = delete;
ProgressTracker& operator=(const ProgressTracker& other) = delete;
ProgressTracker(ProgressTracker&& other);
ProgressTracker& operator=(ProgressTracker&& other) = delete;
void set_proportion(double proportion);
void set_visible(bool v) { m_visible = v; }
bool is_visible() const { return m_visible; }
// Thread-safe: only advances when `proportion` exceeds the current value (for OpenMP loops
// where work completes out of order).
void report_parallel_progress(double proportion);
// visible: nullopt = inherit parent, true = force visible, false = force invisible
ProgressTracker subtracker(double start, double end, std::string name = "",
std::source_location location = std::source_location::current(),
std::optional<bool> visible = std::nullopt);
// Callee entry: set scope display name and emit status text.
void begin_tracking(std::string text, std::source_location location);
virtual ~ProgressTracker();
double proportion() const { return m_proportion; }
};
class AsyncProgressTracker {
std::shared_ptr<ProgressTracker> m_tracker;
public:
explicit AsyncProgressTracker(ProgressObserver* observer = nullptr, std::string name = "")
: m_tracker(std::make_shared<ProgressTracker>(observer, std::move(name))) {}
std::shared_ptr<ProgressTracker> tracker() { return m_tracker; }
};
// Default parent tracker is progress_tracker.
#define SUBTRACKER_GET(_1, _2, _3, NAME, ...) NAME
#define SUBTRACKER(...) SUBTRACKER_GET(__VA_ARGS__, SUBTRACKER_3, SUBTRACKER_2)(__VA_ARGS__)
#define SUBTRACKER_2(start, end) \
(progress_tracker).subtracker((start), (end), "", std::source_location::current())
#define SUBTRACKER_3(start, end, tracker) \
((tracker)).subtracker((start), (end), "", std::source_location::current())
#define SUBTRACKER_HIDDEN(...) \
SUBTRACKER_GET(__VA_ARGS__, SUBTRACKER_HIDDEN_3, SUBTRACKER_HIDDEN_2)(__VA_ARGS__)
#define SUBTRACKER_HIDDEN_2(start, end) \
(progress_tracker).subtracker((start), (end), "", std::source_location::current(), false)
#define SUBTRACKER_HIDDEN_3(start, end, tracker) \
((tracker)).subtracker((start), (end), "", std::source_location::current(), false)
#define SUBTRACKER_VISIBLE(...) \
SUBTRACKER_GET(__VA_ARGS__, SUBTRACKER_VISIBLE_3, SUBTRACKER_VISIBLE_2)(__VA_ARGS__)
#define SUBTRACKER_VISIBLE_2(start, end) \
(progress_tracker).subtracker((start), (end), "", std::source_location::current(), true)
#define SUBTRACKER_VISIBLE_3(start, end, tracker) \
((tracker)).subtracker((start), (end), "", std::source_location::current(), true)
#define START_TRACKER_GET(_1, _2, NAME, ...) NAME
#define START_TRACKER(...) \
START_TRACKER_GET(__VA_ARGS__, START_TRACKER_2, START_TRACKER_1)(__VA_ARGS__)
#define START_TRACKER_1(text) \
(progress_tracker).begin_tracking((text), std::source_location::current())
#define START_TRACKER_2(tracker, text) \
((tracker)).begin_tracking((text), std::source_location::current())