alec

Abstraction Layer for Escape Codes
git clone git://git.dimitrijedobrota.com/alec.git
Log | Files | Refs | README | LICENSE

commit c63bca584930693f0470ee517f924d3eedccabb7
parent 621cc1fdfe336bbff877ceac0318e289d2eb811f
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Mon,  1 Jan 2024 23:18:06 +0000

Rewrite, use template variables

* Not locked in with basic_ostream
* Greatly simplify the template code
* Cleaner implementation
* Enforce validity with concepts
* Further improved my understanding of templates
* Add details namespace to hide implementation

Diffstat:
MCMakeLists.txt | 2+-
Mdemo/demo.cpp | 4++--
Msrc/alec.hpp | 316+++++++++++++++++++++++++++++++------------------------------------------------
3 files changed, 127 insertions(+), 195 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -3,7 +3,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) project( Alec - VERSION 0.0.4 + VERSION 0.0.5 DESCRIPTION "Abstraction Layer for Escape Codes" HOMEPAGE_URL https://git.dimitrijedobrota.com/alec.git LANGUAGES CXX diff --git a/demo/demo.cpp b/demo/demo.cpp @@ -7,10 +7,10 @@ int main(void) { std::cout << ABUF_SHOW << CURSOR_HIDE; - std::cout << CURSOR_POSITION<1, 1> << FOREGROUND<91> << "HELLO!\n"; + std::cout << CURSOR_POSITION<1, 1> << FOREGROUND_256<91> << "HELLO!\n"; std::cout << CURSOR_DOWN<3>; - std::cout << FOREGROUND<30> << BACKGROUND<196> << "WORLD!\n"; + std::cout << FOREGROUND_256<30> << BACKGROUND_256<196> << "WORLD!\n"; std::cout << BACKGROUND<DEFAULT> << "testing 1...\n" << FOREGROUND<DEFAULT>; std::cout << DECOR_SET<INVERSE> << "testing 2...\n" << DECOR_RESET<INVERSE>; diff --git a/src/alec.hpp b/src/alec.hpp @@ -1,10 +1,11 @@ #ifndef ALEC_ALEC_H #define ALEC_ALEC_H +#include <algorithm> #include <array> #include <cstdint> #include <iostream> -#include <string_view> +#include <type_traits> namespace ALEC { @@ -47,233 +48,164 @@ enum class DECOR { using enum COLOR; using enum DECOR; -template <std::string_view const &...Strs> struct join { - static constexpr auto arr = []() { - constexpr std::size_t len = (Strs.size() + ... + 0); - std::array<char, len + 1> arr{}; - auto append = [i = 0, &arr](auto const &s) mutable { - for (auto c : s) - arr[i++] = c; - }; - (append(Strs), ...); +namespace details { + +template <std::size_t N> struct string_literal { + consteval string_literal(const char (&str)[N]) { std::copy_n(str, N, value); } + consteval std::size_t size() const { return N; } + + char value[N]; +}; + +template <auto... Args> struct escape_t { + template <typename T> static consteval std::size_t size(T val); + template <typename T> static constexpr char *append(char *ptr, T val); + + template <std::size_t N> static constexpr std::size_t size(string_literal<N> val) { return val.size(); } + static constexpr std::size_t size(char val) { return 1; } + static constexpr std::size_t size(intmax_t val) { + std::size_t len = 1; + while (val /= 10) + len++; + return len; + } + + template <std::size_t N> static constexpr char *append(char *ptr, string_literal<N> val) { + std::copy_n(val.value, N, ptr); + return ptr + N; + } + + static constexpr char *append(char *ptr, char val) { + *ptr++ = val; + return ptr; + } + + static constexpr char *append(char *ptr, intmax_t val) { + char *tmp = ptr += size(val); + do { + *--tmp = '0' + (val % 10); + } while (val /= 10); + return ptr; + } + + static constexpr const auto value = []() { + constexpr std::size_t len = (size(Args) + ... + 2); + std::array<char, len + 1> arr{CTRL::ESC, '[', 0}; + auto map = [ptr = arr.data() + 2](auto const &s) mutable { ptr = append(ptr, s); }; + (map(Args), ...); arr[len] = 0; return arr; }(); - static constexpr std::string_view value{arr.data(), arr.size() - 1}; }; -template <std::intmax_t N> class to_string_t { - public: - constexpr operator const auto &() const { return value; } - - private: - static constexpr auto buf = []() { - constexpr const auto len = []() { - std::size_t len = 1; - for (auto n = N; n; len++, n /= 10) - ; - return len; - }(); +template <auto... Strs> static constexpr auto escape = escape_t<Strs...>::value.data(); - std::array<char, len> arr{}; +} // namespace details - auto ptr = arr.data() + len; - *--ptr = '\0'; +// Tamplate parameter constraints - arr[0] = 0; - for (auto n = N; n; n /= 10) - *--ptr = '0' + n % 10; +template <intmax_t n> +concept limit_256 = n >= 0 && n < 256; - return arr; - }(); - static constexpr std::string_view value{buf.data(), buf.size() - 1}; -}; +template <intmax_t n> +concept limit_pos = n >= 0; -template <char C> class to_string_c { - public: - constexpr operator const auto &() const { return value; } - - private: - static constexpr auto c = C; - static constexpr std::string_view value{&c, 1}; -}; +template <intmax_t n> +concept limit_4 = n >= 0 && n <= 4; -static constexpr const std::string_view buffer = "?1049"; -static constexpr const std::string_view cursor = "?25"; -static constexpr const std::string_view esc = "\033["; -static constexpr const std::string_view sep = ";"; +// Move cursor up/down/frwd/back +template <intmax_t n> + requires limit_pos<n> +static constexpr auto CURSOR_UP = details::escape<n, 'A'>; -template <std::string_view const &...Strs> static constexpr auto concatenate = join<Strs...>::value; -template <std::string_view const &...Strs> static constexpr auto escape = join<esc, Strs...>::value; +template <intmax_t n> + requires limit_pos<n> +static constexpr auto CURSOR_DOWN = details::escape<n, 'B'>; -template <std::intmax_t N> constexpr to_string_t<N> to_string; -template <char C> constexpr to_string_c<C> to_stringc; -template <std::intmax_t N> static constexpr auto to_stringp = join<to_string<N>, sep>::value; +template <intmax_t n> + requires limit_pos<n> +static constexpr auto CURSOR_FRWD = details::escape<n, 'C'>; -// Move cursor up/down/frwd/back -template <int n, class CharT, class Traits> -static constexpr auto &CURSOR_UP(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'A'>>; -} - -template <int n, class CharT, class Traits> -static constexpr auto &CURSOR_DOWN(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'B'>>; -} - -template <int n, class CharT, class Traits> -static constexpr auto &CURSOR_FRWD(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'C'>>; -} - -template <int n, class CharT, class Traits> -static constexpr auto &CURSOR_BACK(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'D'>>; -} +template <intmax_t n> + requires limit_pos<n> +static constexpr auto CURSOR_BACK = details::escape<n, 'D'>; // Move cursor to the next/prev line -template <int n, class CharT, class Traits> -static constexpr auto &CURSOR_LINE_NEXT(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'E'>>; -} - -template <int n, class CharT, class Traits> -static constexpr auto &CURSOR_LINE_PREV(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'F'>>; -} +template <intmax_t n> + requires limit_pos<n> +static constexpr auto CURSOR_LINE_NEXT = details::escape<n, 'E'>; + +template <intmax_t n> + requires limit_pos<n> +static constexpr auto CURSOR_LINE_PREV = details::escape<n, 'F'>; // Set cursor to specific column -template <int n, class CharT, class Traits> -static constexpr auto &CURSOR_COLUMN(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'G'>>; -} +template <intmax_t n> + requires limit_pos<n> +static constexpr auto CURSOR_COLUMN = details::escape<n, 'G'>; // Erase functions -template <int n, class CharT, class Traits> -static constexpr auto &ERASE_DISPLAY(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0 && n <= 4, "invalid argument"); - return bos << escape<to_string<n>, to_stringc<'J'>>; -} - -template <int n, class CharT, class Traits> -static constexpr auto &ERASE_LINE(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0 && n <= 4, "invalid argument"); - return bos << escape<to_string<n>, to_stringc<'K'>>; -} +template <intmax_t n> + requires limit_4<n> +static constexpr auto ERASE_DISPLAY = details::escape<n, 'J'>; + +template <intmax_t n> + requires limit_4<n> +static constexpr auto ERASE_LINE = details::escape<n, 'K'>; // Scroll up/down -template <int n, class CharT, class Traits> -static constexpr auto &SCROLL_UP(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'S'>>; -} - -template <int n, class CharT, class Traits> -static constexpr auto &SCROLL_DOWN(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0, "cant' use negative numbers"); - return bos << escape<to_string<n>, to_stringc<'T'>>; -} +template <intmax_t n> + requires limit_pos<n> +static constexpr auto SCROLL_UP = details::escape<n, 'S'>; + +template <intmax_t n> + requires limit_pos<n> +static constexpr auto SCROLL_DOWN = details::escape<n, 'T'>; // Set cursor to a specific position -template <int n, int m, class CharT, class Traits> -static constexpr auto &CURSOR_POSITION(std::basic_ostream<CharT, Traits> &bos) { - static_assert(n >= 0 && m >= 0, "cant' use negative numbers"); - return bos << escape<to_stringp<n>, to_string<m>, to_stringc<'H'>>; -} +template <intmax_t n, intmax_t m> + requires limit_pos<n> && limit_pos<m> +static constexpr auto CURSOR_POSITION = details::escape<n, ';', m, 'H'>; // palet colors -template <COLOR color, class CharT, class Traits> -static constexpr auto &FOREGROUND(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<to_string<static_cast<uint>(color) + 30>, to_stringc<'m'>>; -} - -template <COLOR color, class CharT, class Traits> -static constexpr auto &BACKGROUND(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<to_string<static_cast<uint>(color) + 40>, to_stringc<'m'>>; -} +template <COLOR color> static constexpr auto FOREGROUND = details::escape<(intmax_t)color + 30, 'm'>; +template <COLOR color> static constexpr auto BACKGROUND = details::escape<(intmax_t)color + 40, 'm'>; // 256-color palette -template <int idx, class CharT, class Traits> -static constexpr auto &FOREGROUND(std::basic_ostream<CharT, Traits> &bos) { - static_assert(idx >= 0 && idx < 256, "value must be between 0 and 255"); - return bos << escape<to_stringp<38>, to_stringp<5>, to_string<idx>, to_stringc<'m'>>; -} - -template <int idx, class CharT, class Traits> -static constexpr auto &BACKGROUND(std::basic_ostream<CharT, Traits> &bos) { - static_assert(idx >= 0 && idx < 256, "value must be between 0 and 255"); - return bos << escape<to_stringp<48>, to_stringp<5>, to_string<idx>, to_stringc<'m'>>; -} +template <intmax_t idx> + requires limit_256<idx> +static constexpr auto FOREGROUND_256 = details::escape<intmax_t(38), ';', intmax_t(5), ';', idx, 'm'>; + +template <intmax_t idx> + requires limit_256<idx> +static constexpr auto BACKGROUND_256 = details::escape<intmax_t(48), ';', intmax_t(5), ';', idx, 'm'>; // RGB colors -template <int R, int G, int B, class CharT, class Traits> -static constexpr auto &FOREGROUND(std::basic_ostream<CharT, Traits> &bos) { - static_assert(R >= 0 && R < 256, "R value must be between 0 and 255"); - static_assert(G >= 0 && G < 256, "G value must be between 0 and 255"); - static_assert(B >= 0 && B < 256, "B value must be between 0 and 255"); - return bos << escape<to_stringp<38>, to_stringp<5>, to_stringp<R>, to_stringp<G>, to_string<B>, - to_stringc<'m'>>; -} - -template <int R, int G, int B, class CharT, class Traits> -static constexpr auto &BACKGROUND(std::basic_ostream<CharT, Traits> &bos) { - static_assert(R >= 0 && R < 256, "R value must be between 0 and 255"); - static_assert(G >= 0 && G < 256, "G value must be between 0 and 255"); - static_assert(B >= 0 && B < 256, "B value must be between 0 and 255"); - return bos << escape<to_stringp<48>, to_stringp<5>, to_stringp<R>, to_stringp<G>, to_string<B>, - to_stringc<'m'>>; -} +template <intmax_t R, intmax_t G, intmax_t B> + requires limit_256<R> && limit_256<G> && limit_256<B> +static constexpr auto FOREGROUND_RGB = + details::escape<intmax_t(38), ';', intmax_t(5), ';', R, ';', G, ';', B, 'm'>; + +template <intmax_t R, intmax_t G, intmax_t B> + requires limit_256<R> && limit_256<G> && limit_256<B> +static constexpr auto BACKGROUND_RGB = + details::escape<intmax_t(48), ';', intmax_t(5), ';', R, ';', G, ';', B, 'm'>; // Set/reset text decorators -template <DECOR decor, class CharT, class Traits> -static constexpr auto &DECOR_SET(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<to_string<static_cast<uint>(decor)>, to_stringc<'m'>>; -} - -template <DECOR decor, class CharT, class Traits> -static constexpr auto &DECOR_RESET(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<to_string<static_cast<uint>(decor) + 20>, to_stringc<'m'>>; -} - -// Savle/load cursor position -template <class CharT, class Traits> -static constexpr auto &CURSOR_SAVE(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<to_stringc<'s'>>; -} - -template <class CharT, class Traits> -static constexpr auto &CURSOR_LOAD(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<to_stringc<'u'>>; -} +template <DECOR decor> static constexpr const char *DECOR_SET = details::escape<(intmax_t)decor, 'm'>; +template <DECOR decor> static constexpr const char *DECOR_RESET = details::escape<(intmax_t)decor + 20, 'm'>; -// Show/hide cursor -template <class CharT, class Traits> -static constexpr auto &CURSOR_SHOW(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<cursor, to_stringc<'h'>>; -} +// Savle/load cursor position; +static constexpr const char *CURSOR_SAVE = details::escape<'s'>; +static constexpr const char *CURSOR_LOAD = details::escape<'u'>; -template <class CharT, class Traits> -static constexpr auto &CURSOR_HIDE(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<cursor, to_stringc<'l'>>; -} +// Show/hide cursor +static constexpr const char *CURSOR_SHOW = details::escape<details::string_literal("?25h")>; +static constexpr const char *CURSOR_HIDE = details::escape<details::string_literal("?25l")>; // Show/hide alternate buffer -template <class CharT, class Traits> -static constexpr auto &ABUF_SHOW(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<buffer, to_stringc<'h'>>; -} - -template <class CharT, class Traits> -static constexpr auto &ABUF_HIDE(std::basic_ostream<CharT, Traits> &bos) { - return bos << escape<buffer, to_stringc<'l'>>; -} +static constexpr const char *ABUF_SHOW = details::escape<details::string_literal("?1049h")>; +static constexpr const char *ABUF_HIDE = details::escape<details::string_literal("?1049l")>; } // namespace ALEC