diff --git a/Makefile b/Makefile index 6a23304..2b47e6d 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,9 @@ INCLUDE = include PROJECT=shuffle3 # Link to these libraries dynamicalls -SHARED_LIBS= +SHARED_LIBS=fmt # Link to these libraries statically -STATIC_LIBS=fmt +STATIC_LIBS= # Currently supported: # _FS_SPILL_BUFFER: Use file backed buffer instead of memory backed one for unshuffling. See `shuffle3.h`. diff --git a/include/perc.h b/include/perc.h index 6f62e37..9fe0948 100644 --- a/include/perc.h +++ b/include/perc.h @@ -2,6 +2,10 @@ #include #include +#include +#include +#include + #include #ifndef __cplusplus @@ -10,28 +14,78 @@ namespace pr { - namespace details [[gnu::visibility("inernal")]]{ + namespace details [[gnu::visibility("internal")]] { +/* + template typename P> + struct generic_valid { template constexpr static inline bool value = requires{ typename P; }; }; + template class P> + struct generic : std::conditional_t::value, + std::true_type, + std::false_type> + { template using type = P; }; + + template typename T, typename... Args> + concept Generic = generic_valid::template value; + + template + using generic_t = generic::template type; + + template template T> + concept AnyGeneric = requires{ typename generic_valid; }; +*/ template - T* take(T*& ptr) noexcept { return std::exchange(ptr, nullptr); } + T* take(T*&& ptr) noexcept { return std::exchange(ptr, nullptr); } + +/* + template Inst, typename T> + struct is_inst_of { constexpr static inline bool value = std::is_same_v>; }; + + template + constexpr inline bool is_inst_of_v = is_inst_of::value; +*/ + + template + constexpr std::unique_ptr static_ucast(std::unique_ptr&& from) noexcept + { return std::unique_ptr{static_cast(std::move(from).release())}; } + + template + constexpr std::shared_ptr static_ucast(std::shared_ptr&& from) noexcept + { return std::static_pointer_cast(std::move(from)); } + + + template requires(requires(From&& p) { + { p.release() }; + requires(std::is_constructible_v); + }) + constexpr Ptr static_pcast(From&& p) noexcept { return Ptr{p.release()}; } + } using increment_t = long double; template - concept Bar = requires(T& v) { + concept Bar = requires(std::remove_const_t>& v) { { v.spin(increment_t(0)) }; - + { std::as_const(v).aux() } -> std::convertible_to; + //{ v.aux() } -> std::assignable_from; + { (v.aux() = std::string{}) } -> std::same_as>; + //requires(std::assignable_from, std::string_view>); + { v.aux() += std::string_view{} } -> std::convertible_to; + { v.aux(std::declval()) } -> std::convertible_to; }; template struct Carrier; struct Dynamic { - template friend class Carrier; + template friend class Carrier; constexpr auto make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v>); constexpr virtual void spin(increment_t) =0; + constexpr virtual std::string& aux() =0; + constexpr virtual std::string const& aux() const noexcept =0; + constexpr virtual std::string aux(std::string&& s) { return std::exchange(aux(), std::move(s)); } constexpr virtual ~Dynamic() {} private: @@ -43,7 +97,7 @@ namespace pr { template requires(std::constructible_from) constexpr Carrier(Args&&... args) noexcept(std::is_nothrow_constructible_v) - : Dynamic(), member_(std::forward(args)) {} + : Dynamic(), member_(std::forward(args)...) {} constexpr Carrier(Carrier&& m) noexcept(std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v) : Dynamic(), member_(std::move(m.member_)) {} @@ -55,9 +109,14 @@ namespace pr { { if(this != &m) member_ = std::move(m.member_); return *this; } constexpr Carrier& operator=(Carrier const& c) noexcept(std::is_nothrow_copy_assignable_v) requires(std::is_copy_assignable_v) - { if(this != &m) member_ = c.member_; return *this; } + { if(this != &c) member_ = c.member_; return *this; } constexpr void spin(increment_t a) override { member_.spin(a); } + + constexpr std::string& aux() override { return member_.aux(); } + constexpr std::string const& aux() const noexcept override { return member_.aux(); } + constexpr std::string aux(std::string&& s) override { return member_.aux(std::move(s)); } + template requires(std::is_invocable_v) constexpr void spin(increment_t a, Args&&... args) noexcept(std::is_nothrow_invocable_v) { return member_.spin(a, std::forward(args)...); } @@ -75,33 +134,77 @@ namespace pr { constexpr auto Dynamic::make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v>) { return Carrier(std::move(bar)); } - template - constexpr std::unique_ptr make_dyn(T&& bar) noexcept(std::is_nothrow_move_constructible_v) + template> + constexpr Ptr make_dyn(T&& bar) noexcept(std::is_nothrow_move_constructible_v) { - template Inst> - constexpr bool is_inst_of = requires{ - requires(std::is_same_v>); - }; - template Ptr = std::unique_ptr> - constexpr Ptr static_ucast(Ptr&& from) noexcept - { return Ptr{static_cast(std::move(from).release())}; } - - template - constexpr std::shared_ptr static_ucast(std::shared_ptr&& from) noexcept - { return std::static_pointer_cast(std::move(from)); } + using namespace details; + if constexpr(requires{ + typename T::held_type; + requires(std::is_same_v, T>); + }) return static_pcast(static_ucast(std::make_unique(std::move(bar)))); + else return static_pcast(static_ucast(std::make_unique>(std::move(bar)))); + } - if constexpr(is_inst_of) return static_ucast(std::make_unique(std::move(bar))); - else return static_ucast(std::make_unique>(std::move(bar))); + template> + constexpr Ptr make_dyn(T const& bar) noexcept(std::is_nothrow_move_constructible_v) + { + if constexpr(std::is_copy_constructible_v) { + T nbar{bar}; + return make_dyn(std::move(nbar)); + } else { + struct unsafe_ref { + constexpr unsafe_ref(const T& ba) noexcept : b(std::addressof(ba)) {} + constexpr unsafe_ref(const unsafe_ref&) noexcept = default; + constexpr ~unsafe_ref() noexcept = default; + constexpr unsafe_ref(unsafe_ref&& b) noexcept : b(std::exchange(b.b, nullptr)) {} + + constexpr unsafe_ref& operator=(unsafe_ref const&) noexcept = default; + constexpr unsafe_ref& operator=(unsafe_ref&& m) noexcept { + if(this != &m) + b = std::exchange(m.b, nullptr); + return *this; + } + + const T* b; + + constexpr operator T const&() const noexcept { return *b; } + }; + unsafe_ref re{bar}; + return make_dyn(std::move(re)); + } } + template requires(requires(std::unique_ptr&& p) { details::static_pcast(std::move(p)); }) + constexpr Ptr make_dyn_for(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v>) + { return make_dyn(std::move(bar)); } + constexpr void spin(Bar auto& bar, std::convertible_to auto&& a) { return bar.spin(increment_t(a)); } struct None { constexpr None() noexcept = default; constexpr ~None() noexcept = default; - constexpr void spin(increment_t) noexcept {} + constexpr void spin(increment_t) const noexcept {} + constexpr auto aux() noexcept { return not_string(); } + inline std::string const& aux() const noexcept { return none_; } + constexpr std::string aux(std::convertible_to auto&& s) const noexcept { return {}; } //{ return std::exchange(none_, std::string(std::move(s))); } + private: + struct not_string { + constexpr not_string() noexcept= default; + constexpr ~not_string() noexcept= default; + + [[gnu::const]] + constexpr not_string& operator+=(std::convertible_to auto&&) { return *this; } + [[gnu::const]] + constexpr not_string& operator=(std::convertible_to auto&&) { return *this; } + + inline operator std::string&() noexcept { return none_; } + inline operator std::string const&() const noexcept { return none_; } + + }; + /*constinit*/ thread_local static inline std::string none_; }; + constexpr inline None disable{}; // A bounded progress-bar struct Progress { @@ -120,9 +223,14 @@ namespace pr { virtual ~Progress(); - std::string& tag() const noexcept; + inline std::string& aux() noexcept { return tag(); } + inline std::string const& aux() const noexcept { return tag(); } + inline std::string aux(std::convertible_to auto&& s) + { return std::exchange(tag(), std::string(std::move(s))); } +protected: + std::string& tag() noexcept; const std::string& tag() const noexcept; - +public: FILE* output() const noexcept; FILE*& output() noexcept; @@ -145,7 +253,7 @@ namespace pr { ~v() noexcept = default; const v& operator=(fract_t f) const noexcept { p_->percentage(f); return *this; } - operator fract_t() const noexcept { return p_->percentage(); } + operator fract_t() const noexcept { return static_cast(p_)->percentage(); } }; return v(this); } @@ -159,7 +267,76 @@ namespace pr { std::shared_ptr<_impl> inner_; }; + +#ifdef SPINNER + class Spinner { + consteval static auto _GENERATE_MAP(auto const& map, size_t size) noexcept + -> std::array + { + static_assert(sizeof(map) == size, "Bad map size"); + std::array out{}; + while( size --> 0 ) out[int(map[size])] = size; + return out; + } + constexpr static increment_t range(increment_t by) + { + if(by < -1.0l) return -1.0l; + else if(by > 1.0l) return 1.0l; + return by; + } + public: + constexpr static inline auto ROUTINE = "|/-\\|/-\\|"; + constexpr static inline auto ROUTINE_SIZE = sizeof(ROUTINE); + constexpr static inline auto REVERSE_MAP = _GENERATE_MAP(ROUTINE, ROUTINE_SIZE); + static_assert(ROUTINE_SIZE != sizeof(char*), "Invalid routine size"); + + constexpr ssize_t range(int sz) noexcept + { + /*if(__builtin_expect(sz < 0, false)) { + + std::terminate(); // TODO: How to handle wrapping negatives?? Ugh. + }*/ + return ssize_t(sz) % ssize_t(ROUTINE_SIZE); + } + constexpr Spinner(size_t n) noexcept + : cur_(ROUTINE[range(n)]) {} + constexpr Spinner(char c = ROUTINE[0]) noexcept + : cur_(range(REVERSE_MAP[int(c)])) {} + + constexpr Spinner(Spinner const&) noexcept = default; + constexpr Spinner(Spinner &&) noexcept = default; + ~Spinner(); + + inline void spin(int by) noexcept + { + operator+=(by); + render(); + } + inline void spin(increment_by by) noexcept + { + spin(int(range(by) * increment_t(ROUTINE_SIZE))); + } + + inline Spinner& operator+=(int by) noexcept + { + cur_ = ROUTINE[size_t(ssize_t(REVERSE_MAP[int(cur_)]) + range(by))]; + return *this; + } + + inline Spinner& operator++() noexcept { spin(1); return *this; } + inline Spinner operator++(int) noexcept { Spinner s = *this; ++s; return *this; } + constexpr Spinner& operator=(Spinner&&) noexcept = default; + constexpr Spinner& operator=(Spinner const&) noexcept = default; + + constexpr char& character() noexcept { return cur_; } + constexpr char character() const noexcept { return cur_; } + + void render(bool flush=true); + private: + char cur_ = '|'; + }; //TODO: Spinny bar thing (unbounded) +#endif /* struct Bar { diff --git a/src/perc.cpp b/src/perc.cpp index ed5f9c5..1073a1c 100644 --- a/src/perc.cpp +++ b/src/perc.cpp @@ -8,17 +8,20 @@ #include +using fract_t = pr::Progress::fract_t; + namespace { - size_t twidth(int fd = STDIN_FILENO, size_t or = Progress::DEFAULT_WIDTH) noexcept + using namespace pr; + size_t twidth(int fd = STDIN_FILENO, size_t orr = Progress::DEFAULT_WIDTH) noexcept { struct winsize w; - if(ioctl(fd, TIOCGWINSZ, &w) == -1) return or; + if(ioctl(fd, TIOCGWINSZ, &w) == -1) return orr; 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))); + return size_t(std::round(Progress::DEFAULT_TERM_FRACT * fract_t(tw))); } } @@ -57,9 +60,9 @@ namespace pr { size_t cf = size_t(std::floor(wf)); - auto print = [] (F f, auto&&... args) { - return fmt::format_to(std::back_inserter(buffer), std::forward(f), std::forward(args)...); - }; + //auto print = [] (F f, auto&&... args) { +#define print(...) fmt::format_to(std::back_inserter(buffer), __VA_ARGS__); + //}; buffer.clear(); // Render bar @@ -75,11 +78,12 @@ namespace pr { fprintf(out, "\r%.*s", int(buffer.size() & INT_MAX), static_cast(buffer.data())); // Flush output stream if(flush) fflush(out); +#undef print } void Progress::spin(increment_t by, bool r, bool f) noexcept { auto& inner = *inner_; - inner.fract+=by; + inner.fract=by; if(inner.fract > 1.0l) inner.fract=1.0l; if(r) this->render(f); @@ -111,4 +115,21 @@ namespace pr { if(inner_) fputc('\n', inner_->output); } + + +#ifdef SPINNER + Spinner::~Spinner() { + //TODO: Add output: if(cur_) fputc('\n', output); + } + + void Spinner::render(bool flush) { + /*char arr[] = { + '\r', + cur_, + 0, + }; + fputs(arr, output); + if(flush) fflush(output);*/ + } +#endif }