Started progress bar

Fortune for shuffle3's current commit: Middle blessing − 中吉
progress
Avril 2 years ago
parent 0a0fb4555a
commit dd54a4823d
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -6,6 +6,11 @@ INCLUDE = include
PROJECT=shuffle3 PROJECT=shuffle3
# Link to these libraries dynamicalls
SHARED_LIBS=
# Link to these libraries statically
STATIC_LIBS=fmt
# Currently supported: # Currently supported:
# _FS_SPILL_BUFFER: Use file backed buffer instead of memory backed one for unshuffling. See `shuffle3.h`. # _FS_SPILL_BUFFER: Use file backed buffer instead of memory backed one for unshuffling. See `shuffle3.h`.
# Setting the value to `DYN` enables the dynamic buffer, setting it to `MAP` enabled memory-mapped buffer. `MAP` is usually the fastest of the 3 modes. # Setting the value to `DYN` enables the dynamic buffer, setting it to `MAP` enabled memory-mapped buffer. `MAP` is usually the fastest of the 3 modes.
@ -34,7 +39,7 @@ CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors
CFLAGS += $(COMMON_FLAGS) --std=gnu11 CFLAGS += $(COMMON_FLAGS) --std=gnu11
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++23 -fno-exceptions CXXFLAGS += $(COMMON_FLAGS) --std=gnu++23 -fno-exceptions
# XXX: We might need exceptions soon, for OOP usage, because we try multiple approaches from most efficient to least. # XXX: We might need exceptions soon, for OOP usage, because we try multiple approaches from most efficient to least.
LDFLAGS += LDFLAGS += $(addsuffix .a,$(addprefix -l:lib,$(STATIC_LIBS))) $(addprefix -l,$(SHARED_LIBS))
STRIP=strip STRIP=strip

@ -1,5 +1,9 @@
#pragma once #pragma once
#include <string>
#include <memory>
#include <cstdio>
#ifndef __cplusplus #ifndef __cplusplus
#error "C++ header only" #error "C++ header only"
#endif #endif
@ -11,6 +15,153 @@ namespace pr {
T* take(T*& ptr) noexcept { return std::exchange(ptr, nullptr); } T* take(T*& ptr) noexcept { return std::exchange(ptr, nullptr); }
} }
using increment_t = long double;
template<typename T>
concept Bar = requires(T& v) {
{ v.spin(increment_t(0)) };
};
template<Bar>
struct Carrier;
struct Dynamic {
template<Bar B> friend class Carrier<B>;
constexpr auto make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>);
constexpr virtual void spin(increment_t) =0;
constexpr virtual ~Dynamic() {}
private:
constexpr Dynamic() noexcept {}
};
template<Bar B>
struct Carrier : public virtual Dynamic {
using held_type = B;
template<typename... Args> requires(std::constructible_from<B, Args...>)
constexpr Carrier(Args&&... args) noexcept(std::is_nothrow_constructible_v<B, Args...>)
: Dynamic(), member_(std::forward<decltype(args)>(args)) {}
constexpr Carrier(Carrier&& m) noexcept(std::is_nothrow_move_constructible_v<B>)
requires(std::is_move_constructible_v<B>)
: Dynamic(), member_(std::move(m.member_)) {}
constexpr Carrier(Carrier const& c) noexcept(std::is_nothrow_copy_constructible_v<B>)
requires(std::is_copy_constructible_v<B>)
: Dynamic(), member_(c.member_) {}
constexpr Carrier& operator=(Carrier&& m) noexcept(std::is_nothrow_move_assignable_v<B>) requires(std::is_move_assignable_v<B>)
{ if(this != &m) member_ = std::move(m.member_); return *this; }
constexpr Carrier& operator=(Carrier const& c) noexcept(std::is_nothrow_copy_assignable_v<B>) requires(std::is_copy_assignable_v<B>)
{ if(this != &m) member_ = c.member_; return *this; }
constexpr void spin(increment_t a) override { member_.spin(a); }
template<typename... Args> requires(std::is_invocable_v<B::spin, B&, increment_t, Args...>)
constexpr void spin(increment_t a, Args&&... args) noexcept(std::is_nothrow_invocable_v<B::spin, B&, increment_t, decltype(args)...>)
{ return member_.spin(a, std::forward<decltype(args)>(args)...); }
constexpr virtual ~Carrier() {}
private:
B member_;
};
template<Bar T>
Carrier(T) -> Carrier<T>;
template<Bar T>
Carrier(T const&) -> Carrier<T>;
template<Bar T>
Carrier(T&&) -> Carrier<T>;
constexpr auto Dynamic::make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>) { return Carrier(std::move(bar)); }
template<Bar T>
constexpr std::unique_ptr<Dynamic> make_dyn(T&& bar) noexcept(std::is_nothrow_move_constructible_v<T>)
{
template<typename T, template<typename U> Inst>
constexpr bool is_inst_of = requires{
requires(std::is_same_v<T, Inst<U>>);
};
template<typename U, typename T, template<typename...> Ptr = std::unique_ptr>
constexpr Ptr<U> static_ucast(Ptr<T>&& from) noexcept
{ return Ptr<U>{static_cast<U*>(std::move(from).release())}; }
template<typename U, typename T>
constexpr std::shared_ptr<U> static_ucast<U, T, std::shared_ptr>(std::shared_ptr<T>&& from) noexcept
{ return std::static_pointer_cast<U>(std::move(from)); }
if constexpr(is_inst_of<T, Carrier>) return static_ucast<Dynamic>(std::make_unique<T>(std::move(bar)));
else return static_ucast<Dynamic>(std::make_unique<Carrier<T>>(std::move(bar)));
}
constexpr void spin(Bar auto& bar, std::convertible_to<increment_t> auto&& a) { return bar.spin(increment_t(a)); }
struct None {
constexpr None() noexcept = default;
constexpr ~None() noexcept = default;
constexpr void spin(increment_t) noexcept {}
};
// A bounded progress-bar
struct Progress {
using fract_t = increment_t;
constexpr static inline bool DEFAULT_WIDTH = 50;
constexpr static inline fract_t DEFAULT_TERM_FRACT=1.0l/3.0l;
[[gnu::nonnull(1)]]
Progress(FILE* p = stdout) noexcept;
Progress(const Progress& p) noexcept;
Progress(Progress&& p) noexcept;
Progress& operator=(Progress const& c) noexcept;
Progress& operator=(Progress&& m) noexcept;
virtual ~Progress();
std::string& tag() const noexcept;
const std::string& tag() const noexcept;
FILE* output() const noexcept;
FILE*& output() noexcept;
size_t& width() noexcept;
size_t width() const noexcept;
fract_t& fraction() noexcept;
fract_t fraction() const noexcept;
fract_t percentage() const noexcept;
inline auto percentage() noexcept {
struct v final {
Progress* p_;
v(Progress* p) noexcept : p_(p) {}
v(v&&) noexcept = default;
v(v const&) noexcept = default;
v& operator=(v const&) noexcept = default;
v& operator=(v&&) noexcept = default;
~v() noexcept = default;
const v& operator=(fract_t f) const noexcept { p_->percentage(f); return *this; }
operator fract_t() const noexcept { return p_->percentage(); }
};
return v(this);
}
void spin(increment_t by, bool render=true, bool flush=true) noexcept;
void render(bool flush = true) const noexcept;
private:
void percentage(fract_t fract) noexcept;
struct _impl;
std::shared_ptr<_impl> inner_;
};
//TODO: Spinny bar thing (unbounded)
/*
struct Bar { struct Bar {
Bar() noexcept = default; Bar() noexcept = default;
Bar(const Bar&) noexcept = default; Bar(const Bar&) noexcept = default;
@ -74,4 +225,5 @@ namespace pr {
using base_t::_num; using base_t::_num;
size_t _max; size_t _max;
}; };
*/
} }

@ -0,0 +1,114 @@
#include <string>
#include <vector>
#include <fmt/format.h>
#include <sys/ioctl.h>
#include <perc.h>
namespace {
size_t twidth(int fd = STDIN_FILENO, size_t or = Progress::DEFAULT_WIDTH) noexcept
{
struct winsize w;
if(ioctl(fd, TIOCGWINSZ, &w) == -1) return or;
return size_t(w.ws_col);
}
size_t def_bar_width(size_t tw) noexcept
{
return size_t(std::round(DEFAULT_TERM_FRACT * fract_t(tw)));
}
}
namespace pr {
struct Progress::_impl {
Progress::fract_t fract; // 0..=1
FILE* output;
size_t width;
std::string aux;
};
size_t& Progress::width() noexcept { return inner_->width; }
size_t Progress::width() const noexcept { return inner_->width; }
fract_t& Progress::fraction() noexcept { return inner_->fract; }
fract_t Progress::fraction() const noexcept { return inner_->fract; }
fract_t Progress::percentage() const noexcept { return inner_->fract * 100.0l; }
void Progress::percentage(fract_t fract) noexcept { inner_->fract = fract / 100.0l; }
std::string& Progress::tag() noexcept { return inner_->aux; }
const std::string& Progress::tag() const noexcept { return inner_->aux; }
FILE* Progress::output() const noexcept { return inner_->output; }
FILE*& Progress::output() noexcept { return inner_->output; }
void Progress::render(bool flush) const noexcept
{
constinit thread_local static std::vector<char> buffer{};
const auto& inner = *inner_;
FILE* out = inner.output;
auto wf = fract_t(inner.width) * inner.fract;
auto perc = percentage();
size_t cf = size_t(std::floor(wf));
auto print = []<typename F> (F f, auto&&... args) {
return fmt::format_to(std::back_inserter(buffer), std::forward<F>(f), std::forward<decltype(args)>(args)...);
};
buffer.clear();
// Render bar
buffer.push_back('[');
for(size_t i=0;i<inner.width;i++)
if(i<=cf) buffer.push_back( '#' );
else buffer.push_back( ' ' );
buffer.push_back( ']' );
print(": {:00.2}%", perc);
if(inner.aux.size())
print(" ({})", std::string_view(inner.aux));
// Print bar
fprintf(out, "\r%.*s", int(buffer.size() & INT_MAX), static_cast<const char*>(buffer.data()));
// Flush output stream
if(flush) fflush(out);
}
void Progress::spin(increment_t by, bool r, bool f) noexcept
{
auto& inner = *inner_;
inner.fract+=by;
if(inner.fract > 1.0l)
inner.fract=1.0l;
if(r) this->render(f);
}
[[gnu::nonnull(1)]]
Progress::Progress(FILE* p) noexcept
: inner_(std::make_unique<_impl>(0, p, def_bar_width(twidth(fileno(p))) )) {}
Progress::Progress(const Progress& p) noexcept
: inner_(p.inner_ ? std::make_unique<_impl>(*p.inner_) : nullptr) {}
Progress::Progress(Progress&& p) noexcept
: inner_(std::move(p.inner_)) {}
Progress& Progress::operator=(Progress&& p) noexcept
{
if(this != &p) inner_ = std::move(p.inner_);
return *this;
}
Progress& Progress::operator=(Progress const& p) noexcept
{
if(this != &p) inner_ = p.inner_;
return *this;
}
Progress::~Progress() {
if(inner_)
fputc('\n', inner_->output);
}
}
Loading…
Cancel
Save