Compare commits
10 Commits
057dc595ac
...
9c867b0e01
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c867b0e01 | |||
| c716126382 | |||
| fc247a5b83 | |||
| 1604577286 | |||
| a76aadc068 | |||
| dd6606eab9 | |||
| bfebb74fb7 | |||
| afcaf475fd | |||
| 85078ee5a5 | |||
| 626b5a6cd9 |
@@ -14,10 +14,10 @@
|
||||
+ patterns - набор различных паттернов проектирования
|
||||
+ security - что-то типа защиты от чего-то
|
||||
+ utils - вспомогательные решения для библиотеки
|
||||
+ logger - реализация логирования
|
||||
|
||||
- logger - реализация логирования
|
||||
- exception - универсальный класс обработки ошибок
|
||||
- iterators - набор разнообразных реализаций итераторов
|
||||
- exception - универсальный класс обработки ошибок
|
||||
|
||||
+ - задукоментированно с коментариями и примерами
|
||||
- - доки в раборте, нужно делать и разбирать
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <set>
|
||||
#include "hack/concepts/concepts.hpp"
|
||||
#include "hack/logger/logger.hpp"
|
||||
|
||||
@@ -108,12 +109,6 @@ void example_is_any_container(const T& container)
|
||||
hack::log()("Any container with ", container.size(), " elements");
|
||||
}
|
||||
|
||||
template<hack::concepts::is_iterable T>
|
||||
void example_is_iterable(const T& iterable)
|
||||
{
|
||||
hack::log()("Iterable object");
|
||||
}
|
||||
|
||||
template<hack::concepts::is_sized T>
|
||||
void example_is_sized(const T& sized)
|
||||
{
|
||||
@@ -132,12 +127,6 @@ void check_support(const T& value)
|
||||
hack::log()("Type is supported");
|
||||
}
|
||||
|
||||
template<hack::concepts::is_contiguous_container T>
|
||||
void example_is_contiguous_container(const T& container)
|
||||
{
|
||||
hack::log()("Contiguous memory container");
|
||||
}
|
||||
|
||||
template<typename Container, typename Value>
|
||||
requires hack::concepts::can_push_front<Container, Value>
|
||||
void example_can_push_front(Container& container, Value&& value)
|
||||
@@ -200,11 +189,9 @@ auto main(int argc, char *argv[]) -> int
|
||||
example_is_fixed_array(fixed_array);
|
||||
example_is_std_array(std_array);
|
||||
example_is_any_container(vec);
|
||||
example_is_iterable(vec);
|
||||
example_is_sized(vec);
|
||||
check_support(a);
|
||||
check_support(custom);
|
||||
example_is_contiguous_container(vec);
|
||||
example_can_push_front(list, 0);
|
||||
example_can_push_back(vec, 99);
|
||||
example_can_find(set, 3);
|
||||
|
||||
34
bin/examples/exception/main.cpp
Normal file
34
bin/examples/exception/main.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <vector>
|
||||
#include "hack/exception/exception.hpp"
|
||||
#include "hack/logger/logger.hpp"
|
||||
|
||||
auto main(int argc, char *argv[]) -> int
|
||||
{
|
||||
try
|
||||
{
|
||||
hack::exception e;
|
||||
e.title("Test exception");
|
||||
e.description("Super description for test exception");
|
||||
e.service("example");
|
||||
std::vector<int> vi = { 1, 2, 3 };
|
||||
e.set("vector int", vi);
|
||||
e.set("int", 1);
|
||||
e.set("float", 1.13f);
|
||||
e.set("double", 1.23);
|
||||
e.set("file", std::filesystem::path("/tes/path/to_file.txt"));
|
||||
|
||||
throw e;
|
||||
}
|
||||
catch(hack::exception& e)
|
||||
{
|
||||
hack::log().no_info();
|
||||
hack::error()(e);
|
||||
hack::log()(e);
|
||||
hack::log().reset();
|
||||
}
|
||||
|
||||
hack::log().reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include "hack/logger/logger.hpp"
|
||||
#include "hack/patterns/ring_buffer.hpp"
|
||||
#include "hack/utils/timestamp.hpp"
|
||||
|
||||
auto main(int argc, char *argv[]) -> int
|
||||
{
|
||||
@@ -24,6 +26,9 @@ auto main(int argc, char *argv[]) -> int
|
||||
sti.push(3);
|
||||
std::set<int> si = { 1, 2, 3 };
|
||||
std::unordered_set<int> usi = { 1, 1, 1 };
|
||||
hack::patterns::ring_buffer<int> rb;
|
||||
rb.create(10);
|
||||
for (int i = 0; i < 10; ++i) rb.put(i);
|
||||
|
||||
hack::log().set_devider(", ");
|
||||
hack::log().no_func();
|
||||
@@ -44,7 +49,7 @@ auto main(int argc, char *argv[]) -> int
|
||||
hack::log()(mi);
|
||||
hack::log()(mmi);
|
||||
hack::log()(umi);
|
||||
hack::log()(tp);
|
||||
hack::log()("tuple:", tp);
|
||||
hack::log()(true);
|
||||
hack::log().bool_as_number();
|
||||
hack::log()(true);
|
||||
@@ -53,7 +58,19 @@ auto main(int argc, char *argv[]) -> int
|
||||
hack::log().reset();
|
||||
hack::log().set_devider(", ");
|
||||
hack::log()(sti, 123, true);
|
||||
hack::log().reset();
|
||||
hack::log()("log", 123, sti, false, 1.8f, vs);
|
||||
|
||||
hack::warn()("warn");
|
||||
hack::error()("error");
|
||||
|
||||
hack::log()(rb);
|
||||
|
||||
hack::log()(str.c_str());
|
||||
hack::log()(str);
|
||||
|
||||
std::filesystem::path p { "/test/file/path.txt" };
|
||||
hack::log()(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "hack/patterns/ring_buffer.hpp"
|
||||
#include "hack/patterns/identificator.hpp"
|
||||
#include "hack/logger/logger.hpp"
|
||||
#include "hack/patterns/ring_buffer.hpp"
|
||||
|
||||
auto main(int argc, char *argv[]) -> int
|
||||
{
|
||||
@@ -8,21 +8,37 @@ auto main(int argc, char *argv[]) -> int
|
||||
hack::patterns::ring_buffer<int> rb;
|
||||
rb.create(10);
|
||||
for (int i = 0; i < 10; ++i) rb.put(i);
|
||||
hack::log()(rb);
|
||||
hack::log()(rb.size());
|
||||
hack::log()("rb =", rb);
|
||||
hack::log()("size =", rb.size());
|
||||
rb.skip(3);
|
||||
hack::log()(rb.pop().value());
|
||||
hack::log()("size =", rb.size());
|
||||
hack::log()(rb.get().value());
|
||||
hack::log()(rb.size());
|
||||
hack::log()("size =", rb.size());
|
||||
std::vector<int> v(3);
|
||||
rb.get(v, 3);
|
||||
hack::log()(v);
|
||||
hack::log()("rb =", rb);
|
||||
hack::log()("v =", v);
|
||||
rb.pop(v, 5);
|
||||
hack::log()("v =", v);
|
||||
hack::log()(rb.pop().value());
|
||||
hack::log()("rb =", rb);
|
||||
hack::log()("rb:", rb.pop().has_value(), " (пусто...)");
|
||||
rb.put(1);
|
||||
hack::log()("rb =", rb);
|
||||
hack::log()(rb.pop().value());
|
||||
hack::log()("rb =", rb);
|
||||
hack::log()("rb:", rb.pop().has_value(), " (пусто...)");
|
||||
rb.put(v);
|
||||
hack::log()("rb =", rb);
|
||||
|
||||
// identificator
|
||||
struct id_struct : public hack::patterns::identificator<> {} aa;
|
||||
id_struct bb;
|
||||
id_struct cc;
|
||||
id_struct dd;
|
||||
hack::log()(aa.m_id, bb.m_id, cc.m_id, dd.m_id);
|
||||
hack::log()("identificator:");
|
||||
hack::log()(aa.get_id(), bb.get_id(), cc.get_id(), dd.get_id());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ executable(
|
||||
# 'examples/math/main.cpp',
|
||||
# 'examples/range/main.cpp',
|
||||
# 'examples/patterns/main.cpp',
|
||||
'examples/logger/main.cpp',
|
||||
'examples/logger/main.cpp',
|
||||
# 'examples/exception/main.cpp',
|
||||
dependencies : deps,
|
||||
cpp_args: args,
|
||||
include_directories : inc
|
||||
|
||||
@@ -5,7 +5,7 @@ project(
|
||||
default_options : [
|
||||
'warning_level=1',
|
||||
'optimization=3',
|
||||
'cpp_std=c++20',
|
||||
'cpp_std=c++20'
|
||||
])
|
||||
|
||||
add_project_arguments (
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <forward_list>
|
||||
@@ -89,6 +90,7 @@ namespace hack::concepts
|
||||
std::same_as<T, std::deque<typename T::value_type, typename T::allocator_type>> ||
|
||||
std::same_as<T, std::forward_list<typename T::value_type, typename T::allocator_type>>);
|
||||
|
||||
|
||||
// Адаптеры контейнеров
|
||||
// @brief Проверяет, является ли тип адаптером контейнера
|
||||
// @details Обнаруживает типы-обертки над другими контейнерами:
|
||||
@@ -156,88 +158,7 @@ namespace hack::concepts
|
||||
arr[0];
|
||||
};
|
||||
|
||||
// Универсальные концепты для категоризации
|
||||
// @brief Проверяет, является ли тип любым контейнером
|
||||
// @details Всеобъемлющий концепт для обнаружения контейнеров любого типа:
|
||||
// - Последовательные контейнеры
|
||||
// - Ассоциативные контейнеры
|
||||
// - Адаптеры контейнеров
|
||||
// - Массивы (C-style и std::array)
|
||||
// Основной концепт для обобщенных алгоритмов работы с контейнерами
|
||||
template<typename T>
|
||||
concept is_any_container = is_sequence_container<T> ||
|
||||
is_associative_container<T> ||
|
||||
is_unordered_associative_container<T> ||
|
||||
is_container_adapter<T> ||
|
||||
is_fixed_array<T> ||
|
||||
is_std_array<T>;
|
||||
|
||||
// @brief Проверяет, является ли тип итерируемым
|
||||
// @details Обнаруживает любые типы, по которым можно итерироваться:
|
||||
// - Контейнеры STL с begin()/end()
|
||||
// - Массивы (работают с std::begin/std::end)
|
||||
// - Кортежи (хотя итерирование по ним особое)
|
||||
// Самый общий концепт для range-based for и алгоритмов
|
||||
template<typename T>
|
||||
concept is_iterable = has_iterator<T> || is_fixed_array<T> || is_tuple_like<T>;
|
||||
|
||||
// @brief Проверяет, имеет ли тип размер
|
||||
// @details Обнаруживает типы, у которых можно узнать размер:
|
||||
// - Контейнеры с методом size()
|
||||
// - Массивы с известным размером
|
||||
// - Кортежи с известным количеством элементов
|
||||
// Важно для алгоритмов, требующих предварительного знания размера
|
||||
template<typename T>
|
||||
concept is_sized = has_size<T> || is_fixed_array<T> || is_tuple_like<T>;
|
||||
|
||||
// Концепт для "неподдерживаемых" типов
|
||||
// @brief Проверяет, является ли тип неподдерживаемым
|
||||
// @details Обнаруживает типы, которые не входят в известные категории:
|
||||
// - Пользовательские типы без ожидаемого интерфейса
|
||||
// - Специфичные типы из сторонних библиотек
|
||||
// - Типы, для которых нет специализированной обработки
|
||||
// Используется для static_assert и генерации понятных ошибок
|
||||
template<typename T>
|
||||
concept not_supported = !(std::integral<T> ||
|
||||
std::floating_point<T> ||
|
||||
is_string<T> ||
|
||||
is_any_container<T> ||
|
||||
is_tuple_like<T> ||
|
||||
std::is_pointer_v<T>);
|
||||
|
||||
// @brief Проверяет, является ли тип контейнером с непрерывной памятью
|
||||
// @details Обнаруживает контейнеры, элементы которых хранятся в непрерывной памяти:
|
||||
// - std::vector - динамический массив
|
||||
// - C-style массивы
|
||||
// - std::array - статический массив
|
||||
// Критически важно для низкоуровневых операций и взаимодействия с C API
|
||||
template<typename T>
|
||||
concept is_contiguous_container = std::same_as<T, std::vector<typename T::value_type, typename T::allocator_type>> ||
|
||||
is_fixed_array<T> ||
|
||||
is_std_array<T>;
|
||||
|
||||
// @brief Проверяет, поддерживает ли контейнер добавление в начало
|
||||
// @details Обнаруживает контейнеры с push_front():
|
||||
// - deque, list, forward_list
|
||||
// Полезно для алгоритмов, работающих с очередями и стеками
|
||||
template<typename Container, typename Value>
|
||||
concept can_push_front = requires(Container c, Value&& v) { c.push_front(std::forward<Value>(v)); };
|
||||
|
||||
// @brief Проверяет, поддерживает ли контейнер добавление в конец
|
||||
// @details Обнаруживает контейнеры с push_back():
|
||||
// - vector, deque, list
|
||||
// Важно для алгоритмов, которые строят контейнеры последовательно
|
||||
template<typename Container, typename Value>
|
||||
concept can_push_back = requires(Container c, Value&& v) { c.push_back(std::forward<Value>(v)); };
|
||||
|
||||
// @brief Проверяет, поддерживает ли контейнер поиск по ключу
|
||||
// @details Обнаруживает контейнеры с методом find():
|
||||
// - map, set, unordered_map, unordered_set
|
||||
// Ключевой концепт для алгоритмов поиска и проверки существования элементов
|
||||
template<typename Container, typename Key>
|
||||
concept can_find = requires(Container c, Key&& key) { c.find(std::forward<Key>(key)); };
|
||||
|
||||
// @brief Проверяет, является ли тип bool
|
||||
// @brief Проверяет, является ли тип bool
|
||||
template<typename T>
|
||||
concept is_bool = std::is_same_v<std::remove_cvref_t<T>, bool>;
|
||||
|
||||
@@ -278,46 +199,76 @@ namespace hack::concepts
|
||||
{ s.empty() } -> std::same_as<bool>;
|
||||
{ s.size() } -> std::convertible_to<std::size_t>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Универсальные концепты для категоризации
|
||||
// @brief Проверяет, является ли тип любым контейнером
|
||||
// @details Всеобъемлющий концепт для обнаружения контейнеров любого типа:
|
||||
// - Последовательные контейнеры
|
||||
// - Ассоциативные контейнеры
|
||||
// - Адаптеры контейнеров
|
||||
// - Массивы (C-style и std::array)
|
||||
// Основной концепт для обобщенных алгоритмов работы с контейнерами
|
||||
template<typename T>
|
||||
concept is_any_container = is_sequence_container<T> ||
|
||||
is_associative_container<T> ||
|
||||
is_unordered_associative_container<T> ||
|
||||
is_container_adapter<T> ||
|
||||
is_fixed_array<T> ||
|
||||
is_set_like<T> ||
|
||||
is_tuple_like<T> ||
|
||||
is_stack<T> ||
|
||||
is_std_array<T>;
|
||||
|
||||
// @brief Проверяет, имеет ли тип размер
|
||||
// @details Обнаруживает типы, у которых можно узнать размер:
|
||||
// - Контейнеры с методом size()
|
||||
// - Массивы с известным размером
|
||||
// - Кортежи с известным количеством элементов
|
||||
// Важно для алгоритмов, требующих предварительного знания размера
|
||||
template<typename T>
|
||||
concept is_sized = has_size<T> || is_fixed_array<T> || is_tuple_like<T>;
|
||||
|
||||
// Концепт для "неподдерживаемых" типов
|
||||
// @brief Проверяет, является ли тип неподдерживаемым
|
||||
// @details Обнаруживает типы, которые не входят в известные категории:
|
||||
// - Пользовательские типы без ожидаемого интерфейса
|
||||
// - Специфичные типы из сторонних библиотек
|
||||
// - Типы, для которых нет специализированной обработки
|
||||
// Используется для static_assert и генерации понятных ошибок
|
||||
template<typename T>
|
||||
concept not_supported = !(is_number<T> ||
|
||||
is_string<T> ||
|
||||
is_bool<T> ||
|
||||
is_any_container<T> ||
|
||||
std::is_pointer_v<T>);
|
||||
|
||||
// @brief Проверяет, поддерживает ли контейнер добавление в начало
|
||||
// @details Обнаруживает контейнеры с push_front():
|
||||
// - deque, list, forward_list
|
||||
// Полезно для алгоритмов, работающих с очередями и стеками
|
||||
template<typename Container, typename Value>
|
||||
concept can_push_front = requires(Container c, Value&& v) { c.push_front(std::forward<Value>(v)); };
|
||||
|
||||
// @brief Проверяет, поддерживает ли контейнер добавление в конец
|
||||
// @details Обнаруживает контейнеры с push_back():
|
||||
// - vector, deque, list
|
||||
// Важно для алгоритмов, которые строят контейнеры последовательно
|
||||
template<typename Container, typename Value>
|
||||
concept can_push_back = requires(Container c, Value&& v) { c.push_back(std::forward<Value>(v)); };
|
||||
|
||||
// @brief Проверяет, поддерживает ли контейнер поиск по ключу
|
||||
// @details Обнаруживает контейнеры с методом find():
|
||||
// - map, set, unordered_map, unordered_set
|
||||
// Ключевой концепт для алгоритмов поиска и проверки существования элементов
|
||||
template<typename Container, typename Key>
|
||||
concept can_find = requires(Container c, Key&& key) { c.find(std::forward<Key>(key)); };
|
||||
|
||||
// для логирования собственных структур
|
||||
// мало где используется, только в логере для проверки и выдачи сообщения
|
||||
template<typename T>
|
||||
concept has_get_logger_data = requires(T t) {
|
||||
{ t.get_logger_data() };
|
||||
};
|
||||
}
|
||||
|
||||
// namespace hack::concepts
|
||||
// {
|
||||
// template<typename T>
|
||||
// concept is_map = std::same_as<T, std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>> ||
|
||||
// std::same_as<T, std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::hasher, typename T::key_equal, typename T::allocator_type>>;
|
||||
//
|
||||
// template<typename T>
|
||||
// concept is_tuple = requires (T t) { std::tuple_cat(t, std::make_tuple(1, "tuple")); };
|
||||
//
|
||||
// template<typename T>
|
||||
// concept is_set = std::same_as<T, std::set<typename T::key_type, typename T::key_compare, typename T::allocator_type>>;
|
||||
//
|
||||
// template<typename T>
|
||||
// concept is_unordered_set = std::same_as<T, std::unordered_set<typename T::key_type>>;
|
||||
//
|
||||
// template<typename T>
|
||||
// concept is_forward_list = std::same_as<T, std::forward_list<typename T::value_type>>;
|
||||
//
|
||||
// template<typename T>
|
||||
// concept is_string = std::is_convertible_v<T, std::string_view>;
|
||||
//
|
||||
// template<typename T, std::size_t N = 0>
|
||||
// concept is_sequence_container = std::same_as<T, std::vector<typename T::value_type>> || std::same_as<T, std::list<typename T::value_type>> || (std::is_array_v<T> && N > 0);
|
||||
//
|
||||
// template<typename T>
|
||||
// concept is_associative_container = is_map<T> || is_tuple<T> || is_set<T> || is_unordered_set<T>;
|
||||
//
|
||||
//
|
||||
// template<typename T>
|
||||
// concept not_defined = !std::enable_if_t<!(std::integral<T> ||
|
||||
// is_sequence_container<T> ||
|
||||
// is_map<T> ||
|
||||
// is_tuple<T> ||
|
||||
// is_set<T> ||
|
||||
// is_unordered_set<T> ||
|
||||
// is_forward_list<T> ||
|
||||
// std::is_array<T>() ||
|
||||
// is_string<T>), bool>() == true;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <experimental/source_location>
|
||||
#include <source_location>
|
||||
#include <any>
|
||||
|
||||
#include "hack/utils/color.hpp"
|
||||
#include <unordered_map>
|
||||
#include "hack/exception/title.hpp"
|
||||
#include "hack/patterns/identificator.hpp"
|
||||
|
||||
// NOTE
|
||||
// вмесо nlohman::json можно поробовать прикрутить https://jqlang.github.io/jq
|
||||
namespace hack
|
||||
{
|
||||
class exception
|
||||
class exception : public patterns::identificator<>
|
||||
{
|
||||
using LOCATION = std::experimental::source_location;
|
||||
using LOCATION = std::source_location;
|
||||
|
||||
public:
|
||||
exception(const std::experimental::source_location loc = LOCATION::current()) : m_location { loc } {}
|
||||
exception(const LOCATION loc = LOCATION::current()) : m_location { loc } {}
|
||||
exception(const exception& e) noexcept : m_service { e.m_service },
|
||||
m_system_error { e.m_system_error },
|
||||
m_title { e.m_title },
|
||||
m_description { e.m_description },
|
||||
m_location { e.m_location },
|
||||
m_data { e.m_data } {}
|
||||
~exception() = default;
|
||||
|
||||
public:
|
||||
@@ -23,38 +27,18 @@ namespace hack
|
||||
void system_error(const std::exception& e) noexcept { m_system_error = e.what(); }
|
||||
void title(const std::string v) noexcept { m_title = v; }
|
||||
void description(const std::string v) noexcept { m_description = v; }
|
||||
void set_data(std::any v) noexcept { m_data = v; }
|
||||
std::any get_data() noexcept { return m_data; }
|
||||
LOCATION get_location() const noexcept { return m_location; }
|
||||
|
||||
void log()
|
||||
{
|
||||
std::cout << utils::color::bold << utils::color::red <<"["+m_service+"] " << utils::color::reset
|
||||
<< m_location.file_name() << ":"
|
||||
<< utils::color::italic << utils::color::yellow << m_location.function_name() << "()" << utils::color::reset
|
||||
<< utils::color::bold << utils::color::blue << "[" << m_location.line() << "]" << utils::color::reset << ": "
|
||||
<< m_title << std::endl;
|
||||
|
||||
if (!m_description.empty())
|
||||
std::cout << utils::color::bold << utils::color::red <<"["+m_service+"] " << utils::color::reset
|
||||
<< m_location.file_name() << ":"
|
||||
<< utils::color::italic << utils::color::yellow << m_location.function_name() << "()" << utils::color::reset
|
||||
<< utils::color::bold << utils::color::blue << "[" << m_location.line() << "]" << utils::color::reset << ": "
|
||||
<< m_description << std::endl;
|
||||
|
||||
if (!m_system_error.empty())
|
||||
std::cout << utils::color::bold << utils::color::red <<"["+m_service+"] " << utils::color::reset
|
||||
<< m_location.file_name() << ":"
|
||||
<< utils::color::italic << utils::color::yellow << m_location.function_name() << "()" << utils::color::reset
|
||||
<< utils::color::bold << utils::color::blue << "[" << m_location.line() << "]" << utils::color::reset << ": "
|
||||
<< m_system_error << std::endl;
|
||||
}
|
||||
public:
|
||||
void set(std::string key, std::any val) noexcept { m_data[key] = val; }
|
||||
const std::unordered_map<std::string, std::any>& get() const noexcept { return m_data; }
|
||||
|
||||
private:
|
||||
std::string m_service;
|
||||
std::string m_system_error;
|
||||
std::string m_title { exception_title::NO_VALID_DATA };
|
||||
std::string m_description;
|
||||
std::experimental::source_location m_location;
|
||||
std::any m_data;
|
||||
std::string m_service; // имя сервиса где происходит исключение
|
||||
std::string m_system_error; // системная ошибка
|
||||
std::string m_title { exception_title::NO_VALID_DATA }; // название ошибки
|
||||
std::string m_description; // описание этой ошибки
|
||||
LOCATION m_location; // строка, где произошла ошибка
|
||||
std::unordered_map<std::string, std::any> m_data; // данные для логирования
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <experimental/source_location>
|
||||
#include <string>
|
||||
|
||||
#include "hack/utils/color.hpp"
|
||||
#include "hack/concepts/concepts.hpp"
|
||||
#include "hack/iterators/sequence_ostream_iterator.hpp"
|
||||
#include "hack/iterators/associative_ostream_iterator.hpp"
|
||||
|
||||
#include "hack/patterns/ring_buffer.hpp"
|
||||
|
||||
namespace hack
|
||||
{
|
||||
class log
|
||||
{
|
||||
public:
|
||||
log(std::string devider_ = " ", std::experimental::source_location location_ = std::experimental::source_location::current()) : location { location_ }
|
||||
{
|
||||
this->devider = devider_;
|
||||
}
|
||||
|
||||
log(log&) = delete;
|
||||
log(log&&) = delete;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
void operator() (const Args&... args)
|
||||
{
|
||||
count = sizeof...(Args);
|
||||
prepare(make_type_view, location);
|
||||
print(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::experimental::source_location location;
|
||||
inline static int count = 0;
|
||||
inline static std::string devider = " ";
|
||||
|
||||
private:
|
||||
template<typename T, typename U>
|
||||
void prepare(T t, U u)
|
||||
{
|
||||
std::cout << t
|
||||
<< u.file_name() << ":" << utils::color::reset
|
||||
<< utils::color::italic << utils::color::yellow << u.function_name() << "()" << utils::color::reset
|
||||
<< utils::color::bold << utils::color::blue << "[" << u.line() << "]" << utils::color::reset << ": ";
|
||||
}
|
||||
|
||||
static void print() { std::cout << std::endl; }
|
||||
|
||||
static std::ostream& make_type_view(std::ostream &os)
|
||||
{
|
||||
os << utils::color::bold << utils::color::green << "[ok]" << utils::color::reset << utils::color::green;
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static void print(const T& data, const Args&... args)
|
||||
{
|
||||
--count;
|
||||
print_t(data);
|
||||
print(args...);
|
||||
}
|
||||
|
||||
template<concepts::is_string T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << data << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << data << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<concepts::is_sequence_container T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << "{ ";
|
||||
std::copy(data.cbegin(), data.cend(), iterators::sequence_ostream_iterator<typename T::value_type>(data.size(), std::cout));
|
||||
std::cout << " }" << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<concepts::is_set T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << "{ ";
|
||||
std::copy(data.cbegin(), data.cend(), iterators::sequence_ostream_iterator<typename T::value_type>(data.size(), std::cout));
|
||||
std::cout << " }" << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<concepts::is_unordered_set T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << "{ ";
|
||||
std::copy(data.cbegin(), data.cend(), iterators::sequence_ostream_iterator<typename T::value_type>(data.size(), std::cout));
|
||||
std::cout << " }" << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<concepts::is_forward_list T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << "{ ";
|
||||
std::copy(data.cbegin(), data.cend(), iterators::sequence_ostream_iterator<typename T::value_type>(std::distance(data.cbegin(), data.cend()), std::cout));
|
||||
std::cout << " }" << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<concepts::is_map T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << "{";
|
||||
std::copy(data.begin(), data.cend(), iterators::associative_ostream_iterator<typename T::value_type>(data.size(), std::cout));
|
||||
std::cout << "}" << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<concepts::is_tuple T, typename std::size_t... idx>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
print_t(data, std::make_index_sequence<std::tuple_size<T>::value>{});
|
||||
}
|
||||
|
||||
template<typename T, typename std::size_t... idx>
|
||||
static void print_t(const T& data, std::index_sequence<idx...>)
|
||||
{
|
||||
std::cout << "{ ";
|
||||
((std::cout << std::get<idx>(data) << (idx != std::tuple_size<T>::value - 1 ? devider : "")), ...);
|
||||
std::cout << " }" << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<concepts::not_defined T>
|
||||
static void print_t(const T& data)
|
||||
{
|
||||
std::cout << data << (count != 0 ? devider : "");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void print_t(const hack::patterns::ring_buffer<T>& rb)
|
||||
{
|
||||
print_t(rb.get_src());
|
||||
}
|
||||
|
||||
friend class warn;
|
||||
friend class error;
|
||||
};
|
||||
|
||||
class warn : public log
|
||||
{
|
||||
public:
|
||||
warn(std::string devider_ = " ", std::experimental::source_location location_ = std::experimental::source_location::current()) : location { location_ }
|
||||
{
|
||||
this->devider = devider_;
|
||||
}
|
||||
|
||||
warn(warn&) = delete;
|
||||
warn(warn&&) = delete;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
void operator() (const Args&... args)
|
||||
{
|
||||
prepare(make_type_view, location);
|
||||
count = sizeof...(Args);
|
||||
print(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::experimental::source_location location;
|
||||
|
||||
private:
|
||||
static std::ostream& make_type_view(std::ostream &os)
|
||||
{
|
||||
os << utils::color::bold << utils::color::yellow << "[WARN]" << utils::color::reset << utils::color::yellow;
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
class error : public log
|
||||
{
|
||||
public:
|
||||
error(std::string devider_ = " ", std::experimental::source_location location_ = std::experimental::source_location::current()) : location { location_ }
|
||||
{
|
||||
this->devider = devider_;
|
||||
}
|
||||
error(error&) = delete;
|
||||
error(error&&) = delete;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
void operator() (const Args&... args)
|
||||
{
|
||||
prepare(make_type_view, location);
|
||||
count = sizeof...(Args);
|
||||
print(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::experimental::source_location location;
|
||||
|
||||
private:
|
||||
static std::ostream& make_type_view(std::ostream &os)
|
||||
{
|
||||
os << utils::color::bold << utils::color::red << "[ERROR]" << utils::color::reset << utils::color::red;
|
||||
return os;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,16 +3,13 @@
|
||||
#include <source_location> // C++20
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
#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 <iterator>
|
||||
|
||||
#include <execinfo.h>
|
||||
#include <iostream>
|
||||
#include <cxxabi.h>
|
||||
#include "hack/exception/exception.hpp"
|
||||
|
||||
// HERE
|
||||
// и нужно сделать реализацию где выводлится все в одной линии но при помощи цикла
|
||||
@@ -25,19 +22,25 @@ namespace hack
|
||||
public:
|
||||
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_func() { m_no_func = true; };
|
||||
void no_row() { m_no_row = true; }
|
||||
void no_info() { no_file(); no_func(); no_row(); }
|
||||
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_func = m_base_config.m_no_func;
|
||||
m_no_row = m_base_config.m_no_row;
|
||||
m_devider = m_base_config.m_devider;
|
||||
m_bool_as_number = m_base_config.m_bool_as_number;
|
||||
}
|
||||
|
||||
public:
|
||||
void on_log() { m_type = type::LOG; }
|
||||
void on_warn() { m_type = type::WARN; }
|
||||
void on_error() { m_type = type::ERROR; }
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
void operator() (const Args&... args)
|
||||
@@ -51,24 +54,42 @@ namespace hack
|
||||
// настройки по умолчанию
|
||||
struct config
|
||||
{
|
||||
bool m_no_func = false; // показывать/не показывать название функции в выоде логов
|
||||
bool m_no_file = false; // показывать/не показывать название файла/пути в выоде логов
|
||||
bool m_no_func = false; // показывать/не показывать название функции в выоде логов
|
||||
bool m_no_row = false; // показывать/не показывать номер строки в выоде логов
|
||||
bool m_bool_as_number = false; // показывет bool как число или как текст (0, false);
|
||||
std::string m_devider = " "; // разделитель по умолчанию
|
||||
} m_base_config;
|
||||
|
||||
enum class type
|
||||
{
|
||||
LOG,
|
||||
WARN,
|
||||
ERROR
|
||||
} m_type;
|
||||
|
||||
private:
|
||||
void prepare()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << utils::color::bold << utils::color::green << "[ok] " << utils::color::reset;
|
||||
switch(m_type)
|
||||
{
|
||||
case type::LOG:
|
||||
ss << utils::color::bold << utils::color::green << "[ok] " << utils::color::reset;
|
||||
break;
|
||||
case type::WARN:
|
||||
ss << utils::color::bold << utils::color::magenta << "[WARN]" << utils::color::reset;
|
||||
break;
|
||||
case type::ERROR:
|
||||
ss << utils::color::bold << utils::color::red << "[ERROR]" << utils::color::reset;
|
||||
break;
|
||||
}
|
||||
|
||||
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::italic << utils::color::yellow<< m_location.function_name() << utils::color::reset;
|
||||
|
||||
if (!m_no_row)
|
||||
ss << utils::color::bold << utils::color::blue << "[" << m_location.line() << "] " << utils::color::reset;
|
||||
@@ -100,6 +121,11 @@ namespace hack
|
||||
std::cout << data << (m_count != 0 ? m_devider : "");
|
||||
}
|
||||
|
||||
void print_t(const std::filesystem::path& path)
|
||||
{
|
||||
std::cout << path.string() << (m_count != 0 ? m_devider : "");
|
||||
}
|
||||
|
||||
// для bool
|
||||
template<concepts::is_bool T>
|
||||
void print_t(const T& data)
|
||||
@@ -171,25 +197,106 @@ namespace hack
|
||||
}
|
||||
|
||||
// Для stack - выводим содержимое (но stack нельзя итерировать!)
|
||||
template<concepts::is_stack T>
|
||||
void print_t(const T& stack)
|
||||
{
|
||||
T temp = stack;
|
||||
|
||||
std::cout << "{ ";
|
||||
|
||||
bool first = true;
|
||||
while (!temp.empty())
|
||||
template<concepts::is_stack T>
|
||||
void print_t(const T& stack)
|
||||
{
|
||||
if (!first)
|
||||
std::cout << m_devider;
|
||||
first = false;
|
||||
std::cout << temp.top();
|
||||
temp.pop();
|
||||
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 : "");
|
||||
}
|
||||
|
||||
std::cout << " }" << (m_count != 0 ? m_devider : "");
|
||||
}
|
||||
// для пользовательских типов
|
||||
// у них должен быть отпределен метод get_logger_data()
|
||||
// возвращающий один из обработанных типов
|
||||
template<concepts::not_supported T>
|
||||
void print_t(const T& rb)
|
||||
{
|
||||
static_assert(concepts::has_get_logger_data<T>, "Type must have get_logger_data() method that returns value_type");
|
||||
print_t(rb.get_logger_data());
|
||||
}
|
||||
|
||||
void print_t(const hack::exception& e)
|
||||
{
|
||||
auto loc = e.get_location();
|
||||
std::cout << utils::color::bold << utils::color::red;
|
||||
std::cout << utils::color::reset;
|
||||
// в скобочках указывается строка и уникальный id исключения
|
||||
// это id при копировании исключения изменяется, так работает паттерн identificator
|
||||
// т.е. следить и думать что каждое созданное исключение - это увеличение на 1 идентификатора
|
||||
// так себе затея !!!
|
||||
std::cout << " " << loc.file_name() << utils::color::bold << utils::color::blue << "[" << loc.line() << "," << e.get_id() << "]: " << utils::color::reset << std::endl;
|
||||
std::cout << utils::color::bold << utils::color::gray;
|
||||
std::cout << " > ";
|
||||
std::cout << utils::color::reset;
|
||||
const auto& data = e.get();
|
||||
bool first = true;
|
||||
for (const auto& [key, value] : data)
|
||||
{
|
||||
if (!first) std::cout << ", ";
|
||||
|
||||
first = false;
|
||||
std::cout << key << ": ";
|
||||
|
||||
if (value.type() == typeid(std::string))
|
||||
{
|
||||
// HERE
|
||||
// хотельсь бы написать так:
|
||||
// print_t(std::any_cast<std::string>(value));
|
||||
// но по какой-то причине на эту запись ругается clangd
|
||||
// Client clangd quit with exit code 0 and signal 11. Check log for errors: /home/chatlanin/.local/state/nvim/lsp.log
|
||||
// когда они это порявят напишем а пока... пише так:
|
||||
print_t(std::any_cast<std::string>(value).c_str());
|
||||
}
|
||||
else if (value.type() == typeid(std::filesystem::path))
|
||||
print_t(std::any_cast<std::filesystem::path>(value).c_str());
|
||||
// конец HERE выше
|
||||
|
||||
else if (value.type() == typeid(int))
|
||||
print_t(std::any_cast<int>(value));
|
||||
else if (value.type() == typeid(double))
|
||||
print_t(std::any_cast<double>(value));
|
||||
else if (value.type() == typeid(float))
|
||||
print_t(std::any_cast<float>(value));
|
||||
else if (value.type() == typeid(bool))
|
||||
print_t(std::any_cast<bool>(value));
|
||||
// vector
|
||||
else if (value.type() == typeid(std::vector<std::string>))
|
||||
print_t(std::any_cast<std::vector<std::string>>(value));
|
||||
else if (value.type() == typeid(std::vector<int>))
|
||||
print_t(std::any_cast<std::vector<int>>(value));
|
||||
else if (value.type() == typeid(std::vector<double>))
|
||||
print_t(std::any_cast<std::vector<double>>(value));
|
||||
else if (value.type() == typeid(std::vector<float>))
|
||||
print_t(std::any_cast<std::vector<float>>(value));
|
||||
// map
|
||||
else if (value.type() == typeid(std::map<std::string, std::string>))
|
||||
print_t(std::any_cast<std::map<std::string, std::string>>(value));
|
||||
else if (value.type() == typeid(std::map<std::string, int>))
|
||||
print_t(std::any_cast<std::map<std::string, int>>(value));
|
||||
else if (value.type() == typeid(std::map<std::string, double>))
|
||||
print_t(std::any_cast<std::map<std::string, double>>(value));
|
||||
else if (value.type() == typeid(std::map<std::string, float>))
|
||||
print_t(std::any_cast<std::map<std::string, float>>(value));
|
||||
else
|
||||
{
|
||||
std::cout << utils::color::bold << utils::color::magenta;
|
||||
print_t("[unsupported type]");
|
||||
std::cout << utils::color::reset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::source_location m_location;
|
||||
@@ -205,6 +312,21 @@ namespace hack
|
||||
inline logger& log(std::source_location location = std::source_location::current())
|
||||
{
|
||||
logger::instance().set_location(location);
|
||||
logger::instance().on_log();
|
||||
return logger::instance();
|
||||
}
|
||||
|
||||
inline logger& warn(std::source_location location = std::source_location::current())
|
||||
{
|
||||
logger::instance().set_location(location);
|
||||
logger::instance().on_warn();
|
||||
return logger::instance();
|
||||
}
|
||||
|
||||
inline logger& error(std::source_location location = std::source_location::current())
|
||||
{
|
||||
logger::instance().set_location(location);
|
||||
logger::instance().on_error();
|
||||
return logger::instance();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <atomic>
|
||||
#include <type_traits>
|
||||
#include <limits>
|
||||
|
||||
namespace hack::patterns
|
||||
{
|
||||
// Иногда нужно, чтобы был id но в виде какого-то числа.
|
||||
// Например при выводе графики в массиве, типа как в VueJS
|
||||
// вот этьо класс и пытается этим заниматься.
|
||||
/**
|
||||
* @brief Потокобезопасный генератор уникальных идентификаторов
|
||||
* @tparam T Тип идентификатора (должен быть целочисленным)
|
||||
*
|
||||
* Используется для генерации уникальных ID в пределах типа T.
|
||||
* Автоматически обрабатывает переполнение и обеспечивает потокобезопасность.
|
||||
* Идеально для идентификации элементов в UI, графиках, базах данных и т.д.
|
||||
*/
|
||||
template<typename T = std::size_t>
|
||||
class identificator
|
||||
{
|
||||
public:
|
||||
identificator() { m_id = m_counter; ++m_counter; }
|
||||
static_assert(std::is_integral_v<T>, "T must be an integral type");
|
||||
static_assert(!std::is_same_v<T, bool>, "T cannot be bool");
|
||||
|
||||
public:
|
||||
T m_id;
|
||||
public:
|
||||
/// @brief Конструктор по умолчанию - генерирует новый уникальный ID
|
||||
identificator() noexcept : m_id{ next_id() } {}
|
||||
|
||||
private:
|
||||
static inline T m_counter = 0;
|
||||
/// @brief Конструктор копирования - создает новый ID (не копирует старый!)
|
||||
identificator(const identificator&) noexcept : m_id{ next_id() } {}
|
||||
|
||||
identificator(identificator&& idtf) noexcept : m_id{ idtf.get_id() } {}
|
||||
|
||||
/// @brief Оператор присваивания - не меняет ID объекта!
|
||||
identificator& operator=(const identificator&) noexcept { return *this; }
|
||||
|
||||
/// @brief Оператор перемещения - не меняет ID объекта!
|
||||
identificator& operator=(identificator&&) noexcept { return *this; }
|
||||
|
||||
/// @brief Сравнение идентификаторов
|
||||
bool operator==(const identificator& other) const noexcept { return m_id == other.m_id; }
|
||||
bool operator!=(const identificator& other) const noexcept { return m_id != other.m_id; }
|
||||
bool operator<(const identificator& other) const noexcept { return m_id < other.m_id; }
|
||||
|
||||
T get_id() const noexcept { return m_id; }
|
||||
|
||||
protected:
|
||||
// бывает нужно принудительно установить id, например при копировании в наследовании
|
||||
// см. exception
|
||||
void set_id(T new_id) { m_id = new_id; }
|
||||
|
||||
private:
|
||||
/// @brief Генерация следующего уникального ID
|
||||
static T next_id() noexcept
|
||||
{
|
||||
T new_id = m_counter.fetch_add(1, std::memory_order_relaxed);
|
||||
// Обработка переполнения - циклическое повторение
|
||||
if (new_id == std::numeric_limits<T>::max())
|
||||
{
|
||||
// Можно бросить исключение или сбросить счетчик
|
||||
// В данном случае циклически повторяем
|
||||
m_counter.store(1, std::memory_order_relaxed);
|
||||
return new_id; // Возвращаем максимальное значение как особый случай
|
||||
}
|
||||
|
||||
return new_id;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_id;
|
||||
// Атомарный счетчик для потокобезопасности
|
||||
static inline std::atomic<T> m_counter = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,153 +6,311 @@
|
||||
#include <vector>
|
||||
|
||||
#include "hack/exception/exception.hpp"
|
||||
#include "hack/logger/logger.hpp"
|
||||
|
||||
namespace hack::patterns
|
||||
{
|
||||
// Колцевой буфер.
|
||||
// HERE
|
||||
// сделать опсание каждой функции
|
||||
template<typename T>
|
||||
class ring_buffer
|
||||
{
|
||||
using MUTEX = std::lock_guard<std::recursive_mutex>;
|
||||
// Используем рекурсивный мьютекс для возможности вызова методов из других методов
|
||||
using mutex_type = std::recursive_mutex;
|
||||
using lock_guard = std::lock_guard<mutex_type>;
|
||||
|
||||
public:
|
||||
ring_buffer() = default;
|
||||
explicit ring_buffer(int s)
|
||||
{
|
||||
m_size = s;
|
||||
m_data.resize(m_size);
|
||||
};
|
||||
public:
|
||||
// Конструктор по умолчанию - создает неинициализированный буфер
|
||||
// Не выделяет память, все поля остаются нулевыми
|
||||
ring_buffer() = default;
|
||||
|
||||
public:
|
||||
void create(int s)
|
||||
// Конструктор с параметром - создает буфер заданного размера
|
||||
// explicit предотвращает неявное преобразование
|
||||
explicit ring_buffer(std::size_t size) : m_size(size), m_data(size)
|
||||
{
|
||||
// Инициализация через список инициализации:
|
||||
// m_size(size) - устанавливает емкость буфера
|
||||
// m_data(size) - создает вектор с заданным количеством элементов
|
||||
// Элементы вектора инициализируются значением по умолчанию для типа T
|
||||
}
|
||||
|
||||
// Инициализирует буфер заданным размером, если он еще не инициализирован
|
||||
// Если буфер уже был инициализирован, игнорирует вызов (идиома "initialize once")
|
||||
void create(std::size_t size)
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
if (m_size == 0) // Проверяем, не инициализирован ли уже буфер
|
||||
{
|
||||
if (m_size > 0) return;
|
||||
m_size = s;
|
||||
m_data.resize(m_size);
|
||||
m_size = size; // Устанавливаем размер буфера
|
||||
m_data.resize(size); // Выделяем память под данные через resize()
|
||||
// resize() гарантирует, что вектор будет содержать ровно size элементов
|
||||
// Все элементы будут инициализированы значением по умолчанию для типа T
|
||||
}
|
||||
else hack::warn()("Buffer is initialize...");
|
||||
// Если буфер уже инициализирован (m_size > 0), игнорируем вызов
|
||||
// Это предотвращает случайное пересоздание буфера
|
||||
}
|
||||
|
||||
// Добавляет один элемент в буфер (потокобезопасно)
|
||||
// Возвращает true если элемент успешно добавлен, false если буфер не инициализирован
|
||||
bool put(T item) noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
if (m_size == 0) return false; // Проверяем инициализацию буфера
|
||||
|
||||
// Помещаем элемент в текущую позицию головы
|
||||
m_data[m_head] = item;
|
||||
advance_head(); // Обновляем позицию головы и флаги состояния
|
||||
return true;
|
||||
}
|
||||
|
||||
// Добавляет диапазон элементов [first, last) в буфер
|
||||
template<typename InputIt>
|
||||
bool put(InputIt first, InputIt last) noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
if (m_size == 0) return 0; // Проверяем инициализацию буфера
|
||||
|
||||
std::size_t count = 0;
|
||||
// Итерируем по переданному диапазону от first до last
|
||||
while (first != last)
|
||||
{
|
||||
// Копируем элемент из исходного диапазона в буфер
|
||||
// Используем копирование, а не перемещение, чтобы не нарушать исходные данные
|
||||
m_data[m_head] = *first;
|
||||
advance_head(); // Обновляем позицию головы после каждого добавления
|
||||
++first; // Переходим к следующему элементу в исходном диапазоне
|
||||
++count; // Увеличиваем счетчик добавленных элементов
|
||||
}
|
||||
return count; // Возвращаем количество фактически добавленных элементов
|
||||
}
|
||||
|
||||
// Добавляет все элементы из вектора в буфер
|
||||
// Удобная обертка над put для работы с std::vector
|
||||
std::size_t put(const std::vector<T>& source) noexcept
|
||||
{
|
||||
// Вызываем put с итераторами начала и конца вектора
|
||||
return put(source.begin(), source.end());
|
||||
}
|
||||
|
||||
// Добавляет указанное количество элементов из вектора в буфер
|
||||
// Полезно когда нужно добавить только часть вектора
|
||||
std::size_t put(const std::vector<T>& source, std::size_t size)
|
||||
{
|
||||
// Проверяем, что запрошенный размер не превышает размер исходного вектора
|
||||
if (source.size() < size)
|
||||
{
|
||||
// Создаем и бросаем исключение с информацией об ошибке
|
||||
hack::exception ex;
|
||||
ex.title(exception_title::NO_VALID_SIZE);
|
||||
ex.description("data size is not equal source");
|
||||
throw ex;
|
||||
}
|
||||
|
||||
void put(T item) noexcept
|
||||
// Создаем временный диапазон из первых 'size' элементов вектора
|
||||
auto first = source.begin();
|
||||
auto last = source.begin() + size;
|
||||
return put(first, last); // Добавляем указанный диапазон
|
||||
}
|
||||
|
||||
// Извлекает один элемент из буфера (потокобезопасно)
|
||||
// Возвращает std::optional<T> - содержит элемент если буфер не пуст, или std::nullopt
|
||||
// noexcept гарантирует, что метод не выбрасывает исключений
|
||||
std::optional<T> pop() noexcept
|
||||
{
|
||||
// Захватываем мьютекс для потокобезопасности
|
||||
lock_guard lock(m_mutex);
|
||||
// Проверяем, не пуст ли буфер
|
||||
if (empty()) return std::nullopt;
|
||||
|
||||
// Извлекаем элемент из хвоста с перемещением
|
||||
// std::move позволяет избежать копирования и передать владение ресурсами
|
||||
T value = std::move(m_data[m_tail]);
|
||||
advance_tail(); // Обновляем позицию хвоста и снимаем флаг полноты
|
||||
return value; // Возвращаем извлеченный элемент (неявно преобразуется в std::optional)
|
||||
}
|
||||
|
||||
// Извлекает несколько элементов в выходной итератор
|
||||
// count - максимальное количество элементов для извлечения
|
||||
// Возвращает количество фактически извлеченных элементов
|
||||
std::size_t pop(std::vector<T>& out, std::size_t count) noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
|
||||
// Определяем сколько элементов можно реально извлечь
|
||||
// std::min гарантирует, что мы не попытаемся извлечь больше чем есть
|
||||
std::size_t actual_count = std::min(count, size());
|
||||
|
||||
// Извлекаем элементы один за другим
|
||||
for (std::size_t i = 0; i < actual_count; ++i)
|
||||
{
|
||||
MUTEX lock(m_mutex);
|
||||
m_data[m_head] = item;
|
||||
head_refresh();
|
||||
// Перемещаем элемент в выходной итератор
|
||||
// std::move передает владение ресурсами элемента
|
||||
out.push_back(std::move(m_data[m_tail]));
|
||||
advance_tail(); // Обновляем позицию хвоста после каждого извлечения
|
||||
}
|
||||
return actual_count; // Возвращаем количество извлеченных элементов
|
||||
}
|
||||
|
||||
// Возвращает один элемент из буфера (потокобезопасно)
|
||||
// Возвращает std::optional<T> - содержит элемент если буфер не пуст, или std::nullopt
|
||||
// noexcept гарантирует, что метод не выбрасывает исключений
|
||||
// Возвращенный элемент не затирается, хвост не сдвигается. Можно использовать просто для просмотра
|
||||
std::optional<T> get() noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
if (empty()) return std::nullopt; // Проверяем, не пуст ли буфер
|
||||
return m_data[m_tail];
|
||||
}
|
||||
|
||||
// Безопасная версия get() - копирует элементы в вектор с проверкой границ
|
||||
// Возвращает количество фактически скопированных элементов
|
||||
std::size_t get(std::vector<T>& destination, std::size_t count) noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
if (empty()) return 0; // Проверяем, не пуст ли буфер
|
||||
|
||||
// Определяем сколько элементов можно реально извлечь
|
||||
std::size_t actual_count = std::min(count, size());
|
||||
actual_count = std::min(actual_count, destination.size()); // Учитываем размер приемника
|
||||
|
||||
std::size_t current = m_tail;
|
||||
for (std::size_t i = 0; i < actual_count; ++i)
|
||||
{
|
||||
// Копируем элемент из буфера в вектор-приемник
|
||||
destination[i] = m_data[current];
|
||||
// Перемещаемся к следующему элементу с учетом круговой природы буфера
|
||||
current = (current + 1) % m_size;
|
||||
}
|
||||
|
||||
// указываем размер, который хотим положить
|
||||
// т.к. может нужно положить только часть из переданного массива
|
||||
void put(const std::vector<T>& source, std::size_t size)
|
||||
{
|
||||
if (source.size() < size)
|
||||
{
|
||||
hack::exception ex;
|
||||
ex.title(exception_title::NO_VALID_SIZE);
|
||||
ex.description("data size is not equal source");
|
||||
throw ex;
|
||||
}
|
||||
return actual_count;
|
||||
}
|
||||
|
||||
MUTEX lock(m_mutex);
|
||||
for (std::size_t i = 0; i < size; ++i)
|
||||
{
|
||||
m_data[m_head] = source[i];
|
||||
head_refresh();
|
||||
}
|
||||
}
|
||||
// Возвращает копию внутренних данных для логирования (потокобезопасно)
|
||||
// Создает копию, чтобы избежать проблем с одновременным доступом
|
||||
std::vector<T> get_logger_data() const noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
// Создаем копию данных для безопасного доступа извне
|
||||
// Это предотвращает data race при одновременной модификации буфера
|
||||
return m_data;
|
||||
}
|
||||
|
||||
// если знаем, что нужно класть весь массив
|
||||
void put(const std::vector<T>& source)
|
||||
{
|
||||
put(source, source.size());
|
||||
}
|
||||
// Пропускает n элементов в хвосте буфера (увеличивает позицию tail)
|
||||
// Возвращает количество фактически пропущенных элементов
|
||||
std::size_t skip(std::size_t n) noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс для потокобезопасности
|
||||
|
||||
std::optional<T> get() noexcept
|
||||
{
|
||||
MUTEX lock(m_mutex);
|
||||
// Нельзя пропустить больше элементов чем есть в буфере
|
||||
std::size_t actual_skip = std::min(n, size());
|
||||
|
||||
if(empty()) return std::nullopt;
|
||||
// Обновляем позицию хвоста с учетом круговой природы буфера
|
||||
m_tail = (m_tail + actual_skip) % m_size;
|
||||
|
||||
auto val = m_data[m_tail];
|
||||
m_tail = (m_tail + 1) % m_size;
|
||||
m_full = false;
|
||||
// Обновляем флаг полноты:
|
||||
// Если буфер был полон и мы пропустили хоть один элемент, он больше не полон
|
||||
m_full = m_full && (actual_skip == 0);
|
||||
|
||||
return val;
|
||||
}
|
||||
return actual_skip; // Возвращаем количество пропущенных элементов
|
||||
}
|
||||
|
||||
void get(std::vector<T>& d, int n)
|
||||
{
|
||||
if (empty()) return;
|
||||
// Проверяет, пуст ли буфер (потокобезопасно)
|
||||
bool empty() const noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс
|
||||
// Буфер пуст если:
|
||||
// 1. Он не полон (m_full == false)
|
||||
// 2. Голова совпадает с хвостом (m_head == m_tail)
|
||||
return !m_full && m_head == m_tail;
|
||||
}
|
||||
|
||||
int c = m_tail;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
d[i] = m_data[c];
|
||||
c = (c + 1) % m_size;
|
||||
}
|
||||
}
|
||||
// Проверяет, полон ли буфер (потокобезопасно)
|
||||
bool full() const noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс
|
||||
return m_full; // Просто возвращаем значение флага
|
||||
}
|
||||
|
||||
std::vector<T>& get_src() const noexcept
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
// Возвращает текущее количество элементов в буфере (потокобезопасно)
|
||||
std::size_t size() const noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс
|
||||
if (m_full) return m_size; // Если буфер полон, размер равен емкости
|
||||
|
||||
void skip(std::size_t n)
|
||||
{
|
||||
m_tail += n;
|
||||
while (m_tail >= m_size) m_tail -= m_size;
|
||||
}
|
||||
// Вычисляем размер в зависимости от относительного положения head и tail
|
||||
if (m_head >= m_tail)
|
||||
// Обычный случай: голова после хвоста в линейной памяти
|
||||
return m_head - m_tail;
|
||||
else
|
||||
// Круговой случай: голова "перескочила" через конец и находится в начале
|
||||
// Вычисляем: общий размер минус "пропуск" от tail до head
|
||||
return m_size - (m_tail - m_head);
|
||||
}
|
||||
|
||||
// Возвращает максимальную вместимость буфера (потокобезопасно)
|
||||
std::size_t capacity() const noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс
|
||||
return m_size; // Просто возвращаем размер буфера
|
||||
}
|
||||
|
||||
bool empty() const noexcept
|
||||
{
|
||||
MUTEX lock(m_mutex);
|
||||
return (!m_full && (m_head == m_tail));
|
||||
}
|
||||
// Сбрасывает буфер в начальное состояние (очищает)
|
||||
// Не освобождает память, только сбрасывает указатели и флаги
|
||||
void reset() noexcept
|
||||
{
|
||||
lock_guard lock(m_mutex); // Захватываем мьютекс
|
||||
m_head = 0; // Полностью сбрасываем позиции в начало
|
||||
m_tail = 0; // Полностью сбрасываем позиции в начало
|
||||
m_full = false; // Сбрасываем флаг полноты
|
||||
// Данные в векторе остаются, но будут перезаписаны при следующих операциях
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept
|
||||
{
|
||||
MUTEX lock(m_mutex);
|
||||
// Очищает буфер, сбрасывая все состояния (потокобезопасно)
|
||||
// Аналогично reset(), но с более понятным именем
|
||||
void clear() noexcept
|
||||
{
|
||||
reset(); // Просто вызываем reset() для consistency
|
||||
}
|
||||
|
||||
std::size_t s;
|
||||
if (!m_full) s = (m_head >= m_tail) ? m_head - m_tail : m_size - (m_tail - m_head);
|
||||
else s = m_size;
|
||||
private:
|
||||
// Внутренний метод для обновления позиции головы после добавления элемента
|
||||
void advance_head() noexcept
|
||||
{
|
||||
// Если буфер полон, сдвигаем хвост (перезаписываем старые данные)
|
||||
// Это реализация поведения "перезаписи старейших данных"
|
||||
if (m_full) m_tail = (m_tail + 1) % m_size;
|
||||
|
||||
return s;
|
||||
}
|
||||
// Сдвигаем голову с учетом круговой природы буфера
|
||||
// % m_size обеспечивает круговое поведение
|
||||
m_head = (m_head + 1) % m_size;
|
||||
// Проверяем, не стал ли буфер полным после добавления
|
||||
// Буфер полон если голова "догнала" хвост
|
||||
m_full = (m_head == m_tail);
|
||||
}
|
||||
|
||||
void reset() noexcept
|
||||
{
|
||||
MUTEX lock(m_mutex);
|
||||
m_head = m_tail;
|
||||
m_full = false;
|
||||
}
|
||||
// Внутренний метод для обновления позиции хвоста после извлечения элемента
|
||||
void advance_tail() noexcept
|
||||
{
|
||||
// Сдвигаем хвост с учетом круговой природы буфера
|
||||
// % m_size обеспечивает круговое поведение
|
||||
m_tail = (m_tail + 1) % m_size;
|
||||
// После извлечения элемента буфер точно не полон
|
||||
// (если он был полон, теперь в нем есть свободное место)
|
||||
m_full = false;
|
||||
}
|
||||
|
||||
bool full() const noexcept
|
||||
{
|
||||
return m_full;
|
||||
}
|
||||
private:
|
||||
// Приватные поля класса:
|
||||
std::size_t m_head = 0; // Индекс для следующей операции записи (голова)
|
||||
std::size_t m_tail = 0; // Индекс для следующей операции чтения (хвост)
|
||||
std::size_t m_size = 0; // Общая емкость буфера (количество элементов)
|
||||
bool m_full = false; // Флаг, указывающий что буфер полностью заполнен
|
||||
|
||||
std::size_t capacity() const noexcept
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
// Мьютекс для обеспечения потокобезопасности
|
||||
// mutable позволяет мьютексу быть изменяемым в const-методах
|
||||
mutable mutex_type m_mutex;
|
||||
|
||||
private:
|
||||
void head_refresh()
|
||||
{
|
||||
if (m_full) m_tail = (m_tail + 1) % m_size;
|
||||
m_head = (m_head + 1) % m_size;
|
||||
m_full = m_head == m_tail;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t m_head = 0;
|
||||
std::size_t m_tail = 0;
|
||||
std::size_t m_size = 0;
|
||||
bool m_full = false;
|
||||
|
||||
private:
|
||||
mutable std::recursive_mutex m_mutex;
|
||||
mutable std::vector<T> m_data;
|
||||
// Вектор для хранения данных буфера
|
||||
// Хранит фактические элементы буфера
|
||||
std::vector<T> m_data;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace hack::utils::color
|
||||
return os << "\033[0m";
|
||||
}
|
||||
|
||||
// вид
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& bold(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
@@ -28,6 +29,13 @@ namespace hack::utils::color
|
||||
return os << "\033[30m";
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& underline(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[4m";
|
||||
}
|
||||
|
||||
// цвета
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& red(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
@@ -69,4 +77,46 @@ namespace hack::utils::color
|
||||
{
|
||||
return os << "\033[37m";
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& gray(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[90m"; // Яркий черный = серый
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& dark_gray(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[30m"; // Темно-серый
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& brown(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[33m"; // Коричневый (желтый)
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& orange(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[38;5;208m"; // Оранжевый (256 цветов)
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& light_blue(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[94m"; // Голубой (яркий синий)
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& violet(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[95m"; // Фиолетовый (яркий пурпурный)
|
||||
}
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT, Traits>& purple(std::basic_ostream<CharT, Traits> &os)
|
||||
{
|
||||
return os << "\033[35m"; // Пурпурный
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user