diff --git a/bin/examples/concepts/main.cpp b/bin/examples/concepts/main.cpp index 0d8d2b3..ab66cc0 100644 --- a/bin/examples/concepts/main.cpp +++ b/bin/examples/concepts/main.cpp @@ -66,13 +66,6 @@ void example_is_sequence_container(const T& container) hack::log()("Sequence container with ", container.size(), " elements"); } -template -void example_is_random_access_container(T& container) -{ - if (!container.empty()) - hack::log()("First element: ", container[0]); -} - template void example_is_container_adapter(T& adapter) { @@ -139,15 +132,6 @@ void check_support(const T& value) hack::log()("Type is supported"); } -template -void example_has_key_value_semantics(T& container) -{ - if constexpr (hack::concepts::has_mapped_type) - hack::log()("Key-value container, sample access demonstrated"); - else - hack::log()("Set-like container"); -} - template void example_is_contiguous_container(const T& container) { @@ -210,9 +194,7 @@ auto main(int argc, char *argv[]) -> int example_is_string(str); example_is_sequence_container(vec); example_is_sequence_container(list); - example_is_random_access_container(vec); example_is_container_adapter(stack); - example_is_associative_container(set); example_is_unordered_associative_container(umap); example_is_tuple_like(tuple); example_is_fixed_array(fixed_array); @@ -222,7 +204,6 @@ auto main(int argc, char *argv[]) -> int example_is_sized(vec); check_support(a); check_support(custom); - example_has_key_value_semantics(map); example_is_contiguous_container(vec); example_can_push_front(list, 0); example_can_push_back(vec, 99); diff --git a/bin/examples/logger/main.cpp b/bin/examples/logger/main.cpp index fec8100..64cfb3a 100644 --- a/bin/examples/logger/main.cpp +++ b/bin/examples/logger/main.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include "hack/logger/logger.hpp" @@ -9,15 +11,48 @@ auto main(int argc, char *argv[]) -> int double d = 2.0; float f = 3.f; std::vector vs = { "a", "b", "c" }; + std::list l = { 1, 2, 3 }; + std::deque df = { 1.1f, 2.1f, 3.1f }; + std::forward_list fl = { 1, 2, 3 }; std::map mi = { { 1, 1 }, { 2, 2 }, { 3, 3 } }; + std::multimap mmi = { { 1, 1 }, { 1, 1 }, { 2, 2 }, { 3, 3 } }; + std::unordered_map umi = { { 1, 1 }, { 1, 1 }, { 2, 2 }, { 3, 3 } }; + std::tuple tp = { 1, "asdf", false }; + std::stack sti; + sti.push(1); + sti.push(2); + sti.push(3); + std::set si = { 1, 2, 3 }; + std::unordered_set usi = { 1, 1, 1 }; hack::log().set_devider(", "); hack::log().no_func(); - hack::log()(1, 2, 3.1f, 4.3, "asdf", "qwer", "xzcv"); + hack::log()(1, i, 3.1f, f, 4.3, d, "asdf"); hack::log().set_devider(" = "); - hack::log()(1, 2, 3.1f, 4.3, "asdf", "qwer", "xzcv"); + hack::log()(1, i, 3.1f, f, 4.3, d, "asdf"); hack::log().reset(); - hack::log()(1, 2, 3.1f, 4.3, "asdf", "qwer", "xzcv"); + hack::log()(1, i, 3.1f, f, 4.3, d, "asdf"); + hack::log().set_devider(", "); + hack::log().no_func(); + hack::log().no_file(); + hack::log().no_row(); + hack::log()(vs); + hack::log()(l); + hack::log()(df); + hack::log()(fl); + hack::log().reset(); + hack::log()(mi); + hack::log()(mmi); + hack::log()(umi); + hack::log()(tp); + hack::log()(true); + hack::log().bool_as_number(); + hack::log()(true); + hack::log()(si); + hack::log()(usi); + hack::log().reset(); + hack::log().set_devider(", "); + hack::log()(sti, 123, true); return 0; } diff --git a/src/hack/concepts/concepts.hpp b/src/hack/concepts/concepts.hpp index f68d484..b48ef43 100755 --- a/src/hack/concepts/concepts.hpp +++ b/src/hack/concepts/concepts.hpp @@ -3,8 +3,6 @@ #include #include #include -#include -#include #include #include #include @@ -91,16 +89,6 @@ namespace hack::concepts std::same_as> || std::same_as>); - // Контейнеры с произвольным доступом - // @brief Проверяет, поддерживает ли контейнер произвольный доступ - // @details Обнаруживает контейнеры с оператором [] для быстрого доступа: - // - std::vector - O(1) доступ по индексу - // - std::deque - O(1) доступ по индексу - // - std::array - фиксированный массив с быстрым доступом - // Важно для алгоритмов, требующих частого доступа к элементам по индексу - template - concept is_random_access_container = is_sequence_container && requires(T t, std::size_t idx) { t[idx]; }; - // Адаптеры контейнеров // @brief Проверяет, является ли тип адаптером контейнера // @details Обнаруживает типы-обертки над другими контейнерами: @@ -118,32 +106,24 @@ namespace hack::concepts // @details Обнаруживает контейнеры, хранящие элементы в отсортированном порядке: // - std::map - словарь с уникальными ключами // - std::multimap - словарь с возможностью дубликатов ключей - // - std::set - множество уникальных элементов - // - std::multiset - множество с возможностью дубликатов // Использует key_compare для упорядочивания (обычно std::less) template concept is_associative_container = has_iterator && has_key_type && (has_mapped_type || !has_mapped_type) && // Для map и set (std::same_as> || - std::same_as> || - std::same_as> || - std::same_as>); + std::same_as>); // Неупорядоченные ассоциативные контейнеры // @brief Проверяет, является ли тип неупорядоченным ассоциативным контейнером // @details Обнаруживает контейнеры, использующие хеширование: // - std::unordered_map - хеш-таблица с уникальными ключами // - std::unordered_multimap - хеш-таблица с дубликатами ключей - // - std::unordered_set - хеш-множество уникальных элементов - // - std::unordered_multiset - хеш-множество с дубликатами // Используют хеш-функции и сравнение на равенство вместо упорядочивания template concept is_unordered_associative_container = has_iterator && has_key_type && (has_mapped_type || !has_mapped_type) && (std::same_as> || - std::same_as> || - std::same_as> || - std::same_as>); + std::same_as>); // Кортежи и пары // @brief Проверяет, является ли тип кортежоподобным @@ -225,17 +205,6 @@ namespace hack::concepts is_tuple_like || std::is_pointer_v); - // Вспомогательные концепты для метапрограммирования - // @brief Проверяет, имеет ли тип семантику ключ-значение - // @details Обнаруживает контейнеры, которые поддерживают доступ по ключу: - // - map и unordered_map (operator[]) - // - set и unordered_set (хотя у set нет разделения на key/value) - // Полезно для алгоритмов работы со словарями и ассоциативными массивами - template - concept has_key_value_semantics = is_associative_container || - is_unordered_associative_container || - requires(T t, typename T::key_type key) { t[key]; }; - // @brief Проверяет, является ли тип контейнером с непрерывной памятью // @details Обнаруживает контейнеры, элементы которых хранятся в непрерывной памяти: // - std::vector - динамический массив @@ -268,15 +237,87 @@ namespace hack::concepts template concept can_find = requires(Container c, Key&& key) { c.find(std::forward(key)); }; + // @brief Проверяет, является ли тип bool + template + concept is_bool = std::is_same_v, bool>; // @brief Проверяет, является ли данное числом // @details Проверяет на основные операции template concept is_number = requires(T value) { - requires std::is_arithmetic_v; // Включает все арифметические типы + requires std::is_arithmetic_v && !is_bool; // Включает все арифметические типы value + value; // Проверяет арифметические операции value - value; value * value; value / value; }; + + template + concept is_set_like = requires { + // Общие требования для всех set-like контейнеров + typename T::key_type; + typename T::value_type; + requires std::is_same_v; // key == value + } && requires(T t, typename T::key_type key) { + // Методы, характерные для set-like контейнеров + { t.begin() } -> std::same_as; + { t.end() } -> std::same_as; + { t.find(key) } -> std::same_as; + { t.size() } -> std::convertible_to; + { t.empty() } -> std::same_as; + }; + + // Базовый концепт для stack-like типов + template + concept is_stack = requires(T s) { + typename T::value_type; + typename T::container_type; + { s.top() } -> std::same_as; + { s.pop() } -> std::same_as; + { s.push(std::declval()) } -> std::same_as; + { s.empty() } -> std::same_as; + { s.size() } -> std::convertible_to; + }; } + +// namespace hack::concepts +// { +// template +// concept is_map = std::same_as> || +// std::same_as>; +// +// template +// concept is_tuple = requires (T t) { std::tuple_cat(t, std::make_tuple(1, "tuple")); }; +// +// template +// concept is_set = std::same_as>; +// +// template +// concept is_unordered_set = std::same_as>; +// +// template +// concept is_forward_list = std::same_as>; +// +// template +// concept is_string = std::is_convertible_v; +// +// template +// concept is_sequence_container = std::same_as> || std::same_as> || (std::is_array_v && N > 0); +// +// template +// concept is_associative_container = is_map || is_tuple || is_set || is_unordered_set; +// +// +// template +// concept not_defined = !std::enable_if_t || +// is_sequence_container || +// is_map || +// is_tuple || +// is_set || +// is_unordered_set || +// is_forward_list || +// std::is_array() || +// is_string), bool>() == true; +// } + + diff --git a/src/hack/logger/logger.OLD.hpp b/src/hack/logger/logger.OLD.hpp old mode 100755 new mode 100644 index 5e95695..aa10697 --- a/src/hack/logger/logger.OLD.hpp +++ b/src/hack/logger/logger.OLD.hpp @@ -10,10 +10,6 @@ #include "hack/patterns/ring_buffer.hpp" -// HERE -// и нужно сделать реализацию где выводлится все в одной линии но при помощи цикла -// типа такого: -// for (auto i : range) hack::log(hack::log::line)(i); namespace hack { class log @@ -87,8 +83,6 @@ namespace hack std::cout << " }" << (count != 0 ? devider : ""); } - // HERE - // реализовать это с учетом новых концептов template static void print_t(const T& data) { @@ -112,7 +106,7 @@ namespace hack std::copy(data.cbegin(), data.cend(), iterators::sequence_ostream_iterator(std::distance(data.cbegin(), data.cend()), std::cout)); std::cout << " }" << (count != 0 ? devider : ""); } - + template static void print_t(const T& data) { @@ -127,12 +121,6 @@ namespace hack print_t(data, std::make_index_sequence::value>{}); } - template - static void print_t(const T& data) - { - std::cout << data << (count != 0 ? devider : ""); - } - template static void print_t(const T& data, std::index_sequence) { @@ -141,6 +129,12 @@ namespace hack std::cout << " }" << (count != 0 ? devider : ""); } + template + static void print_t(const T& data) + { + std::cout << data << (count != 0 ? devider : ""); + } + template static void print_t(const hack::patterns::ring_buffer& rb) { @@ -212,3 +206,4 @@ namespace hack } }; } + diff --git a/src/hack/logger/logger.hpp b/src/hack/logger/logger.hpp index c4b30a0..0412aa2 100755 --- a/src/hack/logger/logger.hpp +++ b/src/hack/logger/logger.hpp @@ -6,6 +6,9 @@ #include "hack/utils/color.hpp" #include "hack/patterns/singleton.hpp" #include "hack/concepts/concepts.hpp" +#include "hack/iterators/sequence_ostream_iterator.hpp" +#include "hack/iterators/associative_ostream_iterator.hpp" +#include #include #include @@ -23,12 +26,19 @@ namespace hack void set_location(std::source_location location) { m_location = location; } void set_devider(std::string devider) { m_devider = devider; } void no_func() { m_no_func = true; }; + void no_file() { m_no_file = true; } + void no_row() { m_no_row = true; } + void bool_as_number() { m_bool_as_number = true; } void reset() { m_no_func = m_base_config.m_no_func; m_devider = m_base_config.m_devider; + m_no_file = m_base_config.m_no_file; + m_no_row = m_base_config.m_no_row; + m_bool_as_number = m_base_config.m_bool_as_number; } + public: template void operator() (const Args&... args) { @@ -38,23 +48,31 @@ namespace hack } private: + // настройки по умолчанию struct config { - bool m_no_func = false; - std::string m_devider = " "; + bool m_no_func = false; // показывать/не показывать название функции в выоде логов + bool m_no_file = false; // показывать/не показывать название файла/пути в выоде логов + bool m_no_row = false; // показывать/не показывать номер строки в выоде логов + bool m_bool_as_number = false; // показывет bool как число или как текст (0, false); + std::string m_devider = " "; // разделитель по умолчанию } m_base_config; private: void prepare() { std::stringstream ss; - ss << utils::color::bold << utils::color::green << "[ok]" << utils::color::reset << utils::color::green - << m_location.file_name() << ":" << utils::color::reset; + ss << utils::color::bold << utils::color::green << "[ok] " << utils::color::reset; + + if (!m_no_file) + ss << utils::color::green << m_location.file_name() << ":" << utils::color::reset; if (!m_no_func) ss << utils::color::italic << utils::color::yellow << m_location.function_name() << utils::color::reset; - ss << utils::color::bold << utils::color::blue << "[" << m_location.line() << "]" << utils::color::reset << ": "; + if (!m_no_row) + ss << utils::color::bold << utils::color::blue << "[" << m_location.line() << "] " << utils::color::reset; + std::cout << ss.str(); } @@ -62,33 +80,128 @@ namespace hack void print_impl(const T& data, const Args&... args) { --m_count; - pring_first(data); + print_t(data); print_impl(args...); } void print_impl() { std::cout << std::endl; } - template - requires concepts::is_string - void pring_first(const T& data) + // для строк + template + void print_t(const T& data) { std::cout << data << (m_count != 0 ? m_devider : ""); } - template - requires concepts::is_number - void pring_first(const T& data) + // для всех чисел + template + void print_t(const T& data) { std::cout << data << (m_count != 0 ? m_devider : ""); } + + // для bool + template + void print_t(const T& data) + { + if (!m_bool_as_number) + std::cout << (data ? "true" : "false") << (m_count != 0 ? m_devider : ""); + else + std::cout << data << (m_count != 0 ? m_devider : ""); + } + + // для std::vector, std::list, std::forward_list, std::deque + template + void print_t(const T& data) + { + print_t(data); + } + + // для std::set и подобные + template + void print_t(const T& data) + { + print_t(data); + } + + // для std::map, std::multimap + template + void print_t(const T& data) + { + print_t(data); + } + + // для std::unordered_map + template + void print_t(const T& data) + { + print_t(data); + } + + // Вспомогательнгая функция для контейнеров выше + template class Iterator> + void print_t(const T& data) + { + std::cout << "{ "; + + // Вычисляем размер для контейнеров, которые не имеют метода size() + std::size_t size = 0; + if constexpr (requires { data.size(); }) + size = data.size(); + else + size = std::distance(data.cbegin(), data.cend()); + + std::copy(data.cbegin(), data.cend(), Iterator(size, std::cout)); + std::cout << " }" << (m_count != 0 ? m_devider : ""); + } + + // Основная функция для tuple + template + void print_t(const T& data) + { + std::cout << "{ "; + print_t(data, std::make_index_sequence>{}); + std::cout << " }" << (m_count != 0 ? m_devider : ""); + } + + template + void print_t(const T& data, std::index_sequence) + { + ((std::cout << std::get(data) << (idx != std::tuple_size::value - 1 ? m_devider : "")), ...); + } + + // Для stack - выводим содержимое (но stack нельзя итерировать!) + template + void print_t(const T& stack) + { + T temp = stack; + + std::cout << "{ "; + + bool first = true; + while (!temp.empty()) + { + if (!first) + std::cout << m_devider; + first = false; + std::cout << temp.top(); + temp.pop(); + } + + std::cout << " }" << (m_count != 0 ? m_devider : ""); + } private: std::source_location m_location; std::size_t m_count = 0; std::string m_devider = m_base_config.m_devider; bool m_no_func = m_base_config.m_no_func; + bool m_no_file = m_base_config.m_no_file; + bool m_no_row = m_base_config.m_no_row; + bool m_bool_as_number = m_base_config.m_bool_as_number; }; + // основная функция вызова логера inline logger& log(std::source_location location = std::source_location::current()) { logger::instance().set_location(location);