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:
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