From 34bc322b9bf1b9ab3598684a698af0720df30aea Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 22 Oct 2025 15:24:10 +0300 Subject: [PATCH 01/53] version 5.5.3 force using '.' instead of ',' in PIString::fromNumber() --- CMakeLists.txt | 2 +- libs/main/text/pistring.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b721fb5..53406e5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ endif() project(PIP) set(PIP_MAJOR 5) set(PIP_MINOR 5) -set(PIP_REVISION 2) +set(PIP_REVISION 3) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index ec09bf65..534bc955 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -206,7 +206,10 @@ PIString PIString::dtos(const double num, char format, int precision) { if (wr > 4) wr = 4; f[2 + wr] = format; f[3 + wr] = 0; - pisprintf(f, num); + char ch[256]; + piZeroMemory(ch, 256); + snprintf(ch, 256, f, num); + return PIStringAscii(ch).replaceAll(',', '.'); } #undef pisprintf From 563d9c5487c45916d21106d00e12e24e9b6ba267 Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 22 Jan 2026 11:48:29 +0300 Subject: [PATCH 02/53] value_tree_translator: add toolTip --- utils/value_tree_translator/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/value_tree_translator/main.cpp b/utils/value_tree_translator/main.cpp index e9c48d98..99246ab0 100644 --- a/utils/value_tree_translator/main.cpp +++ b/utils/value_tree_translator/main.cpp @@ -72,7 +72,7 @@ const PIString contextName = "QAD::PIValueTreeEdit"; void gatherStrings(TSFile::Context & context, const PIValueTree & vt, const PIString & loc) { - const static PIStringList attrs({Attribute::prefix, Attribute::suffix}); + const static PIStringList attrs({Attribute::prefix, Attribute::suffix, Attribute::toolTip}); for (const auto & c: vt.children()) { context.confirm(c.name(), loc); context.confirm(c.comment(), loc); From e22630b1bd72af3d77b161351cf96608b489c4f6 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 3 Feb 2026 18:56:58 +0300 Subject: [PATCH 03/53] version 5.5.4 add PIValueTree::merge PIValueTreeConversions::from*File supports .override files (replace or add values) empty JSON arrays now 0 size --- CMakeLists.txt | 2 +- libs/main/serialization/pijson.cpp | 4 +- .../serialization/pivaluetree_conversions.cpp | 39 ++++++++++++++++--- libs/main/types/pivaluetree.cpp | 17 ++++++++ libs/main/types/pivaluetree.h | 7 ++++ 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53406e5a..c9c68e93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ endif() project(PIP) set(PIP_MAJOR 5) set(PIP_MINOR 5) -set(PIP_REVISION 3) +set(PIP_REVISION 4) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/libs/main/serialization/pijson.cpp b/libs/main/serialization/pijson.cpp index a917f97c..8ef17798 100644 --- a/libs/main/serialization/pijson.cpp +++ b/libs/main/serialization/pijson.cpp @@ -400,9 +400,9 @@ PIJSON PIJSON::parseValue(PIString & s) { s.trim(); if (s.isEmpty()) return ret; if (s[0] == '{') { - ret = parseObject(s.takeRange('{', '}')); + ret = parseObject(s.takeRange('{', '}').trim()); } else if (s[0] == '[') { - ret = parseArray(s.takeRange('[', ']')); + ret = parseArray(s.takeRange('[', ']').trim()); } else { s.trim(); if (s.startsWith('"')) { diff --git a/libs/main/serialization/pivaluetree_conversions.cpp b/libs/main/serialization/pivaluetree_conversions.cpp index f3419650..1a84d15c 100644 --- a/libs/main/serialization/pivaluetree_conversions.cpp +++ b/libs/main/serialization/pivaluetree_conversions.cpp @@ -42,6 +42,14 @@ PIString mask(const PIString & str) { return ret; } +PIString overrideFile(PIString path) { + if (path.isEmpty()) return {}; + PIFile::FileInfo fi(path); + auto ext = fi.extension(); + path.insert(path.size_s() - ext.size_s() - (ext.isEmpty() ? 0 : 1), ".override"); + return path; +} + PIValueTree PIValueTreeConversions::fromPropertyStorage(const PIPropertyStorage & ps) { PIValueTree ret; @@ -292,7 +300,11 @@ PIString toTextTree(const PIValueTree & root, PIString prefix, PIValueTreeConver ret += "\n[" + prefix + "]\n"; if (root.isArray() && options[PIValueTreeConversions::WithAttributes]) ret += toTextTreeAttributes(root, "", options); for (const auto & c: root.children()) { - PIString cp = prefix; + if (c.hasChildren()) continue; + ret += toTextTree(c, prefix, options); + } + for (const auto & c: root.children()) { + if (!c.hasChildren()) continue; ret += toTextTree(c, prefix, options); } } else { @@ -315,9 +327,13 @@ PIString toTextTree(const PIValueTree & root, PIString prefix, PIValueTreeConver PIString PIValueTreeConversions::toText(const PIValueTree & root, Options options) { PIString ret; for (const auto & c: root.children()) { - ret += toTextTree(c, PIString(), options); + if (c.hasChildren()) continue; + ret += toTextTree(c, {}, options); + } + for (const auto & c: root.children()) { + if (!c.hasChildren()) continue; + ret += toTextTree(c, {}, options); } - return ret; } @@ -329,13 +345,26 @@ PIValueTree PIValueTreeConversions::fromText(const PIString & str) { PIValueTree PIValueTreeConversions::fromJSONFile(const PIString & path) { - return PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(path)))); + auto ret = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(path)))); + auto ofp = overrideFile(path); + if (PIFile::isExists(ofp)) { + auto override = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(ofp)))); + ret.merge(override); + } + return ret; } PIValueTree PIValueTreeConversions::fromTextFile(const PIString & path) { PIFile f(path, PIIODevice::ReadOnly); - return PIValueTreeConversions::fromText(&f); + auto ret = PIValueTreeConversions::fromText(&f); + auto ofp = overrideFile(path); + if (PIFile::isExists(ofp)) { + PIFile of(ofp, PIIODevice::ReadOnly); + auto override = PIValueTreeConversions::fromText(&of); + ret.merge(override); + } + return ret; } diff --git a/libs/main/types/pivaluetree.cpp b/libs/main/types/pivaluetree.cpp index 2d292039..b21f6279 100644 --- a/libs/main/types/pivaluetree.cpp +++ b/libs/main/types/pivaluetree.cpp @@ -124,6 +124,23 @@ void PIValueTree::applyValues(const PIValueTree & root, bool recursive) { } +void PIValueTree::merge(const PIValueTree & root) { + if (_is_null) return; + for (const auto & c: root._children) { + bool found = false; + for (auto & i: _children) { + if (c.name() == i.name()) { + if (c.isValid()) i._value = c._value; + i.merge(c); + found = true; + break; + } + } + if (!found) _children << c; + } +} + + PIVariant PIValueTree::childValue(const PIString & child_name, const PIVariant & default_value, bool * exists) const { const PIValueTree & node = child(child_name); if (node.isNull()) { diff --git a/libs/main/types/pivaluetree.h b/libs/main/types/pivaluetree.h index 323a6a9e..d9ee4865 100644 --- a/libs/main/types/pivaluetree.h +++ b/libs/main/types/pivaluetree.h @@ -170,6 +170,13 @@ public: //! \param recursive Если установлено в true, то значения будут применяться рекурсивно к дочерним узлам. void applyValues(const PIValueTree & root, bool recursive = true); + //! \~\brief + //! \~english Set or add the values of a given %PIValueTree object to the current %PIValueTree object. + //! \param root The %PIValueTree object whose values are to be merged. + //! \~russian Устанавливает или добавляет значения данного объекта %PIValueTree к текущему объекту %PIValueTree. + //! \param root Объект %PIValueTree, значения которого должны быть добавлены. + void merge(const PIValueTree & root); + //! \~\brief //! \~english Returns the children of the current %PIValueTree object. //! \~russian Возвращает дочерние элементы текущего объекта %PIValueTree. From 4253acb72b3d8003809976f42da581a56c683d69 Mon Sep 17 00:00:00 2001 From: peri4 Date: Tue, 3 Feb 2026 20:33:23 +0300 Subject: [PATCH 04/53] rename override to override_vt --- libs/main/serialization/pivaluetree_conversions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/main/serialization/pivaluetree_conversions.cpp b/libs/main/serialization/pivaluetree_conversions.cpp index 1a84d15c..2b9f0f14 100644 --- a/libs/main/serialization/pivaluetree_conversions.cpp +++ b/libs/main/serialization/pivaluetree_conversions.cpp @@ -348,8 +348,8 @@ PIValueTree PIValueTreeConversions::fromJSONFile(const PIString & path) { auto ret = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(path)))); auto ofp = overrideFile(path); if (PIFile::isExists(ofp)) { - auto override = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(ofp)))); - ret.merge(override); + auto override_vt = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(PIFile::readAll(ofp)))); + ret.merge(override_vt); } return ret; } @@ -361,8 +361,8 @@ PIValueTree PIValueTreeConversions::fromTextFile(const PIString & path) { auto ofp = overrideFile(path); if (PIFile::isExists(ofp)) { PIFile of(ofp, PIIODevice::ReadOnly); - auto override = PIValueTreeConversions::fromText(&of); - ret.merge(override); + auto override_vt = PIValueTreeConversions::fromText(&of); + ret.merge(override_vt); } return ret; } From 8c15113cb0cc9af13a8cbd039508b73a1ca27790 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sun, 8 Feb 2026 02:50:54 +0300 Subject: [PATCH 05/53] fix PIHTTPClient with queryArguments --- libs/http_client/pihttpclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/http_client/pihttpclient.cpp b/libs/http_client/pihttpclient.cpp index c6486268..ee07034c 100644 --- a/libs/http_client/pihttpclient.cpp +++ b/libs/http_client/pihttpclient.cpp @@ -59,7 +59,7 @@ bool PIHTTPClient::init() { if (is_cancel) return false; CurlThreadPool::instance(); if (!PRIVATE->init()) return false; - auto ait = request.arguments().makeIterator(); + auto ait = request.queryArguments().makeIterator(); while (ait.next()) { if (!url.contains('?')) url.append('?'); From f50a3abc8eb5eb91ede6f294da64b54f02a54533 Mon Sep 17 00:00:00 2001 From: peri4 Date: Sun, 8 Feb 2026 17:01:52 +0300 Subject: [PATCH 06/53] PIString::buildData for ICU fix according to official doc --- libs/main/text/pistring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index 534bc955..db3b9da8 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -469,7 +469,7 @@ void PIString::buildData(const char * cp) const { UErrorCode e((UErrorCode)0); UConverter * cc = ucnv_open(cp, &e); if (cc) { - const size_t len = MB_CUR_MAX * size() + 1; + const size_t len = UCNV_GET_MAX_BYTES_FOR_STRING(size(), ucnv_getMaxCharSize(cc)) + 1; // MB_CUR_MAX * size() + 1; data_ = (char *)malloc(len); int sz = ucnv_fromUChars(cc, data_, len, (const UChar *)(d.data()), d.size_s(), &e); ucnv_close(cc); From 6f1660fd9e64acbc6e0c576c21d305229223d1ef Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 13 Feb 2026 18:47:33 +0300 Subject: [PATCH 07/53] version 5.5.5 fix PIHIDevice enumeration for Windows --- CMakeLists.txt | 2 +- libs/main/system/pihidevice.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9c68e93..88f677c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ endif() project(PIP) set(PIP_MAJOR 5) set(PIP_MINOR 5) -set(PIP_REVISION 4) +set(PIP_REVISION 5) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/libs/main/system/pihidevice.cpp b/libs/main/system/pihidevice.cpp index 78d3f1ff..e1c67818 100644 --- a/libs/main/system/pihidevice.cpp +++ b/libs/main/system/pihidevice.cpp @@ -122,6 +122,10 @@ bool PIHIDevice::open(const PIHIDeviceInfo & device) { return false; } HidD_GetPreparsedData(PRIVATE->deviceHandle, &PRIVATE->preparsed); + if (!PRIVATE->preparsed) { + close(); + return false; + } return true; #endif } @@ -524,6 +528,7 @@ PIVector PIHIDevice::allDevices(bool try_open) { PHIDP_PREPARSED_DATA preparsed = nullptr; if (HidD_GetPreparsedData(deviceHandle, &preparsed) == FALSE) { piCout << "HidD_GetPreparsedData error:" << errorString(); + CloseHandle(deviceHandle); continue; } // auto pp = PIByteArray(preparsed, 64); From 9029bcf099efbe063dd6aff7519f2abbbb657bbb Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Tue, 17 Feb 2026 21:04:40 +0300 Subject: [PATCH 08/53] Vibecoding PIVector2D - add funcs and doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавь в файл pivector2d.h комментарии для Doxygen ко всем классам и всем функциям. Комментарии должны быть в таком же стиле как в файле pivector.h. Проанализируй функциональность классов pivector2d в файле pivector2d.h и класс pivector в файле pivector.h и добавь недостающую функциональность в pivector2d по аналогии с pivector. --- libs/main/containers/pivector2d.h | 626 ++++++++++++++++++-- tests/math/testpivector2d.cpp | 940 ++++++++++++++++++++++++++++-- 2 files changed, 1468 insertions(+), 98 deletions(-) diff --git a/libs/main/containers/pivector2d.h b/libs/main/containers/pivector2d.h index 807a45ae..8f208ef3 100644 --- a/libs/main/containers/pivector2d.h +++ b/libs/main/containers/pivector2d.h @@ -1,7 +1,7 @@ /*! \file pivector2d.h * \brief 2D wrapper around PIVector * - * This file declares PIVector + * This file declares PIVector2D */ /* PIP - Platform Independent Primitives @@ -27,29 +27,82 @@ #include "pivector.h" -/*! \brief 2D array, - * \details This class used to store 2D array of any type elements as plain vector. - * You can read/write any element via operators [][], first dimension - row, second - column. - * The first dimension is Row, and you can operate with Row as PIVector: modify any element, assign to another Row and etc. - * You can't add values to array, but you can modify any elements or create another PIVector2D. - * PIVector2D has constructors from PIVector and PIVector > - */ +//! \addtogroup Containers +//! \{ +//! \class PIVector2D +//! \brief +//! \~english 2D array container. +//! \~russian Двумерный контейнер-массив. +//! \details +//! \~english +//! This class is used to store a 2D array of elements of any type as a single continuous block of memory (a plain PIVector). +//! Elements can be accessed using the `[][]` operators, where the first index is the row and the second is the column. +//! Rows can be manipulated as \a PIVector objects, allowing modification of individual elements or assignment of entire rows. +//! You cannot directly add or remove elements to change the dimensions of the array after construction +//! (use \a resize(), \a addRow(), \a removeRow(), \a removeColumn() instead), but you can modify the values of existing elements. +//! \~russian +//! Этот класс используется для хранения двумерного массива элементов любого типа в виде единого непрерывного блока памяти (обычного +//! PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со +//! строками можно работать как с объектами \a PIVector, что позволяет изменять отдельные элементы или присваивать целые строки. Нельзя +//! напрямую добавлять или удалять элементы, чтобы изменить размеры массива после создания (используйте \a resize(), \a addRow(), \a +//! removeRow(), \a removeColumn() для этого), но можно изменять значения существующих элементов. + template class PIVector2D { public: + //! \~english Constructs an empty 2D array. + //! \~russian Создает пустой двумерный массив. inline PIVector2D() { rows_ = cols_ = 0; } + //! \~english Constructs a 2D array with the given dimensions, filled with copies of `f`. + //! \~russian Создает двумерный массив заданного размера, заполненный копиями `f`. + //! \param rows Number of rows. + //! \param cols Number of columns. + //! \param f Value to fill the array with. + //! \~english \param rows Количество строк. + //! \~russian \param rows Количество строк. + //! \~english \param cols Количество столбцов. + //! \~russian \param cols Количество столбцов. + //! \~english \param f Значение для заполнения массива. + //! \~russian \param f Значение для заполнения массива. inline PIVector2D(size_t rows, size_t cols, const T & f = T()) { rows_ = rows; cols_ = cols; mat.resize(rows * cols, f); } + //! \~english Constructs a 2D array from an existing 1D vector, reshaping it. + //! \~russian Создает двумерный массив из существующего одномерного вектора, изменяя его форму. + //! \param rows Number of rows. + //! \param cols Number of columns. + //! \param v The source 1D vector. Its size must be at least `rows * cols`. + //! \~english \param rows Количество строк. + //! \~russian \param rows Количество строк. + //! \~english \param cols Количество столбцов. + //! \~russian \param cols Количество столбцов. + //! \~english \param v Исходный одномерный вектор. Его размер должен быть не меньше `rows * cols`. + //! \~russian \param v Исходный одномерный вектор. Его размер должен быть не меньше `rows * cols`. inline PIVector2D(size_t rows, size_t cols, const PIVector & v): rows_(rows), cols_(cols), mat(v) { mat.resize(rows * cols); } + //! \~english Move constructs a 2D array from an existing 1D vector, reshaping it. + //! \~russian Конструктор перемещения из существующего одномерного вектора, изменяя его форму. + //! \param rows Number of rows. + //! \param cols Number of columns. + //! \param v The source 1D vector (rvalue reference). Its size must be at least `rows * cols`. + //! \~english \param rows Количество строк. + //! \~russian \param rows Количество строк. + //! \~english \param cols Количество столбцов. + //! \~russian \param cols Количество столбцов. + //! \~english \param v Исходный одномерный вектор (rvalue-ссылка). Его размер должен быть не меньше `rows * cols`. + //! \~russian \param v Исходный одномерный вектор (rvalue-ссылка). Его размер должен быть не меньше `rows * cols`. inline PIVector2D(size_t rows, size_t cols, PIVector && v): rows_(rows), cols_(cols), mat(std::move(v)) { mat.resize(rows * cols); } + //! \~english Constructs a 2D array from a vector of vectors (jagged array). Assumes all inner vectors have the same size. + //! \~russian Создает двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют + //! одинаковый размер. \param v The source vector of vectors. + //! \~english \param v Исходный вектор векторов. + //! \~russian \param v Исходный вектор векторов. inline PIVector2D(const PIVector> & v) { rows_ = v.size(); if (rows_) { @@ -63,22 +116,43 @@ public: if (mat.isEmpty()) rows_ = cols_ = 0; } + //! \~english Number of rows. + //! \~russian Количество строк. inline size_t rows() const { return rows_; } + //! \~english Number of columns. + //! \~russian Количество столбцов. inline size_t cols() const { return cols_; } + //! \~english Total number of elements (`rows * cols`). + //! \~russian Общее количество элементов (`строки * столбцы`). inline size_t size() const { return mat.size(); } + //! \~english Total number of elements as signed value. + //! \~russian Общее количество элементов в виде знакового числа. inline ssize_t size_s() const { return mat.size_s(); } + //! \~english Total number of elements. + //! \~russian Общее количество элементов. inline size_t length() const { return mat.length(); } + //! \~english Number of elements that the underlying container has currently allocated space for. + //! \~russian Количество элементов, для которого сейчас выделена память во внутреннем контейнере. inline size_t capacity() const { return mat.capacity(); } + //! \~english Checks if the array has no elements. + //! \~russian Проверяет, пуст ли массив. inline bool isEmpty() const { return mat.isEmpty(); } + //! \~english Checks if the array has elements. + //! \~russian Проверяет, не пуст ли массив. inline bool isNotEmpty() const { return mat.isNotEmpty(); } + + //! \class Row + //! \brief + //! \~english Proxy class representing a single row in a \a PIVector2D for modification. + //! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D для модификации. class Row { friend class PIVector2D; @@ -91,65 +165,119 @@ public: size_t st_, sz_; public: + //! \~english Size of the row (number of columns). + //! \~russian Размер строки (количество столбцов). inline size_t size() const { return sz_; } + + //! \~english Accesses the element at the given column index within the row. + //! \~russian Доступ к элементу по заданному индексу столбца в строке. inline T & operator[](size_t index) { return (*p_)[st_ + index]; } + + //! \~english Const access to the element at the given column index within the row. + //! \~russian Константный доступ к элементу по заданному индексу столбца в строке. inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; } + + //! \~english Returns a pointer to the row data starting at an optional offset. + //! \~russian Возвращает указатель на данные строки, начиная с опционального смещения. inline T * data(size_t index = 0) { return p_->data(st_ + index); } + + //! \~english Returns a const pointer to the row data starting at an optional offset. + //! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения. inline const T * data(size_t index = 0) const { return p_->data(st_ + index); } + + //! \~english Assigns the contents of another Row to this row. + //! \~russian Присваивает этой строке содержимое другой строки. inline Row & operator=(const Row & other) { if (p_ == other.p_ && st_ == other.st_) return *this; const size_t sz = piMin(sz_, other.sz_); p_->_copyRaw(p_->data(st_), other.data(), sz); return *this; } + + //! \~english Assigns the contents of a \a PIVector to this row. + //! \~russian Присваивает этой строке содержимое \a PIVector. inline Row & operator=(const PIVector & other) { const size_t sz = piMin(sz_, other.size()); p_->_copyRaw(p_->data(st_), other.data(), sz); return *this; } + + //! \~english Converts the row to a \a PIVector. + //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } }; + //! \class Col + //! \brief + //! \~english Proxy class representing a single column in a \a PIVector2D for modification. + //! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D для модификации. class Col { friend class PIVector2D; private: - inline Col(PIVector2D * p, size_t row): p_(&(p->mat)) { + inline Col(PIVector2D * p, size_t col): p_(&(p->mat)) { step_ = p->cols_; - row_ = row; + col_ = col; sz_ = p->rows_; } PIVector * p_; - size_t step_, row_, sz_; + size_t step_, col_, sz_; public: + //! \~english Size of the column (number of rows). + //! \~russian Размер столбца (количество строк). inline size_t size() const { return sz_; } - inline T & operator[](size_t index) { return (*p_)[index * step_ + row_]; } - inline const T & operator[](size_t index) const { return (*p_)[index * step_ + row_]; } - inline T * data(size_t index = 0) { return p_->data(index * step_ + row_); } - inline const T * data(size_t index = 0) const { return p_->data(index * step_ + row_); } + + //! \~english Accesses the element at the given row index within the column. + //! \~russian Доступ к элементу по заданному индексу строки в столбце. + inline T & operator[](size_t index) { return (*p_)[index * step_ + col_]; } + + //! \~english Const access to the element at the given row index within the column. + //! \~russian Константный доступ к элементу по заданному индексу строки в столбце. + inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; } + + //! \~english Returns a pointer to the column data starting at an optional row offset. + //! \~russian Возвращает указатель на данные столбца, начиная с опционального смещения по строкам. + inline T * data(size_t index = 0) { return p_->data(index * step_ + col_); } + + //! \~english Returns a const pointer to the column data starting at an optional row offset. + //! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам. + inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); } + + //! \~english Assigns the contents of another Col to this column. + //! \~russian Присваивает этому столбцу содержимое другого столбца. inline Col & operator=(const Col & other) { - if (p_ == other.p_ && row_ == other.row_) return *this; + if (p_ == other.p_ && col_ == other.col_) return *this; const size_t sz = piMin(sz_, other.sz_); - for (int i = 0; i < sz; ++i) - (*p_)[i * step_ + row_] = other[i]; + for (size_t i = 0; i < sz; ++i) + (*p_)[i * step_ + col_] = other[i]; return *this; } - inline Row & operator=(const PIVector & other) { + + //! \~english Assigns the contents of a \a PIVector to this column. + //! \~russian Присваивает этому столбцу содержимое \a PIVector. + inline Col & operator=(const PIVector & other) { const size_t sz = piMin(sz_, other.size()); - for (int i = 0; i < sz; ++i) - (*p_)[i * step_ + row_] = other[i]; + for (size_t i = 0; i < sz; ++i) + (*p_)[i * step_ + col_] = other[i]; return *this; } + + //! \~english Converts the column to a \a PIVector. + //! \~russian Преобразует столбец в \a PIVector. inline PIVector toVector() const { PIVector ret; ret.reserve(sz_); for (size_t i = 0; i < sz_; i++) - ret << (*p_)[i * step_ + row_]; + ret << (*p_)[i * step_ + col_]; return ret; } }; + //! \class RowConst + //! \brief + //! \~english Proxy class representing a single read-only row in a \a PIVector2D. + //! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D только для чтения. class RowConst { friend class PIVector2D; @@ -162,64 +290,135 @@ public: size_t st_, sz_; public: + //! \~english Size of the row (number of columns). + //! \~russian Размер строки (количество столбцов). inline size_t size() const { return sz_; } + + //! \~english Const access to the element at the given column index within the row. + //! \~russian Константный доступ к элементу по заданному индексу столбца в строке. inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; } + + //! \~english Returns a const pointer to the row data starting at an optional offset. + //! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения. inline const T * data(size_t index = 0) const { return p_->data(st_ + index); } + + //! \~english Converts the row to a \a PIVector. + //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } }; + //! \class ColConst + //! \brief + //! \~english Proxy class representing a single read-only column in a \a PIVector2D. + //! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D только для чтения. class ColConst { friend class PIVector2D; private: - inline ColConst(const PIVector2D * p, size_t row): p_(&(p->mat)) { + inline ColConst(const PIVector2D * p, size_t col): p_(&(p->mat)) { step_ = p->cols_; - row_ = row; + col_ = col; sz_ = p->rows_; } const PIVector * p_; - size_t step_, row_, sz_; + size_t step_, col_, sz_; public: - inline size_t size() const { return p_->rows_; } - inline const T & operator[](size_t index) const { return (*p_)[index * step_ + row_]; } - inline const T * data(size_t index = 0) const { return p_->data(index * step_ + row_); } + //! \~english Size of the column (number of rows). + //! \~russian Размер столбца (количество строк). + inline size_t size() const { return sz_; } + + //! \~english Const access to the element at the given row index within the column. + //! \~russian Константный доступ к элементу по заданному индексу строки в столбце. + inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; } + + //! \~english Returns a const pointer to the column data starting at an optional row offset. + //! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам. + inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); } + + //! \~english Converts the column to a \a PIVector. + //! \~russian Преобразует столбец в \a PIVector. inline PIVector toVector() const { PIVector ret; ret.reserve(sz_); - for (int i = 0; i < size(); i++) - ret << (*p_)[i * step_ + row_]; + for (size_t i = 0; i < size(); i++) + ret << (*p_)[i * step_ + col_]; return ret; } }; + //! \~english Returns a reference to the element at the given row and column. + //! \~russian Возвращает ссылку на элемент по заданной строке и столбцу. inline T & element(size_t row, size_t col) { return mat[row * cols_ + col]; } + + //! \~english Returns a const reference to the element at the given row and column. + //! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу. inline const T & element(size_t row, size_t col) const { return mat[row * cols_ + col]; } + + //! \~english Returns a const reference to the element at the given row and column (bounds-checked only in debug). + //! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу (проверка границ только в отладочном режиме). inline const T & at(size_t row, size_t col) const { return mat[row * cols_ + col]; } + + //! \~english Returns a proxy object for the row at the given index for modification. + //! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации. inline Row operator[](size_t index) { return Row(this, index); } + + //! \~english Returns a proxy object for the row at the given index for read-only access. + //! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения. inline RowConst operator[](size_t index) const { return RowConst(this, index); } + + //! \~english Returns a pointer to the underlying flat data starting at an optional offset. + //! \~russian Возвращает указатель на внутренние плоские данные, начиная с опционального смещения. inline T * data(size_t index = 0) { return mat.data(index); } + + //! \~english Returns a const pointer to the underlying flat data starting at an optional offset. + //! \~russian Возвращает константный указатель на внутренние плоские данные, начиная с опционального смещения. inline const T * data(size_t index = 0) const { return mat.data(index); } + + //! \~english Returns a proxy object for the row at the given index for modification. + //! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации. inline Row row(size_t index) { return Row(this, index); } + + //! \~english Returns a proxy object for the row at the given index for read-only access. + //! \~russian Возвращает прокси-объект для строки по заданному индексу только для чтения. inline RowConst row(size_t index) const { return RowConst(this, index); } + + //! \~english Returns a proxy object for the column at the given index for modification. + //! \~russian Возвращает прокси-объект для столбца по заданному индексу для модификации. inline Col col(size_t index) { return Col(this, index); } + + //! \~english Returns a proxy object for the column at the given index for read-only access. + //! \~russian Возвращает прокси-объект для столбца по заданному индексу только для чтения. inline ColConst col(size_t index) const { return ColConst(this, index); } + + + //! \~english Replaces a row with the contents of another Row object. + //! \~russian Заменяет строку содержимым другого объекта Row. inline PIVector2D & setRow(size_t row, const Row & other) { const size_t sz = piMin(cols_, other.sz_); mat._copyRaw(mat.data(cols_ * row), other.data(), sz); return *this; } + + //! \~english Replaces a row with the contents of a read-only RowConst object. + //! \~russian Заменяет строку содержимым объекта RowConst только для чтения. inline PIVector2D & setRow(size_t row, const RowConst & other) { const size_t sz = piMin(cols_, other.sz_); mat._copyRaw(mat.data(cols_ * row), other.data(), sz); return *this; } + + //! \~english Replaces a row with the contents of a \a PIVector. + //! \~russian Заменяет строку содержимым \a PIVector. inline PIVector2D & setRow(size_t row, const PIVector & other) { const size_t sz = piMin(cols_, other.size()); mat._copyRaw(mat.data(cols_ * row), other.data(), sz); return *this; } + + //! \~english Appends a new row to the bottom of the array from another Row object. + //! \~russian Добавляет новую строку в конец массива из другого объекта Row. inline PIVector2D & addRow(const Row & other) { if (cols_ == 0) cols_ = other.sz_; const size_t sz = piMin(cols_, other.sz_); @@ -229,6 +428,9 @@ public: rows_++; return *this; } + + //! \~english Appends a new row to the bottom of the array from a read-only RowConst object. + //! \~russian Добавляет новую строку в конец массива из объекта RowConst только для чтения. inline PIVector2D & addRow(const RowConst & other) { if (cols_ == 0) cols_ = other.sz_; const size_t sz = piMin(cols_, other.sz_); @@ -238,6 +440,9 @@ public: rows_++; return *this; } + + //! \~english Appends a new row to the bottom of the array from a \a PIVector. + //! \~russian Добавляет новую строку в конец массива из \a PIVector. inline PIVector2D & addRow(const PIVector & other) { if (cols_ == 0) cols_ = other.size(); const size_t sz = piMin(cols_, other.size()); @@ -248,34 +453,40 @@ public: return *this; } + //! \~english Resizes the 2D array to new dimensions. + //! \~russian Изменяет размер двумерного массива. + //! \details + //! \~english If the new dimensions are larger, new elements are filled with `f`. + //! If they are smaller, the array is truncated. + //! \~russian Если новые размеры больше, новые элементы заполняются `f`. + //! Если они меньше, массив обрезается. inline PIVector2D & resize(size_t rows, size_t cols, const T & f = T()) { - mat.resize(rows * cols_, f); - rows_ = rows; - const int cs = (cols - cols_); - if (cs < 0) { - for (size_t r = 0; r < rows; ++r) { - mat.remove(r * cols + cols, -cs); + if (rows == rows_ && cols == cols_) return *this; + PIVector2D tmp(rows, cols, f); + size_t copyRows = piMin(rows_, rows); + size_t copyCols = piMin(cols_, cols); + for (size_t r = 0; r < copyRows; ++r) { + for (size_t c = 0; c < copyCols; ++c) { + tmp.element(r, c) = element(r, c); } } - mat.resize(rows * cols, f); - if (!mat.isEmpty()) { - if (cs > 0) { - for (size_t r = 0; r < rows_; ++r) { - for (int i = 0; i < cs; ++i) - mat.insert(r * cols + cols_, mat.take_back()); - } - } - } - cols_ = cols; + swap(tmp); return *this; } + //! \~english Equality operator. + //! \~russian Оператор равенства. inline bool operator==(const PIVector2D & t) const { if (cols_ != t.cols_ || rows_ != t.rows_) return false; return mat == t.mat; } + + //! \~english Inequality operator. + //! \~russian Оператор неравенства. inline bool operator!=(const PIVector2D & t) const { return !(*this == t); } + //! \~english Converts the 2D array to a vector of vectors (PIVector>). + //! \~russian Преобразует двумерный массив в вектор векторов (PIVector>). inline PIVector> toVectors() const { PIVector> ret; ret.reserve(rows_); @@ -284,18 +495,27 @@ public: return ret; } + //! \~english Returns a const reference to the underlying flat \a PIVector. + //! \~russian Возвращает константную ссылку на внутренний плоский \a PIVector. + inline const PIVector & asPlainVector() const { return mat; } + + //! \~english Returns a reference to the underlying flat \a PIVector. + //! \~russian Возвращает ссылку на внутренний плоский \a PIVector. + inline PIVector & asPlainVector() { return mat; } + + //! \~english Returns a copy of the underlying flat \a PIVector. + //! \~russian Возвращает копию внутреннего плоского \a PIVector. inline PIVector toPlainVector() const { return mat; } - inline PIVector & plainVector() { return mat; } - - inline const PIVector & plainVector() const { return mat; } - + //! \~english Swaps this 2D array with another. + //! \~russian Меняет местами этот двумерный массив с другим. inline void swap(PIVector2D & other) { mat.swap(other.mat); piSwap(rows_, other.rows_); piSwap(cols_, other.cols_); } + //! \internal template::value, int>::type = 0> inline PIVector2D & _resizeRaw(size_t r, size_t c) { rows_ = r; @@ -304,29 +524,326 @@ public: return *this; } + //! \~english Clears the array, removing all elements and setting dimensions to 0. + //! \~russian Очищает массив, удаляя все элементы и устанавливая размеры в 0. inline void clear() { rows_ = cols_ = 0; mat.clear(); } - template - inline PIVector2D map(std::function f) const { - return PIVector2D(rows_, cols_, mat.map(f)); + + //! \~english Checks if the underlying flat vector contains the element `e`. + //! \~russian Проверяет, содержит ли внутренний плоский вектор элемент `e`. + inline bool contains(const T & e, ssize_t start = 0) const { return mat.contains(e, start); } + + //! \~english Checks if the underlying flat vector contains all elements of `v`. + //! \~russian Проверяет, содержит ли внутренний плоский вектор все элементы `v`. + inline bool contains(const PIVector & v, ssize_t start = 0) const { return mat.contains(v, start); } + + //! \~english Counts occurrences of `e` in the underlying flat vector. + //! \~russian Подсчитывает количество вхождений `e` во внутреннем плоском векторе. + inline int entries(const T & e, ssize_t start = 0) const { return mat.entries(e, start); } + + //! \~english Counts elements in the flat vector that pass the `test`. + //! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`. + inline int entries(std::function test, ssize_t start = 0) const { return mat.entries(test, start); } + + + // TODO: next 4 functions implement in to RowConst and ColConst and rewtite thisto return index as PIPair + //! \~english Returns the first index of `e` in the flat vector. + //! \~russian Возвращает первый индекс `e` в плоском векторе. + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { return mat.indexOf(e, start); } + //! \~english Returns the first index in the flat vector that passes the `test`. + //! \~russian Возвращает первый индекс в плоском векторе, проходящий `test`. + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { return mat.indexWhere(test, start); } + //! \~english Returns the last index of `e` in the flat vector. + //! \~russian Возвращает последний индекс `e` в плоском векторе. + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { return mat.lastIndexOf(e, start); } + //! \~english Returns the last index in the flat vector that passes the `test`. + //! \~russian Возвращает последний индекс в плоском векторе, проходящий `test`. + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + return mat.lastIndexWhere(test, start); } + //! \~english Tests if any element in the flat vector passes the `test`. + //! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`. + inline bool any(std::function test) const { return mat.any(test); } + + //! \~english Tests if all elements in the flat vector pass the `test`. + //! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`. + inline bool every(std::function test) const { return mat.every(test); } + + //! \~english Fills the entire 2D array with copies of `e`. + //! \~russian Заполняет весь двумерный массив копиями `e`. + inline PIVector2D & fill(const T & e = T()) { + mat.fill(e); + return *this; + } + + //! \~english Fills the entire 2D array using a generator function `f` based on flat index. + //! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса. + inline PIVector2D & fill(std::function f) { + mat.fill(f); + return *this; + } + + //! \~english Same as \a fill(). + //! \~russian То же, что и \a fill(). + inline PIVector2D & assign(const T & e = T()) { return fill(e); } + + + // TODO: rewrite with size_t rows, size_t cols arguments + //! \~english Assigns a new size to the underlying flat vector and resets the 2D structure to 1 row. + //! \~russian Присваивает новый размер внутреннему плоскому вектору и сбрасывает 2D-структуру до 1 строки. + inline PIVector2D & assign(size_t new_size, const T & f) { + mat.assign(new_size, f); + if (mat.isEmpty()) { + rows_ = cols_ = 0; + } else { + rows_ = 1; + cols_ = mat.size(); + } + return *this; + } + + // TODO: fix for different rows and cols count + //! \~english Returns a transposed 2D array (rows become columns and vice versa). + //! \~russian Возвращает транспонированный двумерный массив (строки становятся столбцами и наоборот). + inline PIVector2D transposed() const { + if (isEmpty()) return PIVector2D(); + PIVector2D result(cols_, rows_); + for (size_t r = 0; r < rows_; ++r) { + for (size_t c = 0; c < cols_; ++c) { + result.element(c, r) = element(r, c); + } + } + return result; + } + + // TODO: переписать по возможности избегая копирования данных, в идеале использовать piSwap + //! \~english Reverses the order of rows in place. + //! \~russian Изменяет порядок строк на обратный на месте. + inline PIVector2D & reverseRows() { + const size_t half = rows_ / 2; + for (size_t i = 0; i < half; ++i) { + Row r1 = row(i); + Row r2 = row(rows_ - 1 - i); + PIVector temp = r1.toVector(); + r1 = r2.toVector(); + r2 = temp; + } + return *this; + } + + //! \~english Reverses the order of columns in each row in place. + //! \~russian Изменяет порядок столбцов в каждой строке на обратный на месте. + inline PIVector2D & reverseColumns() { + for (size_t r = 0; r < rows_; ++r) { + Row currentRow = row(r); + const size_t half = cols_ / 2; + for (size_t c = 0; c < half; ++c) { + piSwap(currentRow[c], currentRow[cols_ - 1 - c]); + } + } + return *this; + } + + //! \~english Returns a sub-2D array (a range of rows and columns). + //! \~russian Возвращает подмассив (диапазон строк и столбцов). + inline PIVector2D getRange(size_t rowStart, size_t rowCount, size_t colStart, size_t colCount) const { + if (rowStart >= rows_ || colStart >= cols_ || rowCount == 0 || colCount == 0) return PIVector2D(); + size_t actualRowCount = piMin(rowCount, rows_ - rowStart); + size_t actualColCount = piMin(colCount, cols_ - colStart); + + PIVector2D result(actualRowCount, actualColCount); + for (size_t r = 0; r < actualRowCount; ++r) { + for (size_t c = 0; c < actualColCount; ++c) { + result.element(r, c) = element(rowStart + r, colStart + c); + } + } + return result; + } + + //! \~english Applies a function to each element and returns a new 2D array of a different type. + //! \~russian Применяет функцию к каждому элементу и возвращает новый двумерный массив другого типа. + template + inline PIVector2D map(std::function f) const { + return PIVector2D(rows_, cols_, mat.template map(f)); + } + + //! \~english Applies a function (with row and col indices) to each element and returns a new 2D array. + //! \~russian Применяет функцию (с индексами строки и столбца) к каждому элементу и возвращает новый двумерный массив. + template + inline PIVector2D mapIndexed(std::function f) const { + PIVector mappedMat; + mappedMat.reserve(size()); + for (size_t r = 0; r < rows_; ++r) { + for (size_t c = 0; c < cols_; ++c) { + mappedMat << f(r, c, element(r, c)); + } + } + return PIVector2D(rows_, cols_, std::move(mappedMat)); + } + + + /* + //! \~english Executes a read-only function for each element. + //! \~russian Выполняет функцию только для чтения для каждого элемента. inline void forEach(std::function f) const { mat.forEach(f); } + //! \~english Executes a function for each element, allowing modification. + //! \~russian Выполняет функцию для каждого элемента, позволяя их изменять. inline PIVector2D & forEach(std::function f) { - mat.forEach(f); + mat.forEach(f); + return *this; + } + + //! \~english Executes a read-only function (with row and col indices) for each element. + //! \~russian Выполняет функцию только для чтения (с индексами строки и столбца) для каждого элемента. + inline void forEachIndexed(std::function f) const { + for (size_t r = 0; r < rows_; ++r) { + for (size_t c = 0; c < cols_; ++c) { + f(r, c, element(r, c)); + } + } + } + + //! \~english Executes a function (with row and col indices) for each element, allowing modification. + //! \~russian Выполняет функцию (с индексами строки и столбца) для каждого элемента, позволяя их изменять. + inline PIVector2D & forEachIndexed(std::function f) { + for (size_t r = 0; r < rows_; ++r) { + for (size_t c = 0; c < cols_; ++c) { + f(r, c, element(r, c)); + } + } + return *this; + } + */ + // TODO: Переделать закомментаренные выше функции на функции forEachRow, forEachColumn + + + //! \~english Accumulates a value across all elements. + //! \~russian Аккумулирует значение по всем элементам. + template + inline ST reduce(std::function f, const ST & initial = ST()) const { + return mat.template reduce(f, initial); + } + + //! \~english Accumulates a value across all elements with indices. + //! \~russian Аккумулирует значение по всем элементам с индексами. + template + inline ST reduceIndexed(std::function f, const ST & initial = ST()) const { + ST ret(initial); + for (size_t r = 0; r < rows_; ++r) { + for (size_t c = 0; c < cols_; ++c) { + ret = f(r, c, element(r, c), ret); + } + } + return ret; + } + + //! \~english Removes a row from the 2D array. + //! \~russian Удаляет строку из двумерного массива. + inline PIVector2D & removeRow(size_t row) { + if (row >= rows_) return *this; + size_t startIdx = row * cols_; + mat.remove(startIdx, cols_); + rows_--; + if (rows_ == 0) cols_ = 0; return *this; } + //! \~english Removes a column from the 2D array. + //! \~russian Удаляет столбец из двумерного массива. + inline PIVector2D & removeColumn(size_t col) { + if (col >= cols_ || rows_ == 0) return *this; + PIVector2D result(rows_, cols_ - 1); + for (size_t r = 0; r < rows_; ++r) { + for (size_t c = 0, nc = 0; c < cols_; ++c) { + if (c == col) continue; + result.element(r, nc++) = element(r, c); + } + } + swap(result); + return *this; + } + + //! \~english Removes all rows that satisfy a condition. + //! \~russian Удаляет все строки, удовлетворяющие условию. + inline PIVector2D & removeRowsWhere(std::function test) { + ssize_t r = rows_ - 1; + while (r >= 0) { + if (test(RowConst(this, r))) { + removeRow(r); + } + --r; + } + return *this; + } + + //! \~english Removes all columns that satisfy a condition. + //! \~russian Удаляет все столбцы, удовлетворяющие условию. + inline PIVector2D & removeColumnsWhere(std::function test) { + ssize_t c = cols_ - 1; + while (c >= 0) { + if (test(ColConst(this, c))) { + removeColumn(c); + } + --c; + } + return *this; + } + + + //! \~english Returns a new 2D array containing only the rows that pass the test. + //! \~russian Возвращает новый двумерный массив, содержащий только строки, прошедшие проверку. + inline PIVector2D filterRows(std::function test) const { + PIVector2D result; + for (size_t r = 0; r < rows_; ++r) { + RowConst currentRow = row(r); + if (test(currentRow)) { + result.addRow(currentRow); + } + } + return result; + } + + //! \~english Returns a new 2D array containing only the columns that pass the test. + //! \~russian Возвращает новый двумерный массив, содержащий только столбцы, прошедшие проверку. + inline PIVector2D filterColumns(std::function test) const { + if (isEmpty()) return PIVector2D(); + PIVector goodCols; + for (size_t c = 0; c < cols_; ++c) { + if (test(col(c))) { + goodCols << c; + } + } + PIVector2D result(rows_, goodCols.size()); + for (size_t r = 0; r < rows_; ++r) { + for (size_t gc = 0; gc < goodCols.size(); ++gc) { + result.element(r, gc) = element(r, goodCols[gc]); + } + } + return result; + } + + //! \~english Returns a new 2D array (as a single row) containing only the elements that pass the test. + //! \~russian Возвращает новый двумерный массив (в виде одной строки), содержащий только элементы, прошедшие проверку. + inline PIVector2D filterElements(std::function test) const { + PIVector filtered = mat.filter(test); + if (filtered.isEmpty()) return PIVector2D(); + return PIVector2D(1, filtered.size(), filtered); + } + protected: size_t rows_, cols_; PIVector mat; }; +//! \relatesalso PICout +//! \~english Output operator for \a PIVector2D to \a PICout. +//! \~russian Оператор вывода \a PIVector2D в \a PICout. template inline PICout operator<<(PICout s, const PIVector2D & v) { s.saveAndSetControls(0); @@ -346,5 +863,6 @@ inline PICout operator<<(PICout s, const PIVector2D & v) { return s; } +//! \} #endif // PIVECTOR2D_H diff --git a/tests/math/testpivector2d.cpp b/tests/math/testpivector2d.cpp index 12718555..357c2383 100644 --- a/tests/math/testpivector2d.cpp +++ b/tests/math/testpivector2d.cpp @@ -1,85 +1,937 @@ +#include "pistring.h" #include "pivector2d.h" #include "gtest/gtest.h" +#include -int ROWS_COUNT_INIT = 31; +size_t ROWS_COUNT_INIT = 31; +size_t COLS_COUNT_INIT = 34; int ROWS_COUNT_INCREASE = 41; -int ROWS_COUNT_REDUCE = 22; - -int COLS_COUNT_INIT = 34; int COLS_COUNT_INCREASE = 44; +int ROWS_COUNT_REDUCE = 22; int COLS_COUNT_REDUCE = 13; -void assert_fill_with(PIVector2D vec, int rows, int cols) { - for (int r = 0; r < rows; r++) { - for (int c = 0; c < cols; c++) { - ASSERT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c); +void fill_with_sequential(PIVector2D & vec, int rows, int cols) { + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + vec.element(r, c) = r * cols + c; } } } -class Vector2D: public ::testing::Test { +void assert_fill_with_sequential(const PIVector2D & vec, int rows, int cols) { + ASSERT_EQ(vec.rows(), rows); + ASSERT_EQ(vec.cols(), cols); + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + ASSERT_EQ(vec.element(r, c), r * cols + c); + } + } +} + +class Vector2DTest: public ::testing::Test { protected: PIVector2D vec = PIVector2D(ROWS_COUNT_INIT, COLS_COUNT_INIT); - void SetUp() override { - for (int r = 0; r < ROWS_COUNT_INIT; ++r) { - for (int c = 0; c < COLS_COUNT_INIT; ++c) { - vec.element(r, c) = r * COLS_COUNT_INIT + c; + void SetUp() override { fill_with_sequential(vec, ROWS_COUNT_INIT, COLS_COUNT_INIT); } +}; + +// ==================== CONSTRUCTOR TESTS ==================== +TEST_F(Vector2DTest, defaultConstructor_createsEmptyVector) { + PIVector2D emptyVec; + EXPECT_TRUE(emptyVec.isEmpty()); + EXPECT_EQ(emptyVec.rows(), 0); + EXPECT_EQ(emptyVec.cols(), 0); + EXPECT_EQ(emptyVec.size(), 0); +} + +TEST_F(Vector2DTest, sizedConstructor_createsCorrectDimensions) { + PIVector2D testVec(5, 3, 42); + EXPECT_EQ(testVec.rows(), 5); + EXPECT_EQ(testVec.cols(), 3); + EXPECT_EQ(testVec.size(), 15); + + for (size_t r = 0; r < 5; ++r) { + for (size_t c = 0; c < 3; ++c) { + EXPECT_EQ(testVec.element(r, c), 42); + } + } +} + +TEST_F(Vector2DTest, fromPlainVector_constructor_reshapesCorrectly) { + PIVector plain(20); + std::iota(plain.data(), plain.data() + 20, 0); + + PIVector2D vec2d(4, 5, plain); + EXPECT_EQ(vec2d.rows(), 4); + EXPECT_EQ(vec2d.cols(), 5); + + for (size_t r = 0; r < 4; ++r) { + for (size_t c = 0; c < 5; ++c) { + EXPECT_EQ(vec2d.element(r, c), static_cast(r * 5 + c)); + } + } +} + +TEST_F(Vector2DTest, fromPlainVector_move_constructor_reshapesCorrectly) { + PIVector plain(20); + std::iota(plain.data(), plain.data() + 20, 0); + + PIVector2D vec2d(4, 5, std::move(plain)); + EXPECT_EQ(vec2d.rows(), 4); + EXPECT_EQ(vec2d.cols(), 5); + EXPECT_TRUE(plain.isEmpty()); // Moved-from state + + for (size_t r = 0; r < 4; ++r) { + for (size_t c = 0; c < 5; ++c) { + EXPECT_EQ(vec2d.element(r, c), static_cast(r * 5 + c)); + } + } +} + +TEST_F(Vector2DTest, fromVectorOfVectors_constructor_reshapesCorrectly) { + PIVector> vectors; + vectors << PIVector({1, 2, 3}) << PIVector({4, 5, 6}) << PIVector({7, 8, 9}); + + PIVector2D vec2d(vectors); + EXPECT_EQ(vec2d.rows(), 3); + EXPECT_EQ(vec2d.cols(), 3); + + EXPECT_EQ(vec2d.element(0, 0), 1); + EXPECT_EQ(vec2d.element(1, 1), 5); + EXPECT_EQ(vec2d.element(2, 2), 9); +} + +// ==================== CAPACITY TESTS ==================== +TEST_F(Vector2DTest, sizeMethods_returnCorrectValues) { + EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT); + EXPECT_EQ(vec.cols(), COLS_COUNT_INIT); + EXPECT_EQ(vec.size(), ROWS_COUNT_INIT * COLS_COUNT_INIT); + EXPECT_EQ(vec.size_s(), static_cast(ROWS_COUNT_INIT * COLS_COUNT_INIT)); + EXPECT_EQ(vec.length(), ROWS_COUNT_INIT * COLS_COUNT_INIT); + EXPECT_FALSE(vec.isEmpty()); + EXPECT_TRUE(vec.isNotEmpty()); + EXPECT_GE(vec.capacity(), vec.size()); +} + +// ==================== ELEMENT ACCESS TESTS ==================== +TEST_F(Vector2DTest, element_access_returnsCorrectValues) { + EXPECT_EQ(vec.element(5, 7), 5 * COLS_COUNT_INIT + 7); + EXPECT_EQ(vec.at(10, 20), 10 * COLS_COUNT_INIT + 20); + + vec.element(15, 15) = 999; + EXPECT_EQ(vec.element(15, 15), 999); +} + +TEST_F(Vector2DTest, row_proxy_allows_elementAccess) { + auto row = vec[5]; + EXPECT_EQ(row.size(), COLS_COUNT_INIT); + EXPECT_EQ(row[7], 5 * COLS_COUNT_INIT + 7); + + row[10] = 123; + EXPECT_EQ(vec.element(5, 10), 123); +} + +TEST_F(Vector2DTest, row_proxy_data_pointer_works) { + auto row = vec[10]; + int * ptr = row.data(); + EXPECT_EQ(ptr, vec.data(10 * COLS_COUNT_INIT)); + + ptr[5] = 777; + EXPECT_EQ(vec.element(10, 5), 777); +} + +TEST_F(Vector2DTest, row_proxy_const_access_works) { + const auto & constVec = vec; + auto row = constVec[5]; + EXPECT_EQ(row[7], 5 * COLS_COUNT_INIT + 7); + + // Compilation test - uncommenting should fail + // row[10] = 123; +} + +TEST_F(Vector2DTest, row_proxy_assignment_works) { + PIVector2D other(ROWS_COUNT_INIT, COLS_COUNT_INIT, 42); + + vec[10] = other[10]; + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(10, c), 42); + } + + PIVector newRow(COLS_COUNT_INIT, 99); + vec[15] = newRow; + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(15, c), 99); + } +} + +TEST_F(Vector2DTest, row_proxy_toVector_conversion_works) { + auto rowVec = vec[7].toVector(); + EXPECT_EQ(rowVec.size(), COLS_COUNT_INIT); + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(rowVec[c], vec.element(7, c)); + } +} + +TEST_F(Vector2DTest, col_proxy_allows_elementAccess) { + auto col = vec.col(5); + EXPECT_EQ(col.size(), ROWS_COUNT_INIT); + EXPECT_EQ(col[10], 10 * COLS_COUNT_INIT + 5); + + col[15] = 456; + EXPECT_EQ(vec.element(15, 5), 456); +} + +TEST_F(Vector2DTest, col_proxy_data_pointer_works) { + auto col = vec.col(8); + int * ptr = col.data(5); // Start from row 5 + EXPECT_EQ(ptr, &vec.element(5, 8)); + + col[2] = 888; // This should affect row 7 + EXPECT_EQ(vec.element(2, 8), 888); +} + +TEST_F(Vector2DTest, col_proxy_assignment_works) { + PIVector2D other(ROWS_COUNT_INIT, COLS_COUNT_INIT, 42); + + vec.col(12) = other.col(12); + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(vec.element(r, 12), 42); + } + + PIVector newCol(ROWS_COUNT_INIT, 77); + vec.col(20) = newCol; + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(vec.element(r, 20), 77); + } +} + +TEST_F(Vector2DTest, col_proxy_toVector_conversion_works) { + auto colVec = vec.col(9).toVector(); + EXPECT_EQ(colVec.size(), ROWS_COUNT_INIT); + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(colVec[r], vec.element(r, 9)); + } +} + +TEST_F(Vector2DTest, row_and_col_methods_return_same_as_operator) { + auto row1 = vec.row(10); + auto row2 = vec[10]; + EXPECT_EQ(row1[0], row2[0]); + + auto col1 = vec.col(15); + auto col2 = vec.col(15); // No operator[] for col + EXPECT_EQ(col1[5], col2[5]); +} + +// ==================== MODIFIER TESTS ==================== +TEST_F(Vector2DTest, setRow_replaces_row_correctly) { + PIVector newRow(COLS_COUNT_INIT); + std::iota(newRow.data(), newRow.data() + COLS_COUNT_INIT, 100); + + vec.setRow(12, newRow); + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(12, c), static_cast(100 + c)); + } +} + +TEST_F(Vector2DTest, setRow_with_shorter_vector_truncates) { + PIVector shortRow(COLS_COUNT_INIT - 5, 999); + vec.setRow(8, shortRow); + + for (size_t c = 0; c < COLS_COUNT_INIT - 5; ++c) { + EXPECT_EQ(vec.element(8, c), 999); + } + // Rest unchanged + EXPECT_EQ(vec.element(8, COLS_COUNT_INIT - 5), 8 * COLS_COUNT_INIT + COLS_COUNT_INIT - 5); +} + +TEST_F(Vector2DTest, addRow_appends_row_to_empty) { + PIVector2D empty; + PIVector newRow(5, 42); + + empty.addRow(newRow); + EXPECT_EQ(empty.rows(), 1); + EXPECT_EQ(empty.cols(), 5); + for (size_t c = 0; c < 5; ++c) { + EXPECT_EQ(empty.element(0, c), 42); + } +} + +TEST_F(Vector2DTest, addRow_appends_row_to_existing) { + size_t oldRows = vec.rows(); + PIVector newRow(COLS_COUNT_INIT, 999); + + vec.addRow(newRow); + EXPECT_EQ(vec.rows(), oldRows + 1); + EXPECT_EQ(vec.cols(), COLS_COUNT_INIT); + + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(oldRows, c), 999); + } +} + +TEST_F(Vector2DTest, addRow_with_shorter_vector_uses_min) { + size_t oldRows = vec.rows(); + size_t shortCols = COLS_COUNT_INIT - 10; + PIVector shortRow(shortCols, 777); + + vec.addRow(shortRow); + EXPECT_EQ(vec.rows(), oldRows + 1); + EXPECT_EQ(vec.cols(), COLS_COUNT_INIT); // cols unchanged + + for (size_t c = 0; c < shortCols; ++c) { + EXPECT_EQ(vec.element(oldRows, c), 777); + } + for (size_t c = shortCols; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(oldRows, c), 0); // default initialized + } +} + +// ==================== RESIZE TESTS ==================== +class Vector2DResizeTest: public Vector2DTest { +protected: + void assert_resize_reduce_preserves_data(int newRows, int newCols) { + vec.resize(newRows, newCols, 0); + ASSERT_EQ(vec.rows(), newRows); + ASSERT_EQ(vec.cols(), newCols); + + for (int r = 0; r < newRows; ++r) { + for (int c = 0; c < newCols; ++c) { + EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c); } } } - void resize_reduce_is_data_stay_consistent(int newRowsCount, int newColsCount) { - vec.resize(newRowsCount, newColsCount, 0); - assert_fill_with(vec, newRowsCount, newColsCount); - } + void assert_resize_increase_initializes_new(size_t newRows, size_t newCols) { + vec.resize(newRows, newCols, 0); + ASSERT_EQ(vec.rows(), newRows); + ASSERT_EQ(vec.cols(), newCols); - void resize_increase_is_data_stay_consistent(int newRowsCount, int newColsCount) { - vec.resize(newRowsCount, newColsCount, 0); - assert_fill_with(vec, ROWS_COUNT_INIT, COLS_COUNT_INIT); + // Check old data preserved + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c); + } + } - for (int r = 0; r < newRowsCount; ++r) { - for (int c = 0; c < newColsCount; ++c) { - if (r < ROWS_COUNT_INIT || c < COLS_COUNT_INIT) continue; - ASSERT_EQ(vec.element(r, c), 0); + // Check new elements initialized to 0 + for (size_t r = 0; r < newRows; ++r) { + for (size_t c = 0; c < newCols; ++c) { + if (r >= ROWS_COUNT_INIT || c >= COLS_COUNT_INIT) { + EXPECT_EQ(vec.element(r, c), 0); + } } } } }; -TEST_F(Vector2D, resize_is_increase_col_count) { - vec.resize(ROWS_COUNT_INIT, COLS_COUNT_INCREASE, 0); - ASSERT_EQ(vec.cols(), COLS_COUNT_INCREASE); +TEST_F(Vector2DResizeTest, resize_increase_both_preserves_data) { + assert_resize_increase_initializes_new(ROWS_COUNT_INCREASE, COLS_COUNT_INCREASE); } -TEST_F(Vector2D, resize_is_reduce_col_count) { - vec.resize(ROWS_COUNT_INIT, COLS_COUNT_REDUCE, 0); - ASSERT_EQ(vec.cols(), COLS_COUNT_REDUCE); +TEST_F(Vector2DResizeTest, resize_increase_rows_only_preserves_data) { + assert_resize_increase_initializes_new(ROWS_COUNT_INCREASE, COLS_COUNT_INIT); } -TEST_F(Vector2D, resize_is_increase_rows_count) { - vec.resize(ROWS_COUNT_INCREASE, COLS_COUNT_INIT, 0); - ASSERT_EQ(vec.rows(), ROWS_COUNT_INCREASE); +TEST_F(Vector2DResizeTest, resize_increase_cols_only_preserves_data) { + assert_resize_increase_initializes_new(ROWS_COUNT_INIT, COLS_COUNT_INCREASE); } -TEST_F(Vector2D, resize_is_reduce_rows_count) { - vec.resize(ROWS_COUNT_REDUCE, COLS_COUNT_INIT, 0); - ASSERT_EQ(vec.rows(), ROWS_COUNT_REDUCE); +TEST_F(Vector2DResizeTest, resize_reduce_both_preserves_data) { + assert_resize_reduce_preserves_data(ROWS_COUNT_REDUCE, COLS_COUNT_REDUCE); } -TEST_F(Vector2D, resize_increase_both_is_data_stay_consistent) { - resize_increase_is_data_stay_consistent(ROWS_COUNT_INCREASE, COLS_COUNT_INCREASE); +TEST_F(Vector2DResizeTest, resize_reduce_rows_only_preserves_data) { + assert_resize_reduce_preserves_data(ROWS_COUNT_REDUCE, COLS_COUNT_INIT); } -TEST_F(Vector2D, resize_reduce_cols_is_data_stay_consistent) { - resize_reduce_is_data_stay_consistent(ROWS_COUNT_INIT, COLS_COUNT_REDUCE); +TEST_F(Vector2DResizeTest, resize_reduce_cols_only_preserves_data) { + assert_resize_reduce_preserves_data(ROWS_COUNT_INIT, COLS_COUNT_REDUCE); } -TEST_F(Vector2D, resize_reduce_rows_is_data_stay_consistent) { - resize_reduce_is_data_stay_consistent(ROWS_COUNT_REDUCE, COLS_COUNT_INIT); +TEST_F(Vector2DResizeTest, resize_to_zero_creates_empty) { + vec.resize(0, 0, 42); + EXPECT_TRUE(vec.isEmpty()); + EXPECT_EQ(vec.rows(), 0); + EXPECT_EQ(vec.cols(), 0); } -TEST_F(Vector2D, resize_reduce_both_is_data_stay_consistent) { - resize_reduce_is_data_stay_consistent(ROWS_COUNT_REDUCE, COLS_COUNT_REDUCE); +TEST_F(Vector2DResizeTest, resize_same_dimensions_does_nothing) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + PIVector oldData = vec.asPlainVector(); + + vec.resize(oldRows, oldCols, 999); + EXPECT_EQ(vec.rows(), oldRows); + EXPECT_EQ(vec.cols(), oldCols); + EXPECT_EQ(vec.asPlainVector(), oldData); // Data unchanged } + +// ==================== SEARCH AND LOOKUP TESTS ==================== +TEST_F(Vector2DTest, contains_finds_element_in_flat_vector) { + EXPECT_TRUE(vec.contains(5 * COLS_COUNT_INIT + 7)); + EXPECT_FALSE(vec.contains(-999)); + EXPECT_TRUE(vec.contains(0)); // first element + EXPECT_TRUE(vec.contains(ROWS_COUNT_INIT * COLS_COUNT_INIT - 1)); // last element +} + +TEST_F(Vector2DTest, contains_with_start_parameter_works) { + int target = 10 * COLS_COUNT_INIT + 15; + EXPECT_TRUE(vec.contains(target)); + EXPECT_TRUE(vec.contains(target, target)); // start exactly at target (inclusive) + EXPECT_FALSE(vec.contains(target, target + 1)); // start after target +} + +TEST_F(Vector2DTest, contains_vector_of_elements_works) { + PIVector searchFor; + searchFor << 100 << 200 << 300; + EXPECT_TRUE(vec.contains(searchFor)); + + searchFor << -999; + EXPECT_FALSE(vec.contains(searchFor)); +} + +TEST_F(Vector2DTest, entries_counts_occurrences) { + // Add some duplicates + vec.fill(0); + vec.element(5, 5) = 42; + vec.element(10, 10) = 42; + + EXPECT_EQ(vec.entries(42), 2); + EXPECT_EQ(vec.entries(-1), 0); +} + +TEST_F(Vector2DTest, entries_with_predicate_counts_matches) { + auto isEven = [](const int & e) { return e % 2 == 0; }; + int evenCount = 0; + for (size_t i = 0; i < vec.size(); ++i) { + if (vec.asPlainVector()[i] % 2 == 0) evenCount++; + } + EXPECT_EQ(vec.entries(isEven), evenCount); +} + +TEST_F(Vector2DTest, indexOf_finds_first_occurrence) { + int firstTarget = 5 * COLS_COUNT_INIT + 10; + EXPECT_EQ(vec.indexOf(firstTarget), static_cast(firstTarget)); + + // Add duplicate later + vec.asPlainVector()[20 * COLS_COUNT_INIT + 15] = firstTarget; + EXPECT_EQ(vec.indexOf(firstTarget), static_cast(firstTarget)); // Still first +} + +TEST_F(Vector2DTest, indexOf_with_start_works) { + int target = 15 * COLS_COUNT_INIT + 20; + EXPECT_EQ(vec.indexOf(target, target + 1), -1); + EXPECT_EQ(vec.indexOf(target, target), static_cast(target)); +} + +TEST_F(Vector2DTest, indexWhere_finds_first_match) { + auto isLarge = [](const int & e) { return e > 500; }; + ssize_t expected = 501; // First element > 500 + EXPECT_EQ(vec.indexWhere(isLarge), expected); +} + +TEST_F(Vector2DTest, lastIndexOf_finds_last_occurrence) { + // Add duplicate later + int target = 8 * COLS_COUNT_INIT + 8; + vec.asPlainVector()[25 * COLS_COUNT_INIT + 25] = target; + + EXPECT_EQ(vec.lastIndexOf(target), static_cast(25 * COLS_COUNT_INIT + 25)); +} + +TEST_F(Vector2DTest, lastIndexWhere_finds_last_match) { + auto isLarge = [](const int & e) { return e > 900; }; + ssize_t expected = vec.size() - 1; // Last element + EXPECT_EQ(vec.lastIndexWhere(isLarge), expected); +} + +// ==================== STATISTICS AND CONDITIONS TESTS ==================== +TEST_F(Vector2DTest, any_returns_true_if_any_match) { + auto isNegative = [](const int & e) { return e < 0; }; + auto isLarge = [](const int & e) { return e > 1000000; }; + + EXPECT_FALSE(vec.any(isNegative)); + EXPECT_FALSE(vec.any(isLarge)); + + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(vec.any(isPositive)); +} + +TEST_F(Vector2DTest, every_returns_true_if_all_match) { + auto isNonNegative = [](const int & e) { return e >= 0; }; + const int max = ROWS_COUNT_INIT * COLS_COUNT_INIT; + auto isLessThan = [max](const int & e) { return e < max; }; + + EXPECT_TRUE(vec.every(isNonNegative)); + EXPECT_TRUE(vec.every(isLessThan)); + + auto isEven = [](const int & e) { return e % 2 == 0; }; + EXPECT_FALSE(vec.every(isEven)); +} + +// ==================== FILL AND ASSIGN TESTS ==================== +TEST_F(Vector2DTest, fill_sets_all_elements_to_value) { + vec.fill(42); + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(r, c), 42); + } + } +} + +TEST_F(Vector2DTest, fill_with_function_generates_values) { + vec.fill([](size_t i) { return static_cast(i * 2); }); + for (size_t i = 0; i < vec.size(); ++i) { + EXPECT_EQ(vec.asPlainVector()[i], static_cast(i * 2)); + } +} + +TEST_F(Vector2DTest, assign_is_alias_for_fill) { + vec.assign(99); + for (size_t i = 0; i < vec.size(); ++i) { + EXPECT_EQ(vec.asPlainVector()[i], 99); + } +} + +TEST_F(Vector2DTest, assign_with_size_resets_to_single_row) { + vec.assign(10, 77); + EXPECT_EQ(vec.rows(), 1); + EXPECT_EQ(vec.cols(), 10); + for (size_t c = 0; c < 10; ++c) { + EXPECT_EQ(vec.element(0, c), 77); + } +} + +// ==================== COMPARISON TESTS ==================== +TEST_F(Vector2DTest, equality_operator_works) { + PIVector2D same = vec; + EXPECT_EQ(vec, same); + + PIVector2D differentRows(ROWS_COUNT_INIT + 1, COLS_COUNT_INIT); + EXPECT_NE(vec, differentRows); + + PIVector2D differentCols(ROWS_COUNT_INIT, COLS_COUNT_INIT + 1); + EXPECT_NE(vec, differentCols); + + PIVector2D differentData(ROWS_COUNT_INIT, COLS_COUNT_INIT, 99); + EXPECT_NE(vec, differentData); +} + +// ==================== CONVERSION TESTS ==================== +TEST_F(Vector2DTest, toVectors_converts_correctly) { + auto vectors = vec.toVectors(); + EXPECT_EQ(vectors.size(), ROWS_COUNT_INIT); + + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(vectors[r].size(), COLS_COUNT_INIT); + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vectors[r][c], vec.element(r, c)); + } + } +} + +TEST_F(Vector2DTest, plainVector_returns_underlying_storage) { + const auto & plain = vec.asPlainVector(); + EXPECT_EQ(plain.size(), vec.size()); + + for (size_t i = 0; i < plain.size(); ++i) { + EXPECT_EQ(plain[i], vec.asPlainVector()[i]); + } +} + +TEST_F(Vector2DTest, toPlainVector_returns_copy) { + auto copy = vec.toPlainVector(); + EXPECT_EQ(copy.size(), vec.size()); + EXPECT_NE(copy.data(), vec.data()); // Different memory + + for (size_t i = 0; i < copy.size(); ++i) { + EXPECT_EQ(copy[i], vec.asPlainVector()[i]); + } +} + +// ==================== SWAP TESTS ==================== +TEST_F(Vector2DTest, swap_exchanges_contents) { + PIVector2D other(5, 5, 42); + + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + PIVector oldData = vec.asPlainVector(); + + vec.swap(other); + + EXPECT_EQ(vec.rows(), 5); + EXPECT_EQ(vec.cols(), 5); + for (size_t i = 0; i < vec.size(); ++i) { + EXPECT_EQ(vec.asPlainVector()[i], 42); + } + + EXPECT_EQ(other.rows(), oldRows); + EXPECT_EQ(other.cols(), oldCols); + EXPECT_EQ(other.asPlainVector(), oldData); +} + +// ==================== CLEAR TESTS ==================== +TEST_F(Vector2DTest, clear_removes_all_elements) { + vec.clear(); + EXPECT_TRUE(vec.isEmpty()); + EXPECT_EQ(vec.rows(), 0); + EXPECT_EQ(vec.cols(), 0); + EXPECT_EQ(vec.size(), 0); +} + +// ==================== TRANSPOSE AND REVERSE TESTS ==================== +TEST_F(Vector2DTest, transposed_returns_correct_dimensions) { + auto transposed = vec.transposed(); + EXPECT_EQ(transposed.rows(), COLS_COUNT_INIT); + EXPECT_EQ(transposed.cols(), ROWS_COUNT_INIT); + + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(transposed.element(c, r), vec.element(r, c)); + } + } +} + +TEST_F(Vector2DTest, reverseRows_reverses_row_order) { + auto original = vec; + vec.reverseRows(); + + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(r, c), original.element(ROWS_COUNT_INIT - 1 - r, c)); + } + } +} + +TEST_F(Vector2DTest, reverseColumns_reverses_column_order_in_each_row) { + auto original = vec; + vec.reverseColumns(); + + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(r, c), original.element(r, COLS_COUNT_INIT - 1 - c)); + } + } +} + +TEST_F(Vector2DTest, reverseRows_and_reverseColumns_compose_correctly) { + auto original = vec; + vec.reverseRows(); + vec.reverseColumns(); + + // This should be equivalent to 180-degree rotation + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(r, c), original.element(ROWS_COUNT_INIT - 1 - r, COLS_COUNT_INIT - 1 - c)); + } + } +} + +// ==================== RANGE TESTS ==================== +TEST_F(Vector2DTest, getRange_returns_submatrix) { + auto sub = vec.getRange(5, 10, 8, 15); + EXPECT_EQ(sub.rows(), 10); + EXPECT_EQ(sub.cols(), 15); + + for (size_t r = 0; r < 10; ++r) { + for (size_t c = 0; c < 15; ++c) { + EXPECT_EQ(sub.element(r, c), vec.element(5 + r, 8 + c)); + } + } +} + +TEST_F(Vector2DTest, getRange_with_invalid_params_returns_empty) { + auto sub1 = vec.getRange(ROWS_COUNT_INIT, 5, 0, 5); + EXPECT_TRUE(sub1.isEmpty()); + + auto sub2 = vec.getRange(0, 5, COLS_COUNT_INIT, 5); + EXPECT_TRUE(sub2.isEmpty()); +} + +TEST_F(Vector2DTest, getRange_truncates_out_of_bounds) { + auto sub = vec.getRange(ROWS_COUNT_INIT - 5, 10, COLS_COUNT_INIT - 5, 10); + EXPECT_EQ(sub.rows(), 5); + EXPECT_EQ(sub.cols(), 5); +} + +// ==================== FUNCTIONAL PROGRAMMING TESTS ==================== +TEST_F(Vector2DTest, map_transforms_elements) { + auto doubled = vec.map([](const int & e) { return e * 2; }); + EXPECT_EQ(doubled.rows(), vec.rows()); + EXPECT_EQ(doubled.cols(), vec.cols()); + + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(doubled.element(r, c), vec.element(r, c) * 2); + } + } +} + +TEST_F(Vector2DTest, map_changes_type) { + auto asString = vec.map([](const int & e) { return PIString::fromNumber(e); }); + EXPECT_EQ(asString.rows(), vec.rows()); + EXPECT_EQ(asString.cols(), vec.cols()); + + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(asString.element(r, c), PIString::fromNumber(vec.element(r, c))); + } + } +} + +TEST_F(Vector2DTest, mapIndexed_uses_indices) { + auto indexed = vec.mapIndexed([](size_t r, size_t c, const int & e) { return static_cast(r * 1000 + c); }); + + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(indexed.element(r, c), static_cast(r * 1000 + c)); + } + } +} + +TEST_F(Vector2DTest, forEach_readonly_visits_all_elements) { + size_t count = 0; + vec.asPlainVector().forEach([&count](const int &) { count++; }); + EXPECT_EQ(count, vec.size()); +} + +TEST_F(Vector2DTest, forEach_modifying_changes_elements) { + vec.asPlainVector().forEach([](int & e) { e++; }); + + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c + 1); + } + } +} + +/* +TEST_F(Vector2DTest, forEachIndexed_readonly_uses_indices) { + bool allCorrect = true; + vec.forEachIndexed([&allCorrect](size_t r, size_t c, const int & e) { + if (e != static_cast(r * COLS_COUNT_INIT + c)) allCorrect = false; + }); + EXPECT_TRUE(allCorrect); +} + +TEST_F(Vector2DTest, forEachIndexed_modifying_uses_indices) { + vec.forEachIndexed([](size_t r, size_t c, int & e) { e = static_cast(r * 1000 + c); }); + + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * 1000 + c)); + } + } +} +*/ + +TEST_F(Vector2DTest, reduce_accumulates_correctly) { + int sum = vec.reduce([](const int & e, const int & acc) { return e + acc; }); + + int expected = (vec.size() - 1) * vec.size() / 2; + EXPECT_EQ(sum, expected); +} + +TEST_F(Vector2DTest, reduce_with_initial_value) { + int sum = vec.reduce([](const int & e, const int & acc) { return e + acc; }, 100); + + int expected = (vec.size() - 1) * vec.size() / 2 + 100; + EXPECT_EQ(sum, expected); +} + +TEST_F(Vector2DTest, reduceIndexed_uses_indices) { + int sum = + vec.reduceIndexed([](size_t r, size_t c, const int & e, const int & acc) { return acc + static_cast(r * 1000 + c); }); + + int expected = 0; + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + expected += r * 1000 + c; + } + } + EXPECT_EQ(sum, expected); +} + +// ==================== REMOVAL TESTS ==================== +TEST_F(Vector2DTest, removeRow_removes_specified_row) { + size_t oldRows = vec.rows(); + auto rowContent = vec[10].toVector(); + + vec.removeRow(10); + EXPECT_EQ(vec.rows(), oldRows - 1); + + // Check rows after 10 shifted up + for (size_t r = 10; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), (r + 1) * COLS_COUNT_INIT + c); + } + } +} + +TEST_F(Vector2DTest, removeRow_invalid_index_does_nothing) { + size_t oldRows = vec.rows(); + vec.removeRow(ROWS_COUNT_INIT + 10); + EXPECT_EQ(vec.rows(), oldRows); +} + +TEST_F(Vector2DTest, removeRow_last_row_works) { + size_t oldRows = vec.rows(); + vec.removeRow(oldRows - 1); + EXPECT_EQ(vec.rows(), oldRows - 1); +} + +TEST_F(Vector2DTest, removeColumn_removes_specified_column) { + size_t oldCols = vec.cols(); + vec.removeColumn(15); + EXPECT_EQ(vec.cols(), oldCols - 1); + + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < 15; ++c) { + EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c); + } + for (size_t c = 15; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), r * COLS_COUNT_INIT + c + 1); + } + } +} + +TEST_F(Vector2DTest, removeColumn_invalid_index_does_nothing) { + size_t oldCols = vec.cols(); + vec.removeColumn(COLS_COUNT_INIT + 10); + EXPECT_EQ(vec.cols(), oldCols); +} + +TEST_F(Vector2DTest, removeColumn_last_column_works) { + size_t oldCols = vec.cols(); + vec.removeColumn(oldCols - 1); + EXPECT_EQ(vec.cols(), oldCols - 1); +} + +TEST_F(Vector2DTest, removeRowsWhere_removes_matching_rows) { + auto isSpecial = [](const PIVector2D::RowConst & row) { + return row[0] == 999; // First element is 999 + }; + + const size_t rowsCont = 5; + // Add some identifiable rows + for (size_t r = 0; r < rowsCont; ++r) { + vec.addRow(PIVector(COLS_COUNT_INIT, 999)); + } + EXPECT_EQ(vec.filterRows(isSpecial).rows(), rowsCont); + + vec.removeRowsWhere(isSpecial); + EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT); + + // Verify no rows with 999 remain + auto res = vec.filterRows(isSpecial); + EXPECT_TRUE(res.isEmpty()); +} + +TEST_F(Vector2DTest, removeColumnsWhere_removes_matching_columns) { + // Make some columns have a distinctive first element + for (size_t c = 0; c < 5; ++c) { + vec.element(0, c) = 777; + } + + auto isSpecial = [](const PIVector2D::ColConst & col) { + return col[0] == 777; // First element is 777 + }; + + size_t oldCols = vec.cols(); + vec.removeColumnsWhere(isSpecial); + EXPECT_EQ(vec.cols(), oldCols - 5); + + // Verify no columns with 777 in first row remain + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_NE(vec.element(0, c), 777); + } +} + +// ==================== FILTER TESTS ==================== +TEST_F(Vector2DTest, filterRows_returns_only_matching_rows) { + auto rowsWithEvenFirst = vec.filterRows([](const PIVector2D::RowConst & row) { return row[0] % 2 == 0; }); + + // First element of row r is r * COLS_COUNT_INIT + // This is even for all rows since COLS_COUNT_INIT is even (34) + EXPECT_EQ(rowsWithEvenFirst.rows(), ROWS_COUNT_INIT); + + auto rowsWithLargeFirst = vec.filterRows([](const PIVector2D::RowConst & row) { return row[0] > 500; }); + + // First element > 500 means r * 34 > 500 -> r > 14.7 + EXPECT_EQ(rowsWithLargeFirst.rows(), ROWS_COUNT_INIT - 15); +} + +TEST_F(Vector2DTest, filterColumns_returns_only_matching_columns) { + auto colsWithEvenFirst = vec.filterColumns([](const PIVector2D::ColConst & col) { return col[0] % 2 == 0; }); + + // First element of column c is c + EXPECT_EQ(colsWithEvenFirst.cols(), COLS_COUNT_INIT / 2); +} + +TEST_F(Vector2DTest, filterColumns_empty_result_returns_empty) { + auto noCols = vec.filterColumns([](const PIVector2D::ColConst &) { return false; }); + EXPECT_TRUE(noCols.isEmpty()); +} + +// ==================== EDGE CASE TESTS ==================== +TEST(Vector2DEdgeTest, empty_vector_operations) { + PIVector2D empty; + + EXPECT_TRUE(empty.isEmpty()); + EXPECT_EQ(empty.rows(), 0); + EXPECT_EQ(empty.cols(), 0); + + // These should not crash + empty.clear(); + empty.fill(42); + empty.transposed(); + empty.reverseRows(); + empty.reverseColumns(); + + auto range = empty.getRange(0, 5, 0, 5); + EXPECT_TRUE(range.isEmpty()); + + auto filtered = empty.filterRows([](const PIVector2D::RowConst &) { return true; }); + EXPECT_TRUE(filtered.isEmpty()); +} + +TEST(Vector2DEdgeTest, single_element_vector) { + PIVector2D single(1, 1, 42); + + EXPECT_EQ(single.rows(), 1); + EXPECT_EQ(single.cols(), 1); + EXPECT_EQ(single.element(0, 0), 42); + + auto row = single[0]; + EXPECT_EQ(row.size(), 1); + EXPECT_EQ(row[0], 42); + + auto col = single.col(0); + EXPECT_EQ(col.size(), 1); + EXPECT_EQ(col[0], 42); + + single.reverseRows(); // Should do nothing + EXPECT_EQ(single.element(0, 0), 42); + + single.reverseColumns(); // Should do nothing + EXPECT_EQ(single.element(0, 0), 42); +} + +// ==================== OUTPUT TESTS ==================== +TEST_F(Vector2DTest, picout_operator_works) { + // Just test that it compiles and doesn't crash + PICout s; + s << vec; + // No assertion, just ensure it runs +} + +#ifdef PIP_STD_IOSTREAM +TEST_F(Vector2DTest, iostream_operator_works) { + // PIVector2D doesn't have direct iostream operator, + // but PIVector does, and we can test conversion + std::stringstream ss; + ss << vec.plainVector(); + // No assertion, just ensure it runs +} +#endif From 8ecec6b91418489e783eb8256e3cf2c7c2134f2f Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Tue, 17 Feb 2026 21:40:36 +0300 Subject: [PATCH 09/53] add more funcs --- libs/main/containers/pivector2d.h | 269 +++++++++++++++++++++--------- tests/math/testpivector2d.cpp | 46 +---- 2 files changed, 196 insertions(+), 119 deletions(-) diff --git a/libs/main/containers/pivector2d.h b/libs/main/containers/pivector2d.h index 8f208ef3..7bd8a990 100644 --- a/libs/main/containers/pivector2d.h +++ b/libs/main/containers/pivector2d.h @@ -25,6 +25,7 @@ #ifndef PIVECTOR2D_H #define PIVECTOR2D_H +#include "pipair.h" #include "pivector.h" //! \addtogroup Containers @@ -77,12 +78,6 @@ public: //! \param rows Number of rows. //! \param cols Number of columns. //! \param v The source 1D vector. Its size must be at least `rows * cols`. - //! \~english \param rows Количество строк. - //! \~russian \param rows Количество строк. - //! \~english \param cols Количество столбцов. - //! \~russian \param cols Количество столбцов. - //! \~english \param v Исходный одномерный вектор. Его размер должен быть не меньше `rows * cols`. - //! \~russian \param v Исходный одномерный вектор. Его размер должен быть не меньше `rows * cols`. inline PIVector2D(size_t rows, size_t cols, const PIVector & v): rows_(rows), cols_(cols), mat(v) { mat.resize(rows * cols); } //! \~english Move constructs a 2D array from an existing 1D vector, reshaping it. @@ -90,19 +85,11 @@ public: //! \param rows Number of rows. //! \param cols Number of columns. //! \param v The source 1D vector (rvalue reference). Its size must be at least `rows * cols`. - //! \~english \param rows Количество строк. - //! \~russian \param rows Количество строк. - //! \~english \param cols Количество столбцов. - //! \~russian \param cols Количество столбцов. - //! \~english \param v Исходный одномерный вектор (rvalue-ссылка). Его размер должен быть не меньше `rows * cols`. - //! \~russian \param v Исходный одномерный вектор (rvalue-ссылка). Его размер должен быть не меньше `rows * cols`. inline PIVector2D(size_t rows, size_t cols, PIVector && v): rows_(rows), cols_(cols), mat(std::move(v)) { mat.resize(rows * cols); } //! \~english Constructs a 2D array from a vector of vectors (jagged array). Assumes all inner vectors have the same size. //! \~russian Создает двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют //! одинаковый размер. \param v The source vector of vectors. - //! \~english \param v Исходный вектор векторов. - //! \~russian \param v Исходный вектор векторов. inline PIVector2D(const PIVector> & v) { rows_ = v.size(); if (rows_) { @@ -205,6 +192,36 @@ public: //! \~english Converts the row to a \a PIVector. //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } + + // --- Поиск в строке --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[st_ + i] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[st_ + i] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[st_ + i])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[st_ + i])) return i; + } + return -1; + } }; //! \class Col @@ -272,6 +289,36 @@ public: ret << (*p_)[i * step_ + col_]; return ret; } + + // --- Поиск в столбце --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[i * step_ + col_] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[i * step_ + col_] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[i * step_ + col_])) return i; + } + return -1; + } }; //! \class RowConst @@ -305,6 +352,36 @@ public: //! \~english Converts the row to a \a PIVector. //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } + + // --- Поиск в строке (только чтение) --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[st_ + i] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[st_ + i] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[st_ + i])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[st_ + i])) return i; + } + return -1; + } }; //! \class ColConst @@ -345,6 +422,36 @@ public: ret << (*p_)[i * step_ + col_]; return ret; } + + // --- Поиск в столбце (только чтение) --- + inline ssize_t indexOf(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[i * step_ + col_] == e) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if ((*p_)[i * step_ + col_] == e) return i; + } + return -1; + } + inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) return (ssize_t)i; + } + return -1; + } + inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; + for (ssize_t i = from; i >= 0; --i) { + if (test((*p_)[i * step_ + col_])) return i; + } + return -1; + } }; //! \~english Returns a reference to the element at the given row and column. @@ -549,22 +656,39 @@ public: inline int entries(std::function test, ssize_t start = 0) const { return mat.entries(test, start); } - // TODO: next 4 functions implement in to RowConst and ColConst and rewtite thisto return index as PIPair - //! \~english Returns the first index of `e` in the flat vector. - //! \~russian Возвращает первый индекс `e` в плоском векторе. - inline ssize_t indexOf(const T & e, ssize_t start = 0) const { return mat.indexOf(e, start); } - //! \~english Returns the first index in the flat vector that passes the `test`. - //! \~russian Возвращает первый индекс в плоском векторе, проходящий `test`. - inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { return mat.indexWhere(test, start); } - //! \~english Returns the last index of `e` in the flat vector. - //! \~russian Возвращает последний индекс `e` в плоском векторе. - inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { return mat.lastIndexOf(e, start); } - //! \~english Returns the last index in the flat vector that passes the `test`. - //! \~russian Возвращает последний индекс в плоском векторе, проходящий `test`. - inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { - return mat.lastIndexWhere(test, start); + //! \~english Returns the first index (row, col) of `e` in the 2D array. + //! \~russian Возвращает первый индекс (строка, столбец) элемента `e` в двумерном массиве. + inline PIPair indexOf(const T & e, ssize_t start = 0) const { + ssize_t flat = mat.indexOf(e, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); } + //! \~english Returns the first index (row, col) in the 2D array that passes the `test`. + //! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`. + inline PIPair indexWhere(std::function test, ssize_t start = 0) const { + ssize_t flat = mat.indexWhere(test, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); + } + + //! \~english Returns the last index (row, col) of `e` in the 2D array. + //! \~russian Возвращает последний индекс (строка, столбец) элемента `e` в двумерном массиве. + inline PIPair lastIndexOf(const T & e, ssize_t start = -1) const { + ssize_t flat = mat.lastIndexOf(e, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); + } + + //! \~english Returns the last index (row, col) in the 2D array that passes the `test`. + //! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`. + inline PIPair lastIndexWhere(std::function test, ssize_t start = -1) const { + ssize_t flat = mat.lastIndexWhere(test, start); + if (flat < 0 || cols_ == 0) return PIPair(-1, -1); + return PIPair(flat / cols_, flat % cols_); + } + + //! \~english Tests if any element in the flat vector passes the `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`. inline bool any(std::function test) const { return mat.any(test); } @@ -591,22 +715,17 @@ public: //! \~russian То же, что и \a fill(). inline PIVector2D & assign(const T & e = T()) { return fill(e); } - - // TODO: rewrite with size_t rows, size_t cols arguments - //! \~english Assigns a new size to the underlying flat vector and resets the 2D structure to 1 row. - //! \~russian Присваивает новый размер внутреннему плоскому вектору и сбрасывает 2D-структуру до 1 строки. - inline PIVector2D & assign(size_t new_size, const T & f) { - mat.assign(new_size, f); - if (mat.isEmpty()) { - rows_ = cols_ = 0; - } else { - rows_ = 1; - cols_ = mat.size(); - } + //! \~english Assigns new size and fills with value. + //! \~russian Задаёт новый размер и заполняет значением. + inline PIVector2D & assign(size_t rows, size_t cols, const T & f = T()) { + mat.assign(rows * cols, f); + rows_ = rows; + cols_ = cols; return *this; } - // TODO: fix for different rows and cols count + + // TODO: исправить - при транспонировании количество строк становится количеством столбцов и наоборот //! \~english Returns a transposed 2D array (rows become columns and vice versa). //! \~russian Возвращает транспонированный двумерный массив (строки становятся столбцами и наоборот). inline PIVector2D transposed() const { @@ -620,17 +739,16 @@ public: return result; } - // TODO: переписать по возможности избегая копирования данных, в идеале использовать piSwap //! \~english Reverses the order of rows in place. //! \~russian Изменяет порядок строк на обратный на месте. inline PIVector2D & reverseRows() { const size_t half = rows_ / 2; for (size_t i = 0; i < half; ++i) { - Row r1 = row(i); - Row r2 = row(rows_ - 1 - i); - PIVector temp = r1.toVector(); - r1 = r2.toVector(); - r2 = temp; + T * row1 = data(i * cols_); + T * row2 = data((rows_ - 1 - i) * cols_); + for (size_t j = 0; j < cols_; ++j) { + piSwap(row1[j], row2[j]); + } } return *this; } @@ -685,42 +803,33 @@ public: return PIVector2D(rows_, cols_, std::move(mappedMat)); } - - /* - //! \~english Executes a read-only function for each element. - //! \~russian Выполняет функцию только для чтения для каждого элемента. - inline void forEach(std::function f) const { mat.forEach(f); } - - //! \~english Executes a function for each element, allowing modification. - //! \~russian Выполняет функцию для каждого элемента, позволяя их изменять. - inline PIVector2D & forEach(std::function f) { - mat.forEach(f); - return *this; + // --- Итерация по строкам и столбцам --- + //! \~english Applies a function to each row (modifiable). + //! \~russian Применяет функцию к каждой строке (с возможностью изменения). + inline PIVector2D & forEachRow(std::function f) { + for (size_t r = 0; r < rows_; ++r) + f(row(r)); + return *this; } - - //! \~english Executes a read-only function (with row and col indices) for each element. - //! \~russian Выполняет функцию только для чтения (с индексами строки и столбца) для каждого элемента. - inline void forEachIndexed(std::function f) const { - for (size_t r = 0; r < rows_; ++r) { - for (size_t c = 0; c < cols_; ++c) { - f(r, c, element(r, c)); - } - } + //! \~english Applies a function to each row (read-only). + //! \~russian Применяет функцию к каждой строке (только чтение). + inline void forEachRow(std::function f) const { + for (size_t r = 0; r < rows_; ++r) + f(row(r)); } - - //! \~english Executes a function (with row and col indices) for each element, allowing modification. - //! \~russian Выполняет функцию (с индексами строки и столбца) для каждого элемента, позволяя их изменять. - inline PIVector2D & forEachIndexed(std::function f) { - for (size_t r = 0; r < rows_; ++r) { - for (size_t c = 0; c < cols_; ++c) { - f(r, c, element(r, c)); - } - } - return *this; + //! \~english Applies a function to each column (modifiable). + //! \~russian Применяет функцию к каждому столбцу (с возможностью изменения). + inline PIVector2D & forEachColumn(std::function f) { + for (size_t c = 0; c < cols_; ++c) + f(col(c)); + return *this; + } + //! \~english Applies a function to each column (read-only). + //! \~russian Применяет функцию к каждому столбцу (только чтение). + inline void forEachColumn(std::function f) const { + for (size_t c = 0; c < cols_; ++c) + f(col(c)); } - */ - // TODO: Переделать закомментаренные выше функции на функции forEachRow, forEachColumn - //! \~english Accumulates a value across all elements. //! \~russian Аккумулирует значение по всем элементам. diff --git a/tests/math/testpivector2d.cpp b/tests/math/testpivector2d.cpp index 357c2383..f2a47361 100644 --- a/tests/math/testpivector2d.cpp +++ b/tests/math/testpivector2d.cpp @@ -411,40 +411,6 @@ TEST_F(Vector2DTest, entries_with_predicate_counts_matches) { EXPECT_EQ(vec.entries(isEven), evenCount); } -TEST_F(Vector2DTest, indexOf_finds_first_occurrence) { - int firstTarget = 5 * COLS_COUNT_INIT + 10; - EXPECT_EQ(vec.indexOf(firstTarget), static_cast(firstTarget)); - - // Add duplicate later - vec.asPlainVector()[20 * COLS_COUNT_INIT + 15] = firstTarget; - EXPECT_EQ(vec.indexOf(firstTarget), static_cast(firstTarget)); // Still first -} - -TEST_F(Vector2DTest, indexOf_with_start_works) { - int target = 15 * COLS_COUNT_INIT + 20; - EXPECT_EQ(vec.indexOf(target, target + 1), -1); - EXPECT_EQ(vec.indexOf(target, target), static_cast(target)); -} - -TEST_F(Vector2DTest, indexWhere_finds_first_match) { - auto isLarge = [](const int & e) { return e > 500; }; - ssize_t expected = 501; // First element > 500 - EXPECT_EQ(vec.indexWhere(isLarge), expected); -} - -TEST_F(Vector2DTest, lastIndexOf_finds_last_occurrence) { - // Add duplicate later - int target = 8 * COLS_COUNT_INIT + 8; - vec.asPlainVector()[25 * COLS_COUNT_INIT + 25] = target; - - EXPECT_EQ(vec.lastIndexOf(target), static_cast(25 * COLS_COUNT_INIT + 25)); -} - -TEST_F(Vector2DTest, lastIndexWhere_finds_last_match) { - auto isLarge = [](const int & e) { return e > 900; }; - ssize_t expected = vec.size() - 1; // Last element - EXPECT_EQ(vec.lastIndexWhere(isLarge), expected); -} // ==================== STATISTICS AND CONDITIONS TESTS ==================== TEST_F(Vector2DTest, any_returns_true_if_any_match) { @@ -495,11 +461,13 @@ TEST_F(Vector2DTest, assign_is_alias_for_fill) { } TEST_F(Vector2DTest, assign_with_size_resets_to_single_row) { - vec.assign(10, 77); - EXPECT_EQ(vec.rows(), 1); - EXPECT_EQ(vec.cols(), 10); - for (size_t c = 0; c < 10; ++c) { - EXPECT_EQ(vec.element(0, c), 77); + vec.assign(3, 4, 77); + EXPECT_EQ(vec.rows(), 3); + EXPECT_EQ(vec.cols(), 4); + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), 77); + } } } From 7195734765a16e59b39a6dff53f349a1ea44f4fe Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Tue, 17 Feb 2026 21:53:18 +0300 Subject: [PATCH 10/53] add more tests --- tests/math/testpivector2d.cpp | 153 +++++++++++++++++++++++++++++----- 1 file changed, 130 insertions(+), 23 deletions(-) diff --git a/tests/math/testpivector2d.cpp b/tests/math/testpivector2d.cpp index f2a47361..538348d7 100644 --- a/tests/math/testpivector2d.cpp +++ b/tests/math/testpivector2d.cpp @@ -224,6 +224,97 @@ TEST_F(Vector2DTest, row_and_col_methods_return_same_as_operator) { EXPECT_EQ(col1[5], col2[5]); } +// ==================== PROXY SEARCH TESTS ==================== +TEST_F(Vector2DTest, row_proxy_search_works) { + auto row = vec[10]; + // indexOf + ssize_t idx = row.indexOf(vec.element(10, 5)); + EXPECT_EQ(idx, 5); + EXPECT_EQ(row.indexOf(-999), -1); + + // lastIndexOf (add a duplicate) + vec.element(10, 7) = vec.element(10, 5); // duplicate + idx = row.lastIndexOf(vec.element(10, 5)); + EXPECT_EQ(idx, 7); + + // indexWhere + auto isEven = [](const int & e) { return e % 2 == 0; }; + ssize_t firstEven = row.indexWhere(isEven); + EXPECT_GE(firstEven, 0); + + // lastIndexWhere + ssize_t lastEven = row.lastIndexWhere(isEven); + EXPECT_GE(lastEven, firstEven); +} + +TEST_F(Vector2DTest, col_proxy_search_works) { + auto col = vec.col(9); // используем столбец 9, где все элементы нечётные + ssize_t idx = col.indexOf(vec.element(12, 9)); + EXPECT_EQ(idx, 12); + EXPECT_EQ(col.indexOf(-999), -1); + + vec.element(20, 9) = vec.element(12, 9); // duplicate + idx = col.lastIndexOf(vec.element(12, 9)); + EXPECT_EQ(idx, 20); + + auto isOdd = [](const int & e) { return e % 2 != 0; }; + ssize_t firstOdd = col.indexWhere(isOdd); + EXPECT_GE(firstOdd, 0); + ssize_t lastOdd = col.lastIndexWhere(isOdd); + EXPECT_GE(lastOdd, firstOdd); +} + +TEST_F(Vector2DTest, rowconst_proxy_search_works) { + const auto & constVec = vec; + const auto row = constVec[10]; + ssize_t idx = row.indexOf(vec.element(10, 5)); + EXPECT_EQ(idx, 5); + idx = row.lastIndexOf(vec.element(10, 5)); + EXPECT_EQ(idx, 5); +} + +TEST_F(Vector2DTest, colconst_proxy_search_works) { + const auto & constVec = vec; + auto col = constVec.col(8); + ssize_t idx = col.indexOf(vec.element(12, 8)); + EXPECT_EQ(idx, 12); + idx = col.lastIndexOf(vec.element(12, 8)); + EXPECT_EQ(idx, 12); +} + +// ==================== ROW/COLUMN ITERATION TESTS ==================== +TEST_F(Vector2DTest, forEachRow_modifies_rows) { + vec.forEachRow([](PIVector2D::Row row) { + for (size_t c = 0; c < row.size(); ++c) + row[c] = 999; + }); + for (size_t r = 0; r < vec.rows(); ++r) + for (size_t c = 0; c < vec.cols(); ++c) + EXPECT_EQ(vec.element(r, c), 999); +} + +TEST_F(Vector2DTest, forEachRow_readonly_counts_rows) { + size_t count = 0; + vec.forEachRow([&count](PIVector2D::RowConst) { ++count; }); + EXPECT_EQ(count, vec.rows()); +} + +TEST_F(Vector2DTest, forEachColumn_modifies_columns) { + vec.forEachColumn([](PIVector2D::Col col) { + for (size_t r = 0; r < col.size(); ++r) + col[r] = 777; + }); + for (size_t r = 0; r < vec.rows(); ++r) + for (size_t c = 0; c < vec.cols(); ++c) + EXPECT_EQ(vec.element(r, c), 777); +} + +TEST_F(Vector2DTest, forEachColumn_readonly_counts_columns) { + size_t count = 0; + vec.forEachColumn([&count](PIVector2D::ColConst) { ++count; }); + EXPECT_EQ(count, vec.cols()); +} + // ==================== MODIFIER TESTS ==================== TEST_F(Vector2DTest, setRow_replaces_row_correctly) { PIVector newRow(COLS_COUNT_INIT); @@ -460,13 +551,13 @@ TEST_F(Vector2DTest, assign_is_alias_for_fill) { } } -TEST_F(Vector2DTest, assign_with_size_resets_to_single_row) { - vec.assign(3, 4, 77); - EXPECT_EQ(vec.rows(), 3); - EXPECT_EQ(vec.cols(), 4); - for (size_t r = 0; r < vec.rows(); ++r) { - for (size_t c = 0; c < vec.cols(); ++c) { - EXPECT_EQ(vec.element(r, c), 77); +TEST_F(Vector2DTest, assign_with_dimensions_resets_and_fills) { + vec.assign(5, 7, 123); + EXPECT_EQ(vec.rows(), 5); + EXPECT_EQ(vec.cols(), 7); + for (size_t r = 0; r < 5; ++r) { + for (size_t c = 0; c < 7; ++c) { + EXPECT_EQ(vec.element(r, c), 123); } } } @@ -674,25 +765,41 @@ TEST_F(Vector2DTest, forEach_modifying_changes_elements) { } } -/* -TEST_F(Vector2DTest, forEachIndexed_readonly_uses_indices) { - bool allCorrect = true; - vec.forEachIndexed([&allCorrect](size_t r, size_t c, const int & e) { - if (e != static_cast(r * COLS_COUNT_INIT + c)) allCorrect = false; - }); - EXPECT_TRUE(allCorrect); +TEST_F(Vector2DTest, indexOf_returns_correct_pair) { + auto p = vec.indexOf(vec.element(10, 15)); + EXPECT_EQ(p.first, 10); + EXPECT_EQ(p.second, 15); + p = vec.indexOf(-999); + EXPECT_EQ(p.first, -1); + EXPECT_EQ(p.second, -1); } -TEST_F(Vector2DTest, forEachIndexed_modifying_uses_indices) { - vec.forEachIndexed([](size_t r, size_t c, int & e) { e = static_cast(r * 1000 + c); }); - - for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { - for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { - EXPECT_EQ(vec.element(r, c), static_cast(r * 1000 + c)); - } - } +TEST_F(Vector2DTest, indexWhere_returns_correct_pair) { + vec.element(5, 5) = -42; + auto isTarget = [](const int & e) { return e == -42; }; + auto p = vec.indexWhere(isTarget); + EXPECT_EQ(p.first, 5); + EXPECT_EQ(p.second, 5); +} + +TEST_F(Vector2DTest, lastIndexOf_works) { + int val = vec.element(10, 10); + vec.element(20, 20) = val; // duplicate + auto p = vec.lastIndexOf(val); + EXPECT_EQ(p.first, 20); + EXPECT_EQ(p.second, 20); +} + +TEST_F(Vector2DTest, lastIndexWhere_works) { + auto isLarge = [](const int & e) { return e > 500; }; + auto p = vec.lastIndexWhere(isLarge); + EXPECT_GE(p.first, 0); + EXPECT_GE(p.second, 0); + // The last element with value >500 should be the largest index + size_t lastFlat = p.first * vec.cols() + p.second; + size_t expectedLastFlat = vec.size() - 1; + EXPECT_EQ(lastFlat, expectedLastFlat); } -*/ TEST_F(Vector2DTest, reduce_accumulates_correctly) { int sum = vec.reduce([](const int & e, const int & acc) { return e + acc; }); From 1739836a18296d8d9439b8ccb35f0966465011a9 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 25 Feb 2026 17:50:56 +0300 Subject: [PATCH 11/53] fix pistatistic mean calc --- libs/main/math/pistatistic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/main/math/pistatistic.h b/libs/main/math/pistatistic.h index f400dfc7..8e936713 100644 --- a/libs/main/math/pistatistic.h +++ b/libs/main/math/pistatistic.h @@ -46,8 +46,8 @@ public: bool calculate(const PIVector & val, const T & given_mean) { T v = T(), v1 = T(), v2 = T(), stddev = T(), var = T(); int i, n = val.size(); - if (n < 2) return false; mean = given_mean; + if (n < 2) return false; variance = skewness = kurtosis = T(); // Variance (using corrected two-pass algorithm) for (i = 0; i < n; i++) From 9588b48105ba52c469d712d86412749c089eebd7 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Fri, 27 Feb 2026 23:58:44 +0300 Subject: [PATCH 12/53] PIVector2D - add funcs, optimize, tests, fixes, doxygen (#194) Reviewed-on: https://git.shstk.ru/SHS/pip/pulls/194 Co-authored-by: andrey.bychkov Co-committed-by: andrey.bychkov --- .gitignore | 2 + CMakeLists.txt | 4 +- README.md | 115 +++ libs/main/containers/pivector.h | 46 +- libs/main/containers/pivector2d.h | 1109 ++++++++++++++++++++--------- libs/main/system/pihidevice.cpp | 8 +- tests/math/testpivector2d.cpp | 815 ++++++++++++++++++++- 7 files changed, 1724 insertions(+), 375 deletions(-) diff --git a/.gitignore b/.gitignore index 1acefdd3..1c093651 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ CMakeLists.txt.user* /include /release /build* +/AGENTS.md +/plans diff --git a/CMakeLists.txt b/CMakeLists.txt index 88f677c5..4e4740bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ if (POLICY CMP0177) endif() project(PIP) set(PIP_MAJOR 5) -set(PIP_MINOR 5) -set(PIP_REVISION 5) +set(PIP_MINOR 6) +set(PIP_REVISION 0) set(PIP_SUFFIX ) set(PIP_COMPANY SHS) set(PIP_DOMAIN org.SHS) diff --git a/README.md b/README.md index 11691014..236ceaf7 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,118 @@ You should add ${} to your target. [🇷🇺 Онлайн документация](https://shstk.ru/pip/html/ru/index.html) [🇷🇺 Qt-help](https://shstk.ru/pip/pip_ru.qch) + +## Основные опции сборки + +### Стандартные опции (option()) +| Опция | Описание | По умолчанию | +|-------|----------|--------------| +| `ICU` | ICU support для конвертации кодовых страниц | ON (кроме Win/Android/Apple) | +| `STD_IOSTREAM` | Поддержка std::iostream операторов | OFF | +| `INTROSPECTION` | Сборка с интроспекцией | OFF | +| `TESTS` | Сборка тестов | OFF | +| `COVERAGE` | Сборка с информацией о покрытии | OFF | +| `PIP_FFTW_F` | Поддержка FFTW для float | ON | +| `PIP_FFTW_L` | Поддержка FFTW для long double | ON | +| `PIP_FFTW_Q` | Поддержка FFTW для quad double | OFF | + +### Опции модулей (PIP_BUILD_*) +| Опция | Модуль | +|-------|--------| +| `PIP_BUILD_CONSOLE` | console | +| `PIP_BUILD_CRYPT` | crypt (требует libsodium) | +| `PIP_BUILD_COMPRESS` | compress (требует zlib) | +| `PIP_BUILD_USB` | usb | +| `PIP_BUILD_FFTW` | fftw | +| `PIP_BUILD_OPENCL` | opencl | +| `PIP_BUILD_IO_UTILS` | io_utils | +| `PIP_BUILD_CLIENT_SERVER` | client_server | +| `PIP_BUILD_CLOUD` | cloud | +| `PIP_BUILD_LUA` | lua | +| `PIP_BUILD_HTTP_CLIENT` | http_client (требует libcurl) | +| `PIP_BUILD_HTTP_SERVER` | http_server (требует libmicrohttpd) | + +### Дополнительные переменные +| Переменная | Описание | +|------------|----------| +| `PIP_BUILD_DEBUG` | Сборка debug версии | +| `PIP_FREERTOS` | Режим сборки для FreeRTOS | +| `CROSSTOOLS` | Собрать инструменты кросс-сборки под хостовую систему (pip_cmg, pip_rc, ...) | +| `LOCAL` | Локальная установка (bin/lib/include) | +| `PIP_CONTAINERS_MIN_ALLOC` | Переопределить минимальный размер аллокации контейнеров | +| `PIP_CONTAINERS_MAX_POT_ALLOC` | Переопределить максимальный размер дополнительной аллокации (поддерживает X_KiB, X_MiB) | + +### Примеры использования +```bash +# Базовая сборка с тестами +cmake -B build -DTESTS=ON + +# Сборка с покрытием и ICU +cmake -B build -DTESTS=ON -DCOVERAGE=ON -DICU=ON + +# Отключение отдельных модулей +cmake -B build -DPIP_BUILD_CRYPT=OFF -DPIP_BUILD_OPENCL=OFF + +# Переопределение параметров контейнеров +cmake -B build -DPIP_CONTAINERS_MIN_ALLOC=64 + +# Локальная установка +cmake -B build -DLOCAL=ON +``` + +## PIP Dependencies + +### Встроенные (included in 3rd/) + +| Библиотека | Назначение | Модуль PIP | +|------------|------------|------------| +| **PCRE2** | Регулярные выражения | main (internal) | +| **BLAKE2** | Хеширование | main (internal) | +| **SipHash** | Хеширование | main (internal) | +| **Lua** | Lua scripting | lua | +| **LuaBridge** | Lua bindings | lua | + +### Внешние (системные) + +| Библиотека | Опция | Модуль PIP | +|------------|-------|------------| +| **ICU** | `-DICU=ON` | main (string conversion) | +| **zlib** | `PIP_BUILD_COMPRESS` | compress | +| **libsodium** | `PIP_BUILD_CRYPT` | crypt, io_utils, cloud | +| **libusb** | `PIP_BUILD_USB` | usb | +| **FFTW3** (+ threads) | `PIP_BUILD_FFTW` | fftw | +| **OpenCL** | `PIP_BUILD_OPENCL` | opencl | +| **libmicrohttpd** | `PIP_BUILD_HTTP_SERVER` | http_server | +| **libcurl** | `PIP_BUILD_HTTP_CLIENT` | http_client | + +### Опциональные (тесты/документация) + +| Инструмент | Назначение | +|------------|------------| +| **Google Test** | Тестирование (fetched automatically) | +| **Doxygen** | Генерация документации | + + +### Схема зависимостей модулей + +``` +main (core) +├── PCRE2 (встроен) +├── BLAKE2 (встроен) +├── SipHash (встроен) +└── ICU (опционально) + +console → main +compress → zlib +crypt → libsodium +usb → libusb +fftw → FFTW3 +opencl → OpenCL +io_utils → [crypt, если доступен] +client_server → io_utils +cloud → io_utils, crypt +lua → Lua (встроен), LuaBridge (встроен) +http_server → libmicrohttpd +http_client → libcurl +``` + diff --git a/libs/main/containers/pivector.h b/libs/main/containers/pivector.h index 59f0786d..a6b7a766 100644 --- a/libs/main/containers/pivector.h +++ b/libs/main/containers/pivector.h @@ -835,8 +835,8 @@ public: //! piCout << v.contains({1,4}); // true //! piCout << v.contains({1,5}); // false //! \endcode - //! \~\sa \a every(), \a any(), \a entries(), \a forEach() - inline bool contains(const PIVector & v, ssize_t start = 0) const { + //! \~\sa \a every(), \a any(), \a entries(), \a forEach(), \a contains() + inline bool containsAll(const PIVector & v, ssize_t start = 0) const { if (start < 0) { start = piv_size + start; if (start < 0) start = 0; @@ -854,6 +854,24 @@ public: return true; } + //! \~english Tests if any element of `v` exists in the array. + //! \~russian Проверяет наличие хотя бы одного из элементов `v` в массиве. + //! \~\sa \a containsAll(), \a contains() + inline bool containsAny(const PIVector & v, ssize_t start = 0) const { + if (start < 0) { + start = piv_size + start; + if (start < 0) start = 0; + } + for (const T & e: v) { + for (size_t i = start; i < piv_size; ++i) { + if (e == piv_data[i]) { + return true; + } + } + } + return false; + } + //! \~english Count elements equal `e` in the array. //! \~russian Подсчитывает количество элементов, совпадающих с элементом `e` в массиве. //! \~\details @@ -1303,14 +1321,16 @@ public: //! piCout << v; // {1, 3, 7, 5} //! \endcode //! \~\sa \a append(), \a prepend(), \a remove() - inline PIVector & insert(size_t index, const T & e = T()) { - alloc(piv_size + 1); - if (index < piv_size - 1) { - const size_t os = piv_size - index - 1; - memmove(reinterpret_cast(piv_data + index + 1), reinterpret_cast(piv_data + index), os * sizeof(T)); + inline PIVector & insert(size_t index, const T & e = T(), size_t count = 1) { + alloc(piv_size + count); + if (index < piv_size - count) { + const size_t os = piv_size - index - count; + memmove(reinterpret_cast(piv_data + index + count), reinterpret_cast(piv_data + index), os * sizeof(T)); + } + PIINTROSPECTION_CONTAINER_USED(T, count) + for (size_t i = 0; i < count; ++i) { + elementNew(piv_data + index + i, e); } - PIINTROSPECTION_CONTAINER_USED(T, 1) - elementNew(piv_data + index, e); return *this; } @@ -1349,8 +1369,8 @@ public: alloc(piv_size + v.piv_size); if (os > 0) { memmove(reinterpret_cast(piv_data + index + v.piv_size), - reinterpret_cast(piv_data + index), - os * sizeof(T)); + reinterpret_cast(piv_data + index), + os * sizeof(T)); } newT(piv_data + index, v.piv_data, v.piv_size); return *this; @@ -1372,8 +1392,8 @@ public: alloc(piv_size + init_list.size()); if (os > 0) { memmove(reinterpret_cast(piv_data + index + init_list.size()), - reinterpret_cast(piv_data + index), - os * sizeof(T)); + reinterpret_cast(piv_data + index), + os * sizeof(T)); } newT(piv_data + index, init_list.begin(), init_list.size()); return *this; diff --git a/libs/main/containers/pivector2d.h b/libs/main/containers/pivector2d.h index 7bd8a990..f1b019ea 100644 --- a/libs/main/containers/pivector2d.h +++ b/libs/main/containers/pivector2d.h @@ -25,7 +25,6 @@ #ifndef PIVECTOR2D_H #define PIVECTOR2D_H -#include "pipair.h" #include "pivector.h" //! \addtogroup Containers @@ -43,7 +42,7 @@ //! (use \a resize(), \a addRow(), \a removeRow(), \a removeColumn() instead), but you can modify the values of existing elements. //! \~russian //! Этот класс используется для хранения двумерного массива элементов любого типа в виде единого непрерывного блока памяти (обычного -//! PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со +//! \a PIVector). Доступ к элементам осуществляется с помощью операторов `[][]`, где первый индекс — это строка, а второй — столбец. Со //! строками можно работать как с объектами \a PIVector, что позволяет изменять отдельные элементы или присваивать целые строки. Нельзя //! напрямую добавлять или удалять элементы, чтобы изменить размеры массива после создания (используйте \a resize(), \a addRow(), \a //! removeRow(), \a removeColumn() для этого), но можно изменять значения существующих элементов. @@ -52,21 +51,51 @@ template class PIVector2D { public: - //! \~english Constructs an empty 2D array. - //! \~russian Создает пустой двумерный массив. + //! \brief + //! \~english Index structure for 2D array elements (row, column). + //! \~russian Структура индекса для элементов двумерного массива (строка, столбец). + struct Index { + //! \~english Row index in the 2D array. + //! \~russian Индекс строки в двумерном массиве. + ssize_t row = -1; + //! \~english Column index in the 2D array. + //! \~russian Индекс столбца в двумерном массиве. + ssize_t col = -1; + + //! \~english Default constructor. Initializes row and col to -1 (invalid index). + //! \~russian Конструктор по умолчанию. Инициализирует row и col значениями -1 (некорректный индекс). + inline Index() = default; + //! \~english Constructs an Index with the given row and column values. + //! \~russian Создаёт Index с заданными значениями строки и столбца. + inline Index(ssize_t r, ssize_t c): row(r), col(c) {} + + //! \~english Checks if the index is valid (both row and column are non-negative). + //! \~russian Проверяет, является ли индекс корректным (строка и столбец неотрицательны). + //! \~\sa isNotValid() + inline bool isValid() const { return row >= 0 && col >= 0; } + + //! \~english Checks if the index is invalid (either row or column is negative). + //! \~russian Проверяет, является ли индекс некорректным (строка или столбец отрицательны). + //! \~\sa isValid() + inline bool isNotValid() const { return !isValid(); } + }; + + //! \~english Constructs an empty 2D array. No memory is allocated. + //! \~russian Создаёт пустой двумерный массив. Память не выделяется. + //! \details + //! \~english After this constructor, \a rows() and \a cols() return 0, and \a isEmpty() returns true. + //! \~russian После этого конструктора \a rows() и \a cols() возвращают 0, а \a isEmpty() возвращает true. + //! \~\sa PIVector::PIVector() inline PIVector2D() { rows_ = cols_ = 0; } //! \~english Constructs a 2D array with the given dimensions, filled with copies of `f`. - //! \~russian Создает двумерный массив заданного размера, заполненный копиями `f`. - //! \param rows Number of rows. - //! \param cols Number of columns. - //! \param f Value to fill the array with. - //! \~english \param rows Количество строк. - //! \~russian \param rows Количество строк. - //! \~english \param cols Количество столбцов. - //! \~russian \param cols Количество столбцов. - //! \~english \param f Значение для заполнения массива. - //! \~russian \param f Значение для заполнения массива. + //! \~russian Создаёт двумерный массив заданного размера, заполненный копиями `f`. + //! \details + //! \~english The underlying storage is a single contiguous block of memory of size `rows * cols`. + //! All elements are initialized with the value `f`. + //! \~russian Внутреннее хранилище представляет собой единый непрерывный блок памяти размером `rows * cols`. + //! Все элементы инициализируются значением `f`. + //! \~\sa PIVector::PIVector(size_t, const T&) inline PIVector2D(size_t rows, size_t cols, const T & f = T()) { rows_ = rows; cols_ = cols; @@ -74,22 +103,35 @@ public: } //! \~english Constructs a 2D array from an existing 1D vector, reshaping it. - //! \~russian Создает двумерный массив из существующего одномерного вектора, изменяя его форму. - //! \param rows Number of rows. - //! \param cols Number of columns. - //! \param v The source 1D vector. Its size must be at least `rows * cols`. + //! \~russian Создаёт двумерный массив из существующего одномерного вектора, изменяя его форму. + //! \details + //! \~english The constructor copies the data from `v` into the internal flat vector. + //! If `v` is larger than `rows * cols`, the excess elements are ignored (the vector is truncated). + //! If `v` is smaller, other values filled whith default cunstructor T() + //! \~russian Конструктор копирует данные из `v` во внутренний плоский вектор. + //! Если `v` больше, чем `rows * cols`, лишние элементы игнорируются (вектор обрезается). + //! Если `v` меньше, остальные значения будут заполнены из конструктора по умолчанию T() + //! \~\sa PIVector::PIVector(const PIVector&), reshape() inline PIVector2D(size_t rows, size_t cols, const PIVector & v): rows_(rows), cols_(cols), mat(v) { mat.resize(rows * cols); } //! \~english Move constructs a 2D array from an existing 1D vector, reshaping it. //! \~russian Конструктор перемещения из существующего одномерного вектора, изменяя его форму. - //! \param rows Number of rows. - //! \param cols Number of columns. - //! \param v The source 1D vector (rvalue reference). Its size must be at least `rows * cols`. + //! \details + //! \~english The data is moved from `v` into the internal flat vector, avoiding a copy. + //! After construction, `v` is left in a valid but unspecified state. + //! \~russian Данные перемещаются из `v` во внутренний плоский вектор, что позволяет избежать копирования. + //! После завершения конструктора `v` остаётся в корректном, но неопределённом состоянии. + //! \~\sa PIVector::PIVector(PIVector&&) inline PIVector2D(size_t rows, size_t cols, PIVector && v): rows_(rows), cols_(cols), mat(std::move(v)) { mat.resize(rows * cols); } //! \~english Constructs a 2D array from a vector of vectors (jagged array). Assumes all inner vectors have the same size. - //! \~russian Создает двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют - //! одинаковый размер. \param v The source vector of vectors. + //! \~russian Создаёт двумерный массив из вектора векторов (рваного массива). Предполагается, что все внутренние векторы имеют + //! одинаковый размер. + //! \details + //! \~english If the input is empty, the constructed array is also empty. Otherwise, the number of columns is taken from the size of the + //! first inner vector. All inner vectors are concatenated in the internal flat storage. + //! \~russian Если входной массив пуст, создаётся пустой двумерный массив. В противном случае количество столбцов берётся из размера + //! первого внутреннего вектора. Все внутренние векторы конкатенируются во внутреннем плоском хранилище. \sa PIVector::append() inline PIVector2D(const PIVector> & v) { rows_ = v.size(); if (rows_) { @@ -103,240 +145,97 @@ public: if (mat.isEmpty()) rows_ = cols_ = 0; } - //! \~english Number of rows. - //! \~russian Количество строк. + //! \~english Returns the number of rows in the 2D array. + //! \~russian Возвращает количество строк в двумерном массиве. + //! \return Number of rows. + //! \details + //! \~english The result is always non-negative. If the array is empty, returns 0. + //! \~russian Результат всегда неотрицательный. Если массив пуст, возвращает 0. + //! \~\sa cols(), size(), PIVector::size() inline size_t rows() const { return rows_; } - //! \~english Number of columns. - //! \~russian Количество столбцов. + //! \~english Returns the number of columns in the 2D array. + //! \~russian Возвращает количество столбцов в двумерном массиве. + //! \return Number of columns. + //! \details + //! \~english The result is always non-negative. If the array is empty, returns 0. + //! \~russian Результат всегда неотрицательный. Если массив пуст, возвращает 0. + //! \~\sa rows(), size(), PIVector::size() inline size_t cols() const { return cols_; } - //! \~english Total number of elements (`rows * cols`). - //! \~russian Общее количество элементов (`строки * столбцы`). + //! \~english Returns the total number of elements (`rows * cols`). + //! \~russian Возвращает общее количество элементов (`строки * столбцы`). + //! \return Total number of elements. + //! \details + //! \~english This is equivalent to the size of the underlying flat vector. + //! \~russian Это эквивалентно размеру внутреннего плоского вектора. + //! \~\sa rows(), cols(), PIVector::size() inline size_t size() const { return mat.size(); } - //! \~english Total number of elements as signed value. - //! \~russian Общее количество элементов в виде знакового числа. + //! \~english Returns the total number of elements as a signed value. + //! \~russian Возвращает общее количество элементов в виде знакового числа. + //! \return Signed size. + //! \~\sa size(), PIVector::size_s() inline ssize_t size_s() const { return mat.size_s(); } - //! \~english Total number of elements. - //! \~russian Общее количество элементов. + //! \~english Returns the total number of elements (same as \a size()). + //! \~russian Возвращает общее количество элементов (то же, что и \a size()). + //! \return Total number of elements. + //! \~\sa size(), PIVector::length() inline size_t length() const { return mat.length(); } - //! \~english Number of elements that the underlying container has currently allocated space for. - //! \~russian Количество элементов, для которого сейчас выделена память во внутреннем контейнере. + //! \~english Returns the number of elements that the underlying container has currently allocated space for. + //! \~russian Возвращает количество элементов, для которого сейчас выделена память во внутреннем контейнере. + //! \return Capacity of the flat vector. + //! \details + //! \~english This value may be larger than \a size(). It indicates how many elements can be added before a reallocation is needed. + //! \~russian Это значение может быть больше, чем \a size(). Оно показывает, сколько элементов можно добавить до того, как потребуется + //! перераспределение памяти. \sa reserve(), PIVector::capacity() inline size_t capacity() const { return mat.capacity(); } //! \~english Checks if the array has no elements. //! \~russian Проверяет, пуст ли массив. + //! \return \c true if the array is empty, \c false otherwise. + //! \details + //! \~english An empty array has both rows and columns equal to 0. + //! \~russian Пустой массив имеет и строки, и столбцы равные 0. + //! \~\sa isNotEmpty(), PIVector::isEmpty() inline bool isEmpty() const { return mat.isEmpty(); } - //! \~english Checks if the array has elements. + //! \~english Checks if the array has at least one element. //! \~russian Проверяет, не пуст ли массив. + //! \return \c true if the array is not empty, \c false otherwise. + //! \~\sa isEmpty(), PIVector::isNotEmpty() inline bool isNotEmpty() const { return mat.isNotEmpty(); } + class RowConst; + class ColConst; + class Row; + class Col; - //! \class Row - //! \brief - //! \~english Proxy class representing a single row in a \a PIVector2D for modification. - //! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D для модификации. - class Row { - friend class PIVector2D; - - private: - inline Row(PIVector2D * p, size_t row): p_(&(p->mat)) { - st_ = p->cols_ * row; - sz_ = p->cols_; - } - PIVector * p_; - size_t st_, sz_; - - public: - //! \~english Size of the row (number of columns). - //! \~russian Размер строки (количество столбцов). - inline size_t size() const { return sz_; } - - //! \~english Accesses the element at the given column index within the row. - //! \~russian Доступ к элементу по заданному индексу столбца в строке. - inline T & operator[](size_t index) { return (*p_)[st_ + index]; } - - //! \~english Const access to the element at the given column index within the row. - //! \~russian Константный доступ к элементу по заданному индексу столбца в строке. - inline const T & operator[](size_t index) const { return (*p_)[st_ + index]; } - - //! \~english Returns a pointer to the row data starting at an optional offset. - //! \~russian Возвращает указатель на данные строки, начиная с опционального смещения. - inline T * data(size_t index = 0) { return p_->data(st_ + index); } - - //! \~english Returns a const pointer to the row data starting at an optional offset. - //! \~russian Возвращает константный указатель на данные строки, начиная с опционального смещения. - inline const T * data(size_t index = 0) const { return p_->data(st_ + index); } - - //! \~english Assigns the contents of another Row to this row. - //! \~russian Присваивает этой строке содержимое другой строки. - inline Row & operator=(const Row & other) { - if (p_ == other.p_ && st_ == other.st_) return *this; - const size_t sz = piMin(sz_, other.sz_); - p_->_copyRaw(p_->data(st_), other.data(), sz); - return *this; - } - - //! \~english Assigns the contents of a \a PIVector to this row. - //! \~russian Присваивает этой строке содержимое \a PIVector. - inline Row & operator=(const PIVector & other) { - const size_t sz = piMin(sz_, other.size()); - p_->_copyRaw(p_->data(st_), other.data(), sz); - return *this; - } - - //! \~english Converts the row to a \a PIVector. - //! \~russian Преобразует строку в \a PIVector. - inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } - - // --- Поиск в строке --- - inline ssize_t indexOf(const T & e, ssize_t start = 0) const { - if (start < 0) start = 0; - for (size_t i = (size_t)start; i < sz_; ++i) { - if ((*p_)[st_ + i] == e) return (ssize_t)i; - } - return -1; - } - inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { - ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; - for (ssize_t i = from; i >= 0; --i) { - if ((*p_)[st_ + i] == e) return i; - } - return -1; - } - inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { - if (start < 0) start = 0; - for (size_t i = (size_t)start; i < sz_; ++i) { - if (test((*p_)[st_ + i])) return (ssize_t)i; - } - return -1; - } - inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { - ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; - for (ssize_t i = from; i >= 0; --i) { - if (test((*p_)[st_ + i])) return i; - } - return -1; - } - }; - - //! \class Col - //! \brief - //! \~english Proxy class representing a single column in a \a PIVector2D for modification. - //! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D для модификации. - class Col { - friend class PIVector2D; - - private: - inline Col(PIVector2D * p, size_t col): p_(&(p->mat)) { - step_ = p->cols_; - col_ = col; - sz_ = p->rows_; - } - PIVector * p_; - size_t step_, col_, sz_; - - public: - //! \~english Size of the column (number of rows). - //! \~russian Размер столбца (количество строк). - inline size_t size() const { return sz_; } - - //! \~english Accesses the element at the given row index within the column. - //! \~russian Доступ к элементу по заданному индексу строки в столбце. - inline T & operator[](size_t index) { return (*p_)[index * step_ + col_]; } - - //! \~english Const access to the element at the given row index within the column. - //! \~russian Константный доступ к элементу по заданному индексу строки в столбце. - inline const T & operator[](size_t index) const { return (*p_)[index * step_ + col_]; } - - //! \~english Returns a pointer to the column data starting at an optional row offset. - //! \~russian Возвращает указатель на данные столбца, начиная с опционального смещения по строкам. - inline T * data(size_t index = 0) { return p_->data(index * step_ + col_); } - - //! \~english Returns a const pointer to the column data starting at an optional row offset. - //! \~russian Возвращает константный указатель на данные столбца, начиная с опционального смещения по строкам. - inline const T * data(size_t index = 0) const { return p_->data(index * step_ + col_); } - - //! \~english Assigns the contents of another Col to this column. - //! \~russian Присваивает этому столбцу содержимое другого столбца. - inline Col & operator=(const Col & other) { - if (p_ == other.p_ && col_ == other.col_) return *this; - const size_t sz = piMin(sz_, other.sz_); - for (size_t i = 0; i < sz; ++i) - (*p_)[i * step_ + col_] = other[i]; - return *this; - } - - //! \~english Assigns the contents of a \a PIVector to this column. - //! \~russian Присваивает этому столбцу содержимое \a PIVector. - inline Col & operator=(const PIVector & other) { - const size_t sz = piMin(sz_, other.size()); - for (size_t i = 0; i < sz; ++i) - (*p_)[i * step_ + col_] = other[i]; - return *this; - } - - //! \~english Converts the column to a \a PIVector. - //! \~russian Преобразует столбец в \a PIVector. - inline PIVector toVector() const { - PIVector ret; - ret.reserve(sz_); - for (size_t i = 0; i < sz_; i++) - ret << (*p_)[i * step_ + col_]; - return ret; - } - - // --- Поиск в столбце --- - inline ssize_t indexOf(const T & e, ssize_t start = 0) const { - if (start < 0) start = 0; - for (size_t i = (size_t)start; i < sz_; ++i) { - if ((*p_)[i * step_ + col_] == e) return (ssize_t)i; - } - return -1; - } - inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { - ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; - for (ssize_t i = from; i >= 0; --i) { - if ((*p_)[i * step_ + col_] == e) return i; - } - return -1; - } - inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { - if (start < 0) start = 0; - for (size_t i = (size_t)start; i < sz_; ++i) { - if (test((*p_)[i * step_ + col_])) return (ssize_t)i; - } - return -1; - } - inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { - ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; - for (ssize_t i = from; i >= 0; --i) { - if (test((*p_)[i * step_ + col_])) return i; - } - return -1; - } - }; //! \class RowConst //! \brief //! \~english Proxy class representing a single read-only row in a \a PIVector2D. //! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D только для чтения. + //! \details + //! \~english Returned by const \a operator[] or \a row(). Provides const access to row elements. + //! \~russian Возвращается константными версиями \a operator[] или \a row(). Предоставляет константный доступ к элементам строки. + //! \~\sa Row, ColConst class RowConst { friend class PIVector2D; - private: - inline RowConst(const PIVector2D * p, size_t row): p_(&(p->mat)) { - st_ = p->cols_ * row; - sz_ = p->cols_; - } + protected: + inline RowConst(const PIVector2D * p, size_t row): p_(&(p->mat)), st_(p->cols_ * row), sz_(p->cols_) {} const PIVector * p_; - size_t st_, sz_; + const size_t st_, sz_; public: + //! \~english Copy constructor from modifiable Row to read-only RowConst. + //! \~russian Конструктор копирования из модифицируемого класса Row в константный RowConst. + //! \~\sa Row + inline RowConst(const PIVector2D::Row & r): p_(r.p_), st_(r.st_), sz_(r.sz_) {} + //! \~english Size of the row (number of columns). //! \~russian Размер строки (количество столбцов). inline size_t size() const { return sz_; } @@ -353,7 +252,12 @@ public: //! \~russian Преобразует строку в \a PIVector. inline PIVector toVector() const { return PIVector(p_->data(st_), sz_); } - // --- Поиск в строке (только чтение) --- + //! \~english Returns the first index of element `e` in the row, starting from `start`. + //! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`. + //! \details + //! \~english See \a PIVector::indexOf() for details on negative start handling. + //! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf(). + //! \~\sa PIVector::indexOf() inline ssize_t indexOf(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -361,6 +265,11 @@ public: } return -1; } + + //! \~english Returns the last index of element `e` in the row, searching backwards from `start`. + //! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`. + //! \return Index if found, -1 otherwise. + //! \~\sa PIVector::lastIndexOf() inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -368,6 +277,10 @@ public: } return -1; } + + //! \~english Returns the first index where the predicate `test` returns true, starting from `start`. + //! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`. + //! \~\sa PIVector::indexWhere() inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -375,6 +288,11 @@ public: } return -1; } + + //! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`. + //! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true, + //! выполняя поиск в обратном направлении от `start`. + //! \~\sa PIVector::lastIndexWhere() inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -382,25 +300,92 @@ public: } return -1; } + + //! \~english Applies a function to each element of the row (read-only). + //! \~russian Применяет функцию к каждому элементу строки (только чтение). + //! \details + //! \~english The function can't modify the elements. + //! \~russian Функция не может изменять элементы. + //! \~\sa forEach (modifiable) + inline void forEach(std::function func) const { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[st_ + i]); + } + } + + //! \~english Checks if the row contains the element `e`. + //! \~russian Проверяет, содержит ли строка элемент `e`. + //! \~\sa PIVector::contains() + inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } + + //! \~english Counts occurrences of `e` in the row. + //! \~russian Подсчитывает количество вхождений `e` в строке. + //! \~\sa PIVector::entries() + inline int entries(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[st_ + i] == e) ++count; + } + return count; + } + + //! \~english Counts elements in the row that pass the `test`. + //! \~russian Подсчитывает элементы в строке, проходящие `test`. + //! \~\sa PIVector::entries(std::function) + inline int entries(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[st_ + i])) ++count; + } + return count; + } + + //! \~english Tests if any element in the row passes the `test`. + //! \~russian Проверяет, проходит ли какой-либо элемент в строке `test`. + //! \~\sa PIVector::any() + inline bool any(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (test((*p_)[st_ + i])) return true; + } + return false; + } + + //! \~english Tests if all elements in the row pass the `test`. + //! \~russian Проверяет, проходят ли все элементы в строке `test`. + //! \~\sa PIVector::every() + inline bool every(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (!test((*p_)[st_ + i])) return false; + } + return true; + } }; + //! \class ColConst //! \brief //! \~english Proxy class representing a single read-only column in a \a PIVector2D. //! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D только для чтения. + //! \details + //! \~english Returned by const \a col(). Provides const access to column elements. + //! \~russian Возвращается константной версией \a col(). Предоставляет константный доступ к элементам столбца. + //! \~\sa Col, RowConst class ColConst { friend class PIVector2D; - private: - inline ColConst(const PIVector2D * p, size_t col): p_(&(p->mat)) { - step_ = p->cols_; - col_ = col; - sz_ = p->rows_; - } + protected: + inline ColConst(const PIVector2D * p, size_t col): p_(&(p->mat)), step_(p->cols_), col_(col), sz_(p->rows_) {} const PIVector * p_; - size_t step_, col_, sz_; + const size_t step_, col_, sz_; public: + //! \~english Copy constructor from modifiable Col to read-only ColConst. + //! \~russian Конструктор копирования из модифицируемого класса Col в константный ColConst. + //! \~\sa Col + inline ColConst(const PIVector2D::Col & c): p_(c.p_), step_(c.step_), col_(c.col_), sz_(c.sz_) {} + //! \~english Size of the column (number of rows). //! \~russian Размер столбца (количество строк). inline size_t size() const { return sz_; } @@ -423,7 +408,12 @@ public: return ret; } - // --- Поиск в столбце (только чтение) --- + //! \~english Returns the first index of element `e` in the row, starting from `start`. + //! \~russian Возвращает первый индекс элемента `e` в строке, начиная с позиции `start`. + //! \details + //! \~english See \a PIVector::indexOf() for details on negative start handling. + //! \~russian Подробнее об обработке отрицательного `start` см. \a PIVector::indexOf(). + //! \~\sa PIVector::indexOf() inline ssize_t indexOf(const T & e, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -431,6 +421,10 @@ public: } return -1; } + + //! \~english Returns the last index of element `e` in the row, searching backwards from `start`. + //! \~russian Возвращает последний индекс элемента `e` в строке, выполняя поиск в обратном направлении от `start`. + //! \~\sa PIVector::lastIndexOf() inline ssize_t lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -438,6 +432,10 @@ public: } return -1; } + + //! \~english Returns the first index where the predicate `test` returns true, starting from `start`. + //! \~russian Возвращает первый индекс, для которого предикат `test` возвращает true, начиная с `start`. + //! \~\sa PIVector::indexWhere() inline ssize_t indexWhere(std::function test, ssize_t start = 0) const { if (start < 0) start = 0; for (size_t i = (size_t)start; i < sz_; ++i) { @@ -445,6 +443,11 @@ public: } return -1; } + + //! \~english Returns the last index where the predicate `test` returns true, searching backwards from `start`. + //! \~russian Возвращает последний индекс, для которого предикат `test` возвращает true, + //! выполняя поиск в обратном направлении от `start`. + //! \~\sa PIVector::lastIndexWhere() inline ssize_t lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t from = (start < 0 || (size_t)start >= sz_) ? (ssize_t)sz_ - 1 : start; for (ssize_t i = from; i >= 0; --i) { @@ -452,22 +455,277 @@ public: } return -1; } + + //! \~english Applies a function to each element of the column (read-only). + //! \~russian Применяет функцию к каждому элементу столбца (только чтение). + //! \details + //! \~english The function can't modify the elements. + //! \~russian Функция не может изменять элементы. + //! \~\sa forEach (modifiable) + inline void forEach(std::function func) const { + for (size_t i = 0; i < sz_; ++i) { + func((*p_)[i * step_ + col_]); + } + } + + //! \~english Checks if the column contains the element `e`. + //! \~russian Проверяет, содержит ли столбец элемент `e`. + //! \~\sa PIVector::contains() + inline bool contains(const T & e, ssize_t start = 0) const { return indexOf(e, start) != -1; } + + //! \~english Counts occurrences of `e` in the column. + //! \~russian Подсчитывает количество вхождений `e` в столбце. + //! \~\sa PIVector::entries() + inline int entries(const T & e, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if ((*p_)[i * step_ + col_] == e) ++count; + } + return count; + } + + //! \~english Counts elements in the column that pass the `test`. + //! \~russian Подсчитывает элементы в столбце, проходящие `test`. + //! \~\sa PIVector::entries(std::function) + inline int entries(std::function test, ssize_t start = 0) const { + if (start < 0) start = 0; + int count = 0; + for (size_t i = (size_t)start; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) ++count; + } + return count; + } + + //! \~english Tests if any element in the column passes the `test`. + //! \~russian Проверяет, проходит ли какой-либо элемент в столбце `test`. + //! \~\sa PIVector::any() + inline bool any(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (test((*p_)[i * step_ + col_])) return true; + } + return false; + } + + //! \~english Tests if all elements in the column pass the `test`. + //! \~russian Проверяет, проходят ли все элементы в столбце `test`. + //! \~\sa PIVector::every() + inline bool every(std::function test) const { + for (size_t i = 0; i < sz_; ++i) { + if (!test((*p_)[i * step_ + col_])) return false; + } + return true; + } }; + //! \class Row + //! \brief + //! \~english Proxy class representing a single row in a \a PIVector2D for modification. + //! \~russian Прокси-класс, представляющий одну строку в \a PIVector2D для модификации. + //! \details + //! \~english Objects of this class are returned by non-const \a operator[] or \a row(). + //! They provide array-like access to the elements of a specific row and allow operations such as assignment from another row or a \a + //! PIVector, searching, filling, and iteration. + //! \~russian Объекты этого класса возвращаются неконстантными операторами \a operator[] или методом \a row(). + //! Они предоставляют доступ к элементам конкретной строки, подобный массиву, и позволяют выполнять такие операции, как присваивание из + //! другой строки или \a PIVector, поиск, заполнение и итерацию. \sa Col, RowConst + class Row: public RowConst { + friend class PIVector2D; + + private: + inline Row(PIVector2D * p, size_t row): RowConst(p, row), p_(&(p->mat)) {} + PIVector * p_; + + public: + using RowConst::operator[]; + using RowConst::data; + using RowConst::size; + + //! \~english Accesses the element at the given column index within the row. + //! \~russian Доступ к элементу по заданному индексу столбца в строке. + //! \details + //! \~english No bounds checking is performed; use with caution. + //! \~russian Проверка границ не выполняется; используйте с осторожностью. + //! \~\sa PIVector::operator[] + inline T & operator[](size_t index) { return (*p_)[this->st_ + index]; } + + //! \~english Returns a pointer to the row data starting at an optional offset. + //! \~russian Возвращает указатель на данные строки, начиная с опционального смещения. + //! \details + //! \~english The pointer can be used for direct memory operations. It remains valid as long as the underlying 2D array is not + //! reallocated. + //! \~russian Указатель можно использовать для прямых операций с памятью. Он остаётся действительным, пока не произойдёт + //! перераспределение памяти внутреннего двумерного массива. \sa PIVector::data() + inline T * data(size_t index = 0) { return p_->data(this->st_ + index); } + + //! \~english Assigns the contents of another Row to this row. + //! \~russian Присваивает этой строке содержимое другой строки. + //! \details + //! \~english Only the minimum of the two row sizes is copied; if this row is shorter, excess elements in `other` are ignored. + //! \~russian Копируется только минимум из размеров двух строк; если эта строка короче, лишние элементы из `other` игнорируются. + //! \~\sa PIVector::operator= + inline Row & operator=(const Row & other) { + if (p_ == other.p_ && this->st_ == other.st_) return *this; + const size_t sz = piMin(this->sz_, other.sz_); + p_->_copyRaw(p_->data(this->st_), other.data(), sz); + return *this; + } + + //! \~english Assigns the contents of a \a PIVector to this row. + //! \~russian Присваивает этой строке содержимое \a PIVector. + //! \details + //! \~english Only the minimum of the row size and vector size is copied. + //! \~russian Копируется только минимум из размера строки и размера вектора. + //! \~\sa PIVector::operator= + inline Row & operator=(const PIVector & other) { + const size_t sz = piMin(this->sz_, other.size()); + p_->_copyRaw(p_->data(this->st_), other.data(), sz); + return *this; + } + + //! \~english Applies a function to each element of the row (modifiable). + //! \~russian Применяет функцию к каждому элементу строки (с возможностью изменения). + //! \param func Function that takes a reference to T. + //! \details + //! \~english The function can modify the elements. + //! \~russian Функция может изменять элементы. + //! \~\sa PIVector::forEach() + inline void forEach(std::function func) { + for (size_t i = 0; i < this->sz_; ++i) { + func((*p_)[this->st_ + i]); + } + } + + //! \~english Fills the row with copies of `value`. + //! \~russian Заполняет строку копиями `value`. + //! \~\sa PIVector::fill() + inline void fill(const T & value) { + for (size_t i = 0; i < this->sz_; ++i) { + (*p_)[this->st_ + i] = value; + } + } + }; + + + //! \class Col + //! \brief + //! \~english Proxy class representing a single column in a \a PIVector2D for modification. + //! \~russian Прокси-класс, представляющий один столбец в \a PIVector2D для модификации. + //! \details + //! \~english Objects of this class are returned by non-const \a col(). They provide column-wise access and operations similar to \a + //! Row. + //! \~russian Объекты этого класса возвращаются неконстантным методом \a col(). Они предоставляют доступ к столбцам и операции, + //! аналогичные \a Row. \sa Row, ColConst + class Col: public ColConst { + friend class PIVector2D; + + private: + inline Col(PIVector2D * p, size_t col): ColConst(p, col), p_(&(p->mat)) {} + PIVector * p_; + + public: + using ColConst::operator[]; + using ColConst::data; + using ColConst::size; + + //! \~english Accesses the element at the given row index within the column. + //! \~russian Доступ к элементу по заданному индексу строки в столбце. + //! \return Reference to the element. + inline T & operator[](size_t index) { return (*p_)[index * this->step_ + this->col_]; } + + //! \~english Returns a pointer to the column data starting at an optional row offset. + //! \~russian Возвращает указатель на данные столбца, начиная с опционального смещения по строкам. + //! \details + //! \~english Note that column elements are not stored contiguously in memory, so this pointer cannot be used to iterate over the + //! whole column. + //! \~russian Обратите внимание, что элементы столбца не хранятся в памяти непрерывно, поэтому этот указатель нельзя использовать + //! для итерации по всему столбцу. + inline T * data(size_t index = 0) { return p_->data(index * this->step_ + this->col_); } + + //! \~english Assigns the contents of another Col to this column. + //! \~russian Присваивает этому столбцу содержимое другого столбца. + inline Col & operator=(const Col & other) { + if (p_ == other.p_ && this->col_ == other.col_) return *this; + const size_t sz = piMin(this->sz_, other.sz_); + for (size_t i = 0; i < sz; ++i) + (*p_)[i * this->step_ + this->col_] = other[i]; + return *this; + } + + //! \~english Assigns the contents of a \a PIVector to this column. + //! \~russian Присваивает этому столбцу содержимое \a PIVector. + inline Col & operator=(const PIVector & other) { + const size_t sz = piMin(this->sz_, other.size()); + for (size_t i = 0; i < sz; ++i) + (*p_)[i * this->step_ + this->col_] = other[i]; + return *this; + } + + //! \~english Applies a function to each element of the column (modifiable). + //! \~russian Применяет функцию к каждому элементу столбца (с возможностью изменения). + //! \details + //! \~english The function can modify the elements. + //! \~russian Функция может изменять элементы. + //! \~\sa PIVector::forEach() + inline void forEach(std::function func) { + for (size_t i = 0; i < this->sz_; ++i) { + func((*p_)[i * this->step_ + this->col_]); + } + } + + //! \~english Fills the column with copies of `value`. + //! \~russian Заполняет столбец копиями `value`. + //! \~\sa PIVector::fill() + inline void fill(const T & value) { + for (size_t i = 0; i < this->sz_; ++i) { + (*p_)[i * this->step_ + this->col_] = value; + } + } + }; + + //! \~english Returns a reference to the element at the given row and column. //! \~russian Возвращает ссылку на элемент по заданной строке и столбцу. + //! \details + //! \~english No bounds checking is performed. + //! \~russian Проверка границ не выполняется. + //! \~\sa at() (const version), PIVector::operator[] inline T & element(size_t row, size_t col) { return mat[row * cols_ + col]; } //! \~english Returns a const reference to the element at the given row and column. //! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу. inline const T & element(size_t row, size_t col) const { return mat[row * cols_ + col]; } - //! \~english Returns a const reference to the element at the given row and column (bounds-checked only in debug). - //! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу (проверка границ только в отладочном режиме). + //! \~english Returns a const reference to the element at the given row and column + //! \~russian Возвращает константную ссылку на элемент по заданной строке и столбцу + //! \details + //! \~english No bounds checking is performed. + //! \~russian Проверка границ не выполняется. inline const T & at(size_t row, size_t col) const { return mat[row * cols_ + col]; } + //! \~english Returns a reference to the element at the given Index. + //! \~russian Возвращает ссылку на элемент по заданному Index. + inline T & operator[](const Index & idx) { return element(idx.row, idx.col); } + + //! \~english Returns a const reference to the element at the given Index. + //! \~russian Возвращает константную ссылку на элемент по заданному Index. + inline const T & operator[](const Index & idx) const { return element(idx.row, idx.col); } + + //! \~english Returns a reference to the element at the given Index. + //! \~russian Возвращает ссылку на элемент по заданному Index. + inline T & element(const Index & idx) { return element(idx.row, idx.col); } + + //! \~english Returns a const reference to the element at the given Index. + //! \~russian Возвращает константную ссылку на элемент по заданному Index. + inline const T & element(const Index & idx) const { return element(idx.row, idx.col); } + + //! \~english Returns a const reference to the element at the given Index (bounds-checked only in debug). + //! \~russian Возвращает константную ссылку на элемент по заданному Index (проверка границ только в отладочном режиме). + inline const T & at(const Index & idx) const { return at(idx.row, idx.col); } + //! \~english Returns a proxy object for the row at the given index for modification. //! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации. + //! \~\sa row(), Col inline Row operator[](size_t index) { return Row(this, index); } //! \~english Returns a proxy object for the row at the given index for read-only access. @@ -476,6 +734,7 @@ public: //! \~english Returns a pointer to the underlying flat data starting at an optional offset. //! \~russian Возвращает указатель на внутренние плоские данные, начиная с опционального смещения. + //! \~\sa PIVector::data() inline T * data(size_t index = 0) { return mat.data(index); } //! \~english Returns a const pointer to the underlying flat data starting at an optional offset. @@ -485,6 +744,7 @@ public: //! \~english Returns a proxy object for the row at the given index for modification. //! \~russian Возвращает прокси-объект для строки по заданному индексу для модификации. + //! \~\sa operator[] inline Row row(size_t index) { return Row(this, index); } //! \~english Returns a proxy object for the row at the given index for read-only access. @@ -493,21 +753,13 @@ public: //! \~english Returns a proxy object for the column at the given index for modification. //! \~russian Возвращает прокси-объект для столбца по заданному индексу для модификации. + //! \~\sa col() const inline Col col(size_t index) { return Col(this, index); } //! \~english Returns a proxy object for the column at the given index for read-only access. //! \~russian Возвращает прокси-объект для столбца по заданному индексу только для чтения. inline ColConst col(size_t index) const { return ColConst(this, index); } - - //! \~english Replaces a row with the contents of another Row object. - //! \~russian Заменяет строку содержимым другого объекта Row. - inline PIVector2D & setRow(size_t row, const Row & other) { - const size_t sz = piMin(cols_, other.sz_); - mat._copyRaw(mat.data(cols_ * row), other.data(), sz); - return *this; - } - //! \~english Replaces a row with the contents of a read-only RowConst object. //! \~russian Заменяет строку содержимым объекта RowConst только для чтения. inline PIVector2D & setRow(size_t row, const RowConst & other) { @@ -526,25 +778,18 @@ public: //! \~english Appends a new row to the bottom of the array from another Row object. //! \~russian Добавляет новую строку в конец массива из другого объекта Row. - inline PIVector2D & addRow(const Row & other) { - if (cols_ == 0) cols_ = other.sz_; - const size_t sz = piMin(cols_, other.sz_); - const size_t ps = mat.size(); - mat.resize(mat.size() + cols_); - mat._copyRaw(mat.data(ps), other.data(), sz); - rows_++; - return *this; - } - - //! \~english Appends a new row to the bottom of the array from a read-only RowConst object. - //! \~russian Добавляет новую строку в конец массива из объекта RowConst только для чтения. + //! \details + //! \~english If the array was empty, its column count is set to the size of the source row. + //! Otherwise, only `min(cols(), other.size())` elements are copied; the rest of the new row is default-initialized. + //! \~russian Если массив был пуст, количество столбцов устанавливается равным размеру исходной строки. + //! В противном случае копируется только `min(cols(), other.size())` элементов; остальные элементы новой строки инициализируются по + //! умолчанию. + //! \~\sa PIVector::push_back() inline PIVector2D & addRow(const RowConst & other) { if (cols_ == 0) cols_ = other.sz_; - const size_t sz = piMin(cols_, other.sz_); - const size_t ps = mat.size(); - mat.resize(mat.size() + cols_); - mat._copyRaw(mat.data(ps), other.data(), sz); + mat.append(other.toVector()); rows_++; + mat.resize(rows_ * cols_); return *this; } @@ -552,37 +797,191 @@ public: //! \~russian Добавляет новую строку в конец массива из \a PIVector. inline PIVector2D & addRow(const PIVector & other) { if (cols_ == 0) cols_ = other.size(); - const size_t sz = piMin(cols_, other.size()); - const size_t ps = mat.size(); - mat.resize(mat.size() + cols_); - mat._copyRaw(mat.data(ps), other.data(), sz); + mat.append(other); rows_++; + mat.resize(rows_ * cols_); + return *this; + } + + //! \~english Appends \a count new empty rows to the bottom of the array, filled with value \a f. + //! \~russian Добавляет \a count новых пустых строк в конец массива, заполненных значением \a f. + //! \details + //! \~english If the array was empty (no columns defined), the column count is set to 1. + //! The new rows are filled with the default value \a f. + //! \~russian Если массив был пуст (количество столбцов не определено), количество столбцов устанавливается равным 1. + //! Новые строки заполняются значением по умолчанию \a f. + //! \~\sa addRow(), appendColumns() + inline PIVector2D & appendRows(size_t count, const T & f = T()) { + if (count == 0) return *this; + if (cols_ == 0) ++cols_; + mat.resize(mat.size() + count * cols_, f); + rows_ += count; + return *this; + } + + //! \~english Appends \a count new empty columns to the end of each row of the array. + //! \~russian Добавляет \a count новых пустых столбцов в конец каждой строки массива. + //! \details + //! \~english If the array was empty (rows not defined), the array becomes a single row with \a count columns. + //! If the array already has rows, new elements are inserted at the end of each existing row. + //! \~russian Если массив был пуст (строки не определены), массив становится одной строкой с \a count столбцов. + //! Если массив уже содержит строки, новые элементы добавляются в конец каждой существующей строки. + //! \~\sa appendRows(), addColumn() + inline PIVector2D & appendColumns(size_t count, const T & f = T()) { + if (count == 0) return *this; + if (rows_ == 0) { + mat.resize(count, f); + rows_ = 1; + cols_ = count; + return *this; + } + + const size_t newCols = cols_ + count; + mat.reserve(rows_ * newCols); + for (size_t r = rows_; r > 0; --r) { + mat.insert(r * cols_, f, count); + } + + cols_ = newCols; + return *this; + } + + //! \~english Deletes `count` rows starting from the specified row index. + //! \~russian Удаляет `count` строк, начиная с указанного индекса строки. + //! \details + //! \~english Removes the specified rows from the array and updates the row count. If all elements are deleted (array becomes empty), + //! both rows and columns are set to 0. + //! \~russian Удаляет указанные строки из массива и обновляет количество строк. Если все элементы удалены (массив становится пустым), + //! количество строк и столбцов устанавливается в 0. + //! \~\sa deleteColumns() + inline PIVector2D & deleteRows(size_t row_start, size_t count) { + if (row_start >= rows_ || count == 0) return *this; + mat.remove(row_start * cols_, cols_ * count); + if (isEmpty()) { + cols_ = 0; + rows_ = 0; + } else { + rows_ -= count; + } + return *this; + } + + //! \~english Removes the specified columns from the array and updates the column count. + //! \~russian Удаляет указанные столбцы из массива и обновляет количество столбцов. + //! \details + //! \~english Removes \a count columns starting from \a col_start. If \a col_start is out of range or \a count is 0, + //! the function does nothing. If \a count extends beyond the last column, only available columns are deleted. + //! \~russian Удаляет \a count столбцов начиная с \a col_start. Если \a col_start выходит за границы или \a count равен 0, + //! функция ничего не делает. Если \a count выходит за последний столбец, удаляются только доступные столбцы. + //! \~\sa removeColumn(), deleteRows() + inline PIVector2D & deleteColumns(size_t col_start, size_t count) { + if (col_start >= cols_ || rows_ == 0) return *this; + count = piMin(count, cols_ - col_start); + if (count == 0) return *this; + for (size_t r = 0; r < rows_; ++r) { + mat.remove(r * (cols_ - count) + col_start, count); + } + cols_ -= count; + mat.resize(rows_ * cols_); + return *this; + } + + //! \~english Appends a new column to the right of the array from a \a ColConst. + //! \~russian Добавляет новую строку в конец массива из \a ColConst. + inline PIVector2D & addColumn(const ColConst & other) { + if (other.size() == 0) return *this; + if (isEmpty()) { + mat.reserve(other.size()); + for (size_t r = 0; r < other.size(); ++r) { + mat.append(other[r]); + } + rows_ = mat.size(); + cols_ = 1; + return *this; + } + + const size_t newCols = cols_ + 1; + mat.reserve(rows_ * newCols); + for (size_t r = rows_; r > 0; --r) { + if (r - 1 < other.size()) { + mat.insert(r * cols_, other[r - 1]); + } else { + mat.insert(r * cols_); + } + } + + cols_ = newCols; + return *this; + } + + //! \~english Appends a new column to the right of the array from a \a PIVector. + //! \~russian Добавляет новую строку в конец массива из \a PIVector. + inline PIVector2D & addColumn(const PIVector & other) { + if (other.size() == 0) return *this; + if (isEmpty()) { + mat.append(other); + rows_ = mat.size(); + cols_ = 1; + return *this; + } + + const size_t newCols = cols_ + 1; + mat.reserve(rows_ * newCols); + for (size_t r = rows_; r > 0; --r) { + if (r - 1 < other.size()) { + mat.insert(r * cols_, other[r - 1]); + } else { + mat.insert(r * cols_); + } + } + + cols_ = newCols; return *this; } //! \~english Resizes the 2D array to new dimensions. //! \~russian Изменяет размер двумерного массива. //! \details - //! \~english If the new dimensions are larger, new elements are filled with `f`. - //! If they are smaller, the array is truncated. - //! \~russian Если новые размеры больше, новые элементы заполняются `f`. - //! Если они меньше, массив обрезается. + //! \~english If the new dimensions are larger, new elements are appended and filled with copies of `f`. + //! If they are smaller, the array is truncated (excess elements are destroyed). The underlying memory may be reallocated. + //! \~russian Если новые размеры больше текущих, новые элементы добавляются в конец и заполняются копиями `f`. + //! Если новые размеры меньше, массив усекается (лишние элементы уничтожаются). Внутренняя память может быть перераспределена. + //! \code + //! PIVector2D mat(2, 3, 0); // 2x3 matrix filled with 0 + //! mat.resize(3, 4, 1); // becomes 3x4, new elements filled with 1 + //! \endcode + //! \~\sa PIVector::resize() inline PIVector2D & resize(size_t rows, size_t cols, const T & f = T()) { if (rows == rows_ && cols == cols_) return *this; - PIVector2D tmp(rows, cols, f); - size_t copyRows = piMin(rows_, rows); - size_t copyCols = piMin(cols_, cols); - for (size_t r = 0; r < copyRows; ++r) { - for (size_t c = 0; c < copyCols; ++c) { - tmp.element(r, c) = element(r, c); - } + if (rows_ == 0 || cols_ == 0) { + mat.resize(rows * cols, f); + rows_ = rows; + cols_ = cols; + return *this; + } + if (rows != rows_ && cols == cols_) { + mat.resize(rows * cols_, f); + rows_ = rows; + return *this; + } + if (cols > cols_) { + appendColumns(cols - cols_, f); + } + if (rows > rows_) { + appendRows(rows - rows_, f); + } + if (cols < cols_) { + deleteColumns(cols, cols_ - cols); + } + if (rows < rows_) { + deleteRows(rows, rows_ - rows); } - swap(tmp); return *this; } //! \~english Equality operator. //! \~russian Оператор равенства. + //! \~\sa PIVector::operator== inline bool operator==(const PIVector2D & t) const { if (cols_ != t.cols_ || rows_ != t.rows_) return false; return mat == t.mat; @@ -594,6 +993,10 @@ public: //! \~english Converts the 2D array to a vector of vectors (PIVector>). //! \~russian Преобразует двумерный массив в вектор векторов (PIVector>). + //! \details + //! \~english Each row vector is a copy of the corresponding row. + //! \~russian Каждый вектор-строка является копией соответствующей строки. + //! \~\sa fromVectors(), PIVector::PIVector(const T*, size_t) inline PIVector> toVectors() const { PIVector> ret; ret.reserve(rows_); @@ -616,6 +1019,10 @@ public: //! \~english Swaps this 2D array with another. //! \~russian Меняет местами этот двумерный массив с другим. + //! \details + //! \~english Swaps the flat vectors and the dimension members. Very fast, no memory allocation. + //! \~russian Обменивает внутренние плоские векторы и члены, хранящие размеры. Очень быстро, без выделения памяти. + //! \~\sa PIVector::swap() inline void swap(PIVector2D & other) { mat.swap(other.mat); piSwap(rows_, other.rows_); @@ -633,6 +1040,10 @@ public: //! \~english Clears the array, removing all elements and setting dimensions to 0. //! \~russian Очищает массив, удаляя все элементы и устанавливая размеры в 0. + //! \details + //! \~english The capacity of the underlying flat vector may remain unchanged. + //! \~russian Ёмкость внутреннего плоского вектора может остаться неизменной. + //! \~\sa PIVector::clear() inline void clear() { rows_ = cols_ = 0; mat.clear(); @@ -641,64 +1052,70 @@ public: //! \~english Checks if the underlying flat vector contains the element `e`. //! \~russian Проверяет, содержит ли внутренний плоский вектор элемент `e`. - inline bool contains(const T & e, ssize_t start = 0) const { return mat.contains(e, start); } - - //! \~english Checks if the underlying flat vector contains all elements of `v`. - //! \~russian Проверяет, содержит ли внутренний плоский вектор все элементы `v`. - inline bool contains(const PIVector & v, ssize_t start = 0) const { return mat.contains(v, start); } + //! \~\sa PIVector::contains() + inline bool contains(const T & e) const { return mat.contains(e); } //! \~english Counts occurrences of `e` in the underlying flat vector. //! \~russian Подсчитывает количество вхождений `e` во внутреннем плоском векторе. - inline int entries(const T & e, ssize_t start = 0) const { return mat.entries(e, start); } + //! \~\sa PIVector::entries() + inline int entries(const T & e) const { return mat.entries(e); } //! \~english Counts elements in the flat vector that pass the `test`. //! \~russian Подсчитывает элементы в плоском векторе, проходящие `test`. - inline int entries(std::function test, ssize_t start = 0) const { return mat.entries(test, start); } + //! \~\sa PIVector::entries(std::function) + inline int entries(std::function test) const { return mat.entries(test); } //! \~english Returns the first index (row, col) of `e` in the 2D array. //! \~russian Возвращает первый индекс (строка, столбец) элемента `e` в двумерном массиве. - inline PIPair indexOf(const T & e, ssize_t start = 0) const { - ssize_t flat = mat.indexOf(e, start); - if (flat < 0 || cols_ == 0) return PIPair(-1, -1); - return PIPair(flat / cols_, flat % cols_); + //! \~\sa PIVector::indexOf() + inline Index indexOf(const T & e) const { + ssize_t flat = mat.indexOf(e); + if (flat < 0 || cols_ == 0) return Index{-1, -1}; + return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Returns the first index (row, col) in the 2D array that passes the `test`. //! \~russian Возвращает первый индекс (строка, столбец) в двумерном массиве, проходящий `test`. - inline PIPair indexWhere(std::function test, ssize_t start = 0) const { + //! \~\sa PIVector::indexWhere() + inline Index indexWhere(std::function test, ssize_t start = 0) const { ssize_t flat = mat.indexWhere(test, start); - if (flat < 0 || cols_ == 0) return PIPair(-1, -1); - return PIPair(flat / cols_, flat % cols_); + if (flat < 0 || cols_ == 0) return Index{-1, -1}; + return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Returns the last index (row, col) of `e` in the 2D array. //! \~russian Возвращает последний индекс (строка, столбец) элемента `e` в двумерном массиве. - inline PIPair lastIndexOf(const T & e, ssize_t start = -1) const { + //! \~\sa PIVector::lastIndexOf() + inline Index lastIndexOf(const T & e, ssize_t start = -1) const { ssize_t flat = mat.lastIndexOf(e, start); - if (flat < 0 || cols_ == 0) return PIPair(-1, -1); - return PIPair(flat / cols_, flat % cols_); + if (flat < 0 || cols_ == 0) return Index{-1, -1}; + return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Returns the last index (row, col) in the 2D array that passes the `test`. //! \~russian Возвращает последний индекс (строка, столбец) в двумерном массиве, проходящий `test`. - inline PIPair lastIndexWhere(std::function test, ssize_t start = -1) const { + //! \~\sa PIVector::lastIndexWhere() + inline Index lastIndexWhere(std::function test, ssize_t start = -1) const { ssize_t flat = mat.lastIndexWhere(test, start); - if (flat < 0 || cols_ == 0) return PIPair(-1, -1); - return PIPair(flat / cols_, flat % cols_); + if (flat < 0 || cols_ == 0) return Index{-1, -1}; + return Index{flat / static_cast(cols_), flat % static_cast(cols_)}; } //! \~english Tests if any element in the flat vector passes the `test`. //! \~russian Проверяет, проходит ли какой-либо элемент в плоском векторе `test`. + //! \~\sa PIVector::any() inline bool any(std::function test) const { return mat.any(test); } //! \~english Tests if all elements in the flat vector pass the `test`. //! \~russian Проверяет, проходят ли все элементы в плоском векторе `test`. + //! \~\sa PIVector::every() inline bool every(std::function test) const { return mat.every(test); } //! \~english Fills the entire 2D array with copies of `e`. //! \~russian Заполняет весь двумерный массив копиями `e`. + //! \~\sa PIVector::fill() inline PIVector2D & fill(const T & e = T()) { mat.fill(e); return *this; @@ -706,6 +1123,7 @@ public: //! \~english Fills the entire 2D array using a generator function `f` based on flat index. //! \~russian Заполняет весь двумерный массив, используя функцию-генератор `f` на основе плоского индекса. + //! \~\sa PIVector::fill(std::function) inline PIVector2D & fill(std::function f) { mat.fill(f); return *this; @@ -717,6 +1135,7 @@ public: //! \~english Assigns new size and fills with value. //! \~russian Задаёт новый размер и заполняет значением. + //! \~\sa PIVector::assign(size_t, const T&) inline PIVector2D & assign(size_t rows, size_t cols, const T & f = T()) { mat.assign(rows * cols, f); rows_ = rows; @@ -725,9 +1144,15 @@ public: } - // TODO: исправить - при транспонировании количество строк становится количеством столбцов и наоборот //! \~english Returns a transposed 2D array (rows become columns and vice versa). //! \~russian Возвращает транспонированный двумерный массив (строки становятся столбцами и наоборот). + //! \details + //! \~english The element at (r, c) in the original becomes at (c, r) in the result. + //! \~russian Элемент (r, c) исходного массива становится элементом (c, r) в результате. + //! \code + //! PIVector2D mat(2, 3, ...); + //! auto t = mat.transposed(); // now 3x2 + //! \endcode inline PIVector2D transposed() const { if (isEmpty()) return PIVector2D(); PIVector2D result(cols_, rows_); @@ -741,6 +1166,7 @@ public: //! \~english Reverses the order of rows in place. //! \~russian Изменяет порядок строк на обратный на месте. + //! \~\sa reverseColumns(), PIVector::reverse() inline PIVector2D & reverseRows() { const size_t half = rows_ / 2; for (size_t i = 0; i < half; ++i) { @@ -755,6 +1181,7 @@ public: //! \~english Reverses the order of columns in each row in place. //! \~russian Изменяет порядок столбцов в каждой строке на обратный на месте. + //! \~\sa reverseRows(), PIVector::reverse() inline PIVector2D & reverseColumns() { for (size_t r = 0; r < rows_; ++r) { Row currentRow = row(r); @@ -768,10 +1195,14 @@ public: //! \~english Returns a sub-2D array (a range of rows and columns). //! \~russian Возвращает подмассив (диапазон строк и столбцов). + //! \details + //! \~english If the range exceeds the array boundaries, it is clipped. If rowCount or colCount is 0, an empty array is returned. + //! \~russian Если диапазон выходит за границы массива, он обрезается. Если rowCount или colCount равны 0, возвращается пустой массив. + //! \~\sa PIVector::getRange() inline PIVector2D getRange(size_t rowStart, size_t rowCount, size_t colStart, size_t colCount) const { if (rowStart >= rows_ || colStart >= cols_ || rowCount == 0 || colCount == 0) return PIVector2D(); - size_t actualRowCount = piMin(rowCount, rows_ - rowStart); - size_t actualColCount = piMin(colCount, cols_ - colStart); + const size_t actualRowCount = piMin(rowCount, rows_ - rowStart); + const size_t actualColCount = piMin(colCount, cols_ - colStart); PIVector2D result(actualRowCount, actualColCount); for (size_t r = 0; r < actualRowCount; ++r) { @@ -784,6 +1215,10 @@ public: //! \~english Applies a function to each element and returns a new 2D array of a different type. //! \~russian Применяет функцию к каждому элементу и возвращает новый двумерный массив другого типа. + //! \details + //! \~english The original array is not modified. + //! \~russian Исходный массив не изменяется. + //! \~\sa PIVector::map() template inline PIVector2D map(std::function f) const { return PIVector2D(rows_, cols_, mat.template map(f)); @@ -791,6 +1226,7 @@ public: //! \~english Applies a function (with row and col indices) to each element and returns a new 2D array. //! \~russian Применяет функцию (с индексами строки и столбца) к каждому элементу и возвращает новый двумерный массив. + //! \~\sa PIVector::mapIndexed() template inline PIVector2D mapIndexed(std::function f) const { PIVector mappedMat; @@ -803,20 +1239,22 @@ public: return PIVector2D(rows_, cols_, std::move(mappedMat)); } - // --- Итерация по строкам и столбцам --- //! \~english Applies a function to each row (modifiable). //! \~russian Применяет функцию к каждой строке (с возможностью изменения). + //! \~\sa forEachRow() const, PIVector::forEach() inline PIVector2D & forEachRow(std::function f) { for (size_t r = 0; r < rows_; ++r) f(row(r)); return *this; } + //! \~english Applies a function to each row (read-only). //! \~russian Применяет функцию к каждой строке (только чтение). inline void forEachRow(std::function f) const { for (size_t r = 0; r < rows_; ++r) f(row(r)); } + //! \~english Applies a function to each column (modifiable). //! \~russian Применяет функцию к каждому столбцу (с возможностью изменения). inline PIVector2D & forEachColumn(std::function f) { @@ -824,8 +1262,10 @@ public: f(col(c)); return *this; } + //! \~english Applies a function to each column (read-only). //! \~russian Применяет функцию к каждому столбцу (только чтение). + //! \param f Function taking a \a ColConst. inline void forEachColumn(std::function f) const { for (size_t c = 0; c < cols_; ++c) f(col(c)); @@ -833,6 +1273,7 @@ public: //! \~english Accumulates a value across all elements. //! \~russian Аккумулирует значение по всем элементам. + //! \~\sa PIVector::reduce() template inline ST reduce(std::function f, const ST & initial = ST()) const { return mat.template reduce(f, initial); @@ -840,6 +1281,7 @@ public: //! \~english Accumulates a value across all elements with indices. //! \~russian Аккумулирует значение по всем элементам с индексами. + //! \~\sa PIVector::reduceIndexed() template inline ST reduceIndexed(std::function f, const ST & initial = ST()) const { ST ret(initial); @@ -853,52 +1295,45 @@ public: //! \~english Removes a row from the 2D array. //! \~russian Удаляет строку из двумерного массива. - inline PIVector2D & removeRow(size_t row) { - if (row >= rows_) return *this; - size_t startIdx = row * cols_; - mat.remove(startIdx, cols_); - rows_--; - if (rows_ == 0) cols_ = 0; - return *this; - } + //! \details + //! \~english If the last row is removed and the array becomes empty, \a cols() is set to 0. + //! \~russian Если удаляется последняя строка и массив становится пустым, \a cols() устанавливается в 0. + //! \~\sa removeColumn(), PIVector::remove() + inline PIVector2D & removeRow(size_t row) { return deleteRows(row, 1); } //! \~english Removes a column from the 2D array. //! \~russian Удаляет столбец из двумерного массива. - inline PIVector2D & removeColumn(size_t col) { - if (col >= cols_ || rows_ == 0) return *this; - PIVector2D result(rows_, cols_ - 1); - for (size_t r = 0; r < rows_; ++r) { - for (size_t c = 0, nc = 0; c < cols_; ++c) { - if (c == col) continue; - result.element(r, nc++) = element(r, c); - } - } - swap(result); - return *this; - } + //! \details + //! \~english This operation is more expensive than removing a row because elements must be moved. + //! \~russian Эта операция дороже, чем удаление строки, поскольку требуется перемещение элементов. + //! \~\sa removeRow(), PIVector::remove() + inline PIVector2D & removeColumn(size_t col) { return deleteColumns(col, 1); } //! \~english Removes all rows that satisfy a condition. //! \~russian Удаляет все строки, удовлетворяющие условию. + //! \details + //! \~english Rows are removed from the bottom to avoid index shifting issues. + //! \~russian Строки удаляются снизу вверх, чтобы избежать проблем со смещением индексов. + //! \~\sa removeColumnsWhere(), PIVector::removeWhere() inline PIVector2D & removeRowsWhere(std::function test) { - ssize_t r = rows_ - 1; - while (r >= 0) { + ssize_t r = rows_; + while (--r >= 0) { if (test(RowConst(this, r))) { removeRow(r); } - --r; } return *this; } //! \~english Removes all columns that satisfy a condition. //! \~russian Удаляет все столбцы, удовлетворяющие условию. + //! \~\sa removeRowsWhere() inline PIVector2D & removeColumnsWhere(std::function test) { - ssize_t c = cols_ - 1; - while (c >= 0) { + ssize_t c = cols_; + while (--c >= 0) { if (test(ColConst(this, c))) { removeColumn(c); } - --c; } return *this; } @@ -906,6 +1341,7 @@ public: //! \~english Returns a new 2D array containing only the rows that pass the test. //! \~russian Возвращает новый двумерный массив, содержащий только строки, прошедшие проверку. + //! \~\sa filterColumns(), PIVector::filter() inline PIVector2D filterRows(std::function test) const { PIVector2D result; for (size_t r = 0; r < rows_; ++r) { @@ -919,6 +1355,7 @@ public: //! \~english Returns a new 2D array containing only the columns that pass the test. //! \~russian Возвращает новый двумерный массив, содержащий только столбцы, прошедшие проверку. + //! \~\sa filterRows() inline PIVector2D filterColumns(std::function test) const { if (isEmpty()) return PIVector2D(); PIVector goodCols; @@ -927,23 +1364,13 @@ public: goodCols << c; } } - PIVector2D result(rows_, goodCols.size()); - for (size_t r = 0; r < rows_; ++r) { - for (size_t gc = 0; gc < goodCols.size(); ++gc) { - result.element(r, gc) = element(r, goodCols[gc]); - } + PIVector2D result; + for (size_t gc = 0; gc < goodCols.size(); ++gc) { + result.addColumn(col(goodCols[gc])); } return result; } - //! \~english Returns a new 2D array (as a single row) containing only the elements that pass the test. - //! \~russian Возвращает новый двумерный массив (в виде одной строки), содержащий только элементы, прошедшие проверку. - inline PIVector2D filterElements(std::function test) const { - PIVector filtered = mat.filter(test); - if (filtered.isEmpty()) return PIVector2D(); - return PIVector2D(1, filtered.size(), filtered); - } - protected: size_t rows_, cols_; PIVector mat; diff --git a/libs/main/system/pihidevice.cpp b/libs/main/system/pihidevice.cpp index e1c67818..605c3c46 100644 --- a/libs/main/system/pihidevice.cpp +++ b/libs/main/system/pihidevice.cpp @@ -342,12 +342,12 @@ PIVector PIHIDevice::allDevices(bool try_open) { PIDir hid_dir("/sys/bus/hid/devices"_a); auto hid_devs = hid_dir.entries(); - for (auto hd: hid_devs) { + for (const auto & hd: hid_devs) { // piCout << d.path; if (!isDir(hd)) continue; PIDir dir_input(hd.path + "/input"_a); auto hid_inputs = dir_input.entries(); - for (auto hd_i: hid_inputs) { + for (const auto & hd_i: hid_inputs) { if (!isDir(hd_i)) continue; // now in /sys/bus/hid/devices//input/input // piCout << hd_i.path; @@ -364,7 +364,7 @@ PIVector PIHIDevice::allDevices(bool try_open) { PIDir dir_e(hd_i.path); PIStringList devs; auto dl_e = dir_e.entries(); - for (auto d_e: dl_e) { + for (const auto & d_e: dl_e) { if (!d_e.isDir() || d_e.flags[PIFile::FileInfo::Dot] || d_e.flags[PIFile::FileInfo::DotDot]) continue; devs << d_e.name(); } @@ -392,7 +392,7 @@ PIVector PIHIDevice::allDevices(bool try_open) { if (test_f.isClosed()) continue; } - ullong ev = readFile(hd_i.path + "/capabilities/ev"_a).toULLong(16); + // ullong ev = readFile(hd_i.path + "/capabilities/ev"_a).toULLong(16); auto readAxes = [readFile, checkBit, &hd_i, &dev](const PIString & file, bool is_relative) { PIVector ret; diff --git a/tests/math/testpivector2d.cpp b/tests/math/testpivector2d.cpp index 538348d7..a744becc 100644 --- a/tests/math/testpivector2d.cpp +++ b/tests/math/testpivector2d.cpp @@ -379,6 +379,419 @@ TEST_F(Vector2DTest, addRow_with_shorter_vector_uses_min) { } } +// ==================== APPEND ROWS TESTS ==================== + +TEST_F(Vector2DTest, appendRows_adds_rows_at_bottom) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + vec.appendRows(5, 42); + + EXPECT_EQ(vec.rows(), oldRows + 5); + EXPECT_EQ(vec.cols(), oldCols); + + // Original data preserved + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = 0; c < oldCols; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } + + // New rows filled with 42 + for (size_t r = oldRows; r < vec.rows(); ++r) { + for (size_t c = 0; c < oldCols; ++c) { + EXPECT_EQ(vec.element(r, c), 42); + } + } +} + +TEST_F(Vector2DTest, appendRows_with_zero_count_does_nothing) { + auto oldVec = vec; + vec.appendRows(0); + EXPECT_EQ(vec, oldVec); +} + +TEST_F(Vector2DTest, appendRows_on_empty_matrix) { + PIVector2D empty; + empty.appendRows(5, 99); + EXPECT_TRUE(empty.isNotEmpty()); + EXPECT_EQ(empty.rows(), 5); + EXPECT_EQ(empty.cols(), 1); + EXPECT_EQ(empty.size(), empty.entries(99)); +} + +TEST_F(Vector2DTest, appendRows_with_default_value) { + size_t oldRows = vec.rows(); + + vec.appendRows(3); + + for (size_t r = oldRows; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), 0); + } + } +} + +// ==================== APPEND COLUMNS TESTS ==================== + +TEST_F(Vector2DTest, appendColumns_adds_columns_at_right) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + vec.appendColumns(3, 99); + + EXPECT_EQ(vec.rows(), oldRows); + EXPECT_EQ(vec.cols(), oldCols + 3); + + // Original data preserved in original columns + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = 0; c < oldCols; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } + + // New columns filled with 99 + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = oldCols; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), 99); + } + } +} + +TEST_F(Vector2DTest, appendColumns_with_zero_count_does_nothing) { + auto oldVec = vec; + vec.appendColumns(0); + EXPECT_EQ(vec, oldVec); +} + +TEST_F(Vector2DTest, appendColumns_on_empty_matrix) { + PIVector2D empty; + empty.appendColumns(5, 99); + EXPECT_TRUE(empty.isNotEmpty()); + EXPECT_EQ(empty.cols(), 5); + EXPECT_EQ(empty.rows(), 1); + EXPECT_EQ(empty.size(), empty.entries(99)); +} + +TEST_F(Vector2DTest, appendColumns_single_column) { + auto oldVec = vec; + + vec.appendColumns(1, 77); + + EXPECT_EQ(vec.rows(), oldVec.rows()); + EXPECT_EQ(vec.cols(), oldVec.cols() + 1); + + // Check original data + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < oldVec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), oldVec.element(r, c)); + } + } + + // Check new column + for (size_t r = 0; r < vec.rows(); ++r) { + EXPECT_EQ(vec.element(r, oldVec.cols()), 77); + } +} + +// ==================== DELETE ROWS TESTS ==================== + +TEST_F(Vector2DTest, deleteRows_removes_rows_from_middle) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + vec.deleteRows(5, 3); + + EXPECT_EQ(vec.rows(), oldRows - 3); + EXPECT_EQ(vec.cols(), oldCols); + + // Rows before deleted remain + for (size_t r = 0; r < 5; ++r) { + for (size_t c = 0; c < oldCols; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } + + // Rows after deleted shifted up + for (size_t r = 5; r < vec.rows(); ++r) { + for (size_t c = 0; c < oldCols; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast((r + 3) * COLS_COUNT_INIT + c)); + } + } +} + +TEST_F(Vector2DTest, deleteRows_at_end_works) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + vec.deleteRows(oldRows - 2, 2); + + EXPECT_EQ(vec.rows(), oldRows - 2); + EXPECT_EQ(vec.cols(), oldCols); + + // All remaining rows should have original content + for (size_t r = 0; r < vec.rows(); ++r) { + for (size_t c = 0; c < oldCols; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } +} + +TEST_F(Vector2DTest, deleteRows_beyond_bounds_is_limited) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + size_t count = 10; + + vec.deleteRows(oldRows - 2, count); + + EXPECT_EQ(vec.rows(), oldRows - count); + EXPECT_EQ(vec.cols(), oldCols); + + // All remaining rows should have original content + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } + + // All new rows should have original content + for (size_t r = oldRows; r < vec.rows(); ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), static_cast((r+count) * COLS_COUNT_INIT + c)); + } + } +} + +TEST_F(Vector2DTest, deleteRows_invalid_start_does_nothing) { + auto oldVec = vec; + vec.deleteRows(oldVec.rows() + 10, 2); + EXPECT_EQ(vec, oldVec); +} + +TEST_F(Vector2DTest, deleteRows_zero_count_does_nothing) { + auto oldVec = vec; + vec.deleteRows(5, 0); + EXPECT_EQ(vec, oldVec); +} + +TEST_F(Vector2DTest, deleteRows_all_rows_creates_empty) { + vec.deleteRows(0, vec.rows()); + EXPECT_TRUE(vec.isEmpty()); + EXPECT_EQ(vec.rows(), 0); + EXPECT_EQ(vec.cols(), 0); +} + +// ==================== DELETE COLUMNS TESTS ==================== + +TEST_F(Vector2DTest, deleteColumns_removes_columns_from_middle) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + vec.deleteColumns(4, 3); + + EXPECT_EQ(vec.rows(), oldRows); + EXPECT_EQ(vec.cols(), oldCols - 3); + + // Columns before deleted remain + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = 0; c < 4; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } + + // Columns after deleted shifted left + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = 4; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c + 3)); + } + } +} + +TEST_F(Vector2DTest, deleteColumns_at_end_works) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + vec.deleteColumns(oldCols - 2, 2); + + EXPECT_EQ(vec.rows(), oldRows); + EXPECT_EQ(vec.cols(), oldCols - 2); + + // All remaining columns should have original content + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = 0; c < vec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } +} + +TEST_F(Vector2DTest, deleteColumns_beyond_bounds_is_limited) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + vec.deleteColumns(oldCols - 2, 10); + + EXPECT_EQ(vec.rows(), oldRows); + EXPECT_EQ(vec.cols(), oldCols - 2); +} + +TEST_F(Vector2DTest, deleteColumns_invalid_start_does_nothing) { + auto oldVec = vec; + vec.deleteColumns(oldVec.cols() + 10, 2); + EXPECT_EQ(vec, oldVec); +} + +TEST_F(Vector2DTest, deleteColumns_zero_count_does_nothing) { + auto oldVec = vec; + vec.deleteColumns(5, 0); + EXPECT_EQ(vec, oldVec); +} + +TEST_F(Vector2DTest, deleteColumns_all_columns_preserves_rows) { + vec.deleteColumns(0, vec.cols()); + EXPECT_EQ(vec.rows(), ROWS_COUNT_INIT); + EXPECT_EQ(vec.cols(), 0); + EXPECT_EQ(vec.size(), 0); +} + +// ==================== ADD COLUMN TESTS ==================== + +TEST_F(Vector2DTest, addColumn_appends_column_to_empty) { + PIVector2D empty; + PIVector newCol(5); + for (size_t i = 0; i < 5; ++i) + newCol[i] = static_cast(100 + i); + + empty.addColumn(newCol); + + EXPECT_EQ(empty.rows(), 5); + EXPECT_EQ(empty.cols(), 1); + for (size_t r = 0; r < 5; ++r) { + EXPECT_EQ(empty.element(r, 0), static_cast(100 + r)); + } +} + +TEST_F(Vector2DTest, addColumn_appends_column_to_existing) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + PIVector newCol(oldRows, [](size_t i){return -900 - (int)i;}); + vec.addColumn(newCol); + + EXPECT_EQ(vec.rows(), oldRows); + EXPECT_EQ(vec.cols(), oldCols + 1); + + // Check that old data is preserved + for (size_t r = 0; r < oldRows; ++r) { + for (size_t c = 0; c < oldCols; ++c) { + EXPECT_EQ(vec.element(r, c), static_cast(r * COLS_COUNT_INIT + c)); + } + } + + // Check new column + for (size_t r = 0; r < oldRows; ++r) { + EXPECT_EQ(vec.element(r, oldCols), -900 - (int)r); + } +} + +TEST_F(Vector2DTest, addColumn_with_shorter_vector_uses_min) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + size_t shortLen = oldRows - 10; + PIVector shortCol(shortLen, 777); + + vec.addColumn(shortCol); + + EXPECT_EQ(vec.cols(), oldCols + 1); + + // First shortLen rows should be 777 + for (size_t r = 0; r < shortLen; ++r) { + EXPECT_EQ(vec.element(r, oldCols), 777); + } + // Remaining rows should be default-initialized (0) + for (size_t r = shortLen; r < oldRows; ++r) { + EXPECT_EQ(vec.element(r, oldCols), 0); + } +} + +TEST_F(Vector2DTest, addColumn_with_longer_vector_truncates) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + size_t longLen = oldRows + 10; + PIVector longCol(longLen, 555); + + vec.addColumn(longCol); + + EXPECT_EQ(vec.cols(), oldCols + 1); + + // All rows should be 555 (only first oldRows elements are used) + for (size_t r = 0; r < oldRows; ++r) { + EXPECT_EQ(vec.element(r, oldCols), 555); + } +} + +TEST_F(Vector2DTest, addColumn_with_empty_source_does_nothing_on_empty) { + PIVector2D empty; + PIVector emptyCol; + + empty.addColumn(emptyCol); + EXPECT_TRUE(empty.isEmpty()); + EXPECT_EQ(empty.rows(), 0); + EXPECT_EQ(empty.cols(), 0); +} + +TEST_F(Vector2DTest, addColumn_with_empty_source_adds_default_column) { + auto oldVec = vec; + + vec.addColumn({}); + + EXPECT_EQ(vec.cols(), oldVec.cols()); + EXPECT_EQ(vec.rows(), oldVec.rows()); + + for (size_t r = 0; r < oldVec.rows(); ++r) { + for (size_t c = 0; c < oldVec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), oldVec.element(r, c)); + } + } +} + +TEST_F(Vector2DTest, addColumn_with_Col_proxy_works) { + auto oldVec = vec; + const size_t colIndex = 5; + + auto srcCol = oldVec.col(colIndex); + vec.addColumn(srcCol); + + EXPECT_EQ(vec.cols(), oldVec.cols() + 1); + EXPECT_EQ(vec.rows(), oldVec.rows()); + + for (size_t r = 0; r < oldVec.rows(); ++r) { + for (size_t c = 0; c < oldVec.cols(); ++c) { + EXPECT_EQ(vec.element(r, c), oldVec.element(r, c)); + } + } + for (size_t r = 0; r < vec.rows(); ++r) { + // EXPECT_EQ(vec.element(r, oldVec.cols()), int()); + EXPECT_EQ(vec.element(r, oldVec.cols()), oldVec.element(r, colIndex)); + } +} + +TEST_F(Vector2DTest, addColumn_with_ColConst_proxy_works) { + size_t oldRows = vec.rows(); + size_t oldCols = vec.cols(); + + const auto & constVec = vec; + auto srcCol = constVec.col(7); + // Need a non-const array to add to + PIVector2D mutableVec = vec; // copy + mutableVec.addColumn(srcCol); + + EXPECT_EQ(mutableVec.cols(), oldCols + 1); + + for (size_t r = 0; r < oldRows; ++r) { + EXPECT_EQ(mutableVec.element(r, oldCols), vec.element(r, 7)); + } +} + // ==================== RESIZE TESTS ==================== class Vector2DResizeTest: public Vector2DTest { protected: @@ -470,17 +883,15 @@ TEST_F(Vector2DTest, contains_finds_element_in_flat_vector) { TEST_F(Vector2DTest, contains_with_start_parameter_works) { int target = 10 * COLS_COUNT_INIT + 15; EXPECT_TRUE(vec.contains(target)); - EXPECT_TRUE(vec.contains(target, target)); // start exactly at target (inclusive) - EXPECT_FALSE(vec.contains(target, target + 1)); // start after target } TEST_F(Vector2DTest, contains_vector_of_elements_works) { PIVector searchFor; searchFor << 100 << 200 << 300; - EXPECT_TRUE(vec.contains(searchFor)); + EXPECT_TRUE(vec.asPlainVector().containsAll(searchFor)); searchFor << -999; - EXPECT_FALSE(vec.contains(searchFor)); + EXPECT_FALSE(vec.asPlainVector().containsAll(searchFor)); } TEST_F(Vector2DTest, entries_counts_occurrences) { @@ -652,6 +1063,53 @@ TEST_F(Vector2DTest, transposed_returns_correct_dimensions) { } } +TEST(Vector2DTransposeTest, emptyMatrix_returnsEmpty) { + PIVector2D empty; + auto transposed = empty.transposed(); + EXPECT_TRUE(transposed.isEmpty()); + EXPECT_EQ(transposed.rows(), 0); + EXPECT_EQ(transposed.cols(), 0); +} + +TEST(Vector2DTransposeTest, singleElement_returnsSame) { + PIVector2D single(1, 1, 42); + auto transposed = single.transposed(); + EXPECT_EQ(transposed.rows(), 1); + EXPECT_EQ(transposed.cols(), 1); + EXPECT_EQ(transposed.element(0, 0), 42); +} + +TEST(Vector2DTransposeTest, oneRow_becomesOneColumn) { + PIVector2D rowVec(1, 5); + for (size_t c = 0; c < 5; ++c) + rowVec.element(0, c) = static_cast(c); + auto transposed = rowVec.transposed(); + EXPECT_EQ(transposed.rows(), 5); + EXPECT_EQ(transposed.cols(), 1); + for (size_t r = 0; r < 5; ++r) { + EXPECT_EQ(transposed.element(r, 0), static_cast(r)); + } +} + +TEST(Vector2DTransposeTest, oneColumn_becomesOneRow) { + PIVector2D colVec(5, 1); + for (size_t r = 0; r < 5; ++r) + colVec.element(r, 0) = static_cast(r); + auto transposed = colVec.transposed(); + EXPECT_EQ(transposed.rows(), 1); + EXPECT_EQ(transposed.cols(), 5); + for (size_t c = 0; c < 5; ++c) { + EXPECT_EQ(transposed.element(0, c), static_cast(c)); + } +} + +TEST_F(Vector2DTest, transposed_doesNotModifyOriginal) { + auto original = vec; // копия для сравнения + auto transposed = vec.transposed(); + // Проверяем, что исходный массив не изменился + EXPECT_EQ(vec, original); +} + TEST_F(Vector2DTest, reverseRows_reverses_row_order) { auto original = vec; vec.reverseRows(); @@ -767,36 +1225,36 @@ TEST_F(Vector2DTest, forEach_modifying_changes_elements) { TEST_F(Vector2DTest, indexOf_returns_correct_pair) { auto p = vec.indexOf(vec.element(10, 15)); - EXPECT_EQ(p.first, 10); - EXPECT_EQ(p.second, 15); + EXPECT_EQ(p.row, 10); + EXPECT_EQ(p.col, 15); p = vec.indexOf(-999); - EXPECT_EQ(p.first, -1); - EXPECT_EQ(p.second, -1); + EXPECT_EQ(p.row, -1); + EXPECT_EQ(p.col, -1); } TEST_F(Vector2DTest, indexWhere_returns_correct_pair) { vec.element(5, 5) = -42; auto isTarget = [](const int & e) { return e == -42; }; auto p = vec.indexWhere(isTarget); - EXPECT_EQ(p.first, 5); - EXPECT_EQ(p.second, 5); + EXPECT_EQ(p.row, 5); + EXPECT_EQ(p.col, 5); } TEST_F(Vector2DTest, lastIndexOf_works) { int val = vec.element(10, 10); vec.element(20, 20) = val; // duplicate auto p = vec.lastIndexOf(val); - EXPECT_EQ(p.first, 20); - EXPECT_EQ(p.second, 20); + EXPECT_EQ(p.row, 20); + EXPECT_EQ(p.col, 20); } TEST_F(Vector2DTest, lastIndexWhere_works) { auto isLarge = [](const int & e) { return e > 500; }; auto p = vec.lastIndexWhere(isLarge); - EXPECT_GE(p.first, 0); - EXPECT_GE(p.second, 0); + EXPECT_GE(p.row, 0); + EXPECT_GE(p.col, 0); // The last element with value >500 should be the largest index - size_t lastFlat = p.first * vec.cols() + p.second; + size_t lastFlat = p.row * vec.cols() + p.col; size_t expectedLastFlat = vec.size() - 1; EXPECT_EQ(lastFlat, expectedLastFlat); } @@ -993,6 +1451,242 @@ TEST(Vector2DEdgeTest, single_element_vector) { EXPECT_EQ(single.element(0, 0), 42); } +// ==================== PROXY ADDITIONAL OPERATIONS TESTS ==================== +TEST_F(Vector2DTest, row_proxy_forEach_modifies_elements) { + auto row = vec[5]; + row.forEach([](int & e) { e += 100; }); + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(5, c), 5 * COLS_COUNT_INIT + c + 100); + } +} + +TEST_F(Vector2DTest, row_proxy_forEach_const_iterates) { + const auto & constVec = vec; + auto row = constVec[5]; + size_t count = 0; + row.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, COLS_COUNT_INIT); +} + +TEST_F(Vector2DTest, row_proxy_fill_sets_all_elements) { + auto row = vec[12]; + row.fill(999); + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + EXPECT_EQ(vec.element(12, c), 999); + } +} + +TEST_F(Vector2DTest, row_proxy_contains_finds_element) { + auto row = vec[8]; + EXPECT_TRUE(row.contains(vec.element(8, 10))); + EXPECT_FALSE(row.contains(-999)); +} + +TEST_F(Vector2DTest, row_proxy_entries_counts_occurrences) { + auto row = vec[15]; + // Add a duplicate + int val = vec.element(15, 5); + vec.element(15, 20) = val; + EXPECT_EQ(row.entries(val), 2); + EXPECT_EQ(row.entries(-999), 0); +} + +TEST_F(Vector2DTest, row_proxy_entries_with_predicate_counts_matches) { + auto row = vec[20]; + auto isEven = [](const int & e) { return e % 2 == 0; }; + int expected = 0; + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + if (row[c] % 2 == 0) ++expected; + } + EXPECT_EQ(row.entries(isEven), expected); +} + +TEST_F(Vector2DTest, row_proxy_any_returns_true_if_any_match) { + auto row = vec[25]; + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(row.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(row.any(isPositive)); +} + +TEST_F(Vector2DTest, row_proxy_every_returns_true_if_all_match) { + auto row = vec[30]; + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(row.every(isLessThanMax)); + auto isEven = [](const int & e) { return e % 2 == 0; }; + EXPECT_FALSE(row.every(isEven)); +} + +// ---------------------------------------------------------------------------- +TEST_F(Vector2DTest, col_proxy_forEach_modifies_elements) { + auto col = vec.col(7); + col.forEach([](int & e) { e += 50; }); + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(vec.element(r, 7), r * COLS_COUNT_INIT + 7 + 50); + } +} + +TEST_F(Vector2DTest, col_proxy_forEach_const_iterates) { + const auto & constVec = vec; + auto col = constVec.col(9); + size_t count = 0; + col.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, ROWS_COUNT_INIT); +} + +TEST_F(Vector2DTest, col_proxy_fill_sets_all_elements) { + auto col = vec.col(11); + col.fill(777); + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + EXPECT_EQ(vec.element(r, 11), 777); + } +} + +TEST_F(Vector2DTest, col_proxy_contains_finds_element) { + auto col = vec.col(13); + EXPECT_TRUE(col.contains(vec.element(5, 13))); + EXPECT_FALSE(col.contains(-999)); +} + +TEST_F(Vector2DTest, col_proxy_entries_counts_occurrences) { + auto col = vec.col(17); + int val = vec.element(3, 17); + vec.element(22, 17) = val; // duplicate + EXPECT_EQ(col.entries(val), 2); + EXPECT_EQ(col.entries(-999), 0); +} + +TEST_F(Vector2DTest, col_proxy_entries_with_predicate_counts_matches) { + auto col = vec.col(19); + auto isOdd = [](const int & e) { return e % 2 != 0; }; + int expected = 0; + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + if (col[r] % 2 != 0) ++expected; + } + EXPECT_EQ(col.entries(isOdd), expected); +} + +TEST_F(Vector2DTest, col_proxy_any_returns_true_if_any_match) { + auto col = vec.col(21); + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(col.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(col.any(isPositive)); +} + +TEST_F(Vector2DTest, col_proxy_every_returns_true_if_all_match) { + auto col = vec.col(23); + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(col.every(isLessThanMax)); + auto isEven = [](const int & e) { return e % 2 == 0; }; + EXPECT_FALSE(col.every(isEven)); +} + +// ---------------------------------------------------------------------------- +TEST_F(Vector2DTest, rowconst_proxy_forEach_iterates) { + const auto & constVec = vec; + auto row = constVec[5]; + size_t count = 0; + row.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, COLS_COUNT_INIT); +} + +TEST_F(Vector2DTest, rowconst_proxy_contains_finds_element) { + const auto & constVec = vec; + auto row = constVec[6]; + EXPECT_TRUE(row.contains(vec.element(6, 10))); + EXPECT_FALSE(row.contains(-999)); +} + +TEST_F(Vector2DTest, rowconst_proxy_entries_counts_occurrences) { + const auto & constVec = vec; + auto row = constVec[7]; + int val = vec.element(7, 5); + // We can't modify through const proxy, but duplicates already exist from previous tests + EXPECT_GE(row.entries(val), 1); // at least one +} + +TEST_F(Vector2DTest, rowconst_proxy_entries_with_predicate_counts_matches) { + const auto & constVec = vec; + auto row = constVec[9]; + auto isEven = [](const int & e) { return e % 2 == 0; }; + int expected = 0; + for (size_t c = 0; c < COLS_COUNT_INIT; ++c) { + if (vec.element(9, c) % 2 == 0) ++expected; + } + EXPECT_EQ(row.entries(isEven), expected); +} + +TEST_F(Vector2DTest, rowconst_proxy_any_returns_true_if_any_match) { + const auto & constVec = vec; + auto row = constVec[10]; + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(row.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(row.any(isPositive)); +} + +TEST_F(Vector2DTest, rowconst_proxy_every_returns_true_if_all_match) { + const auto & constVec = vec; + auto row = constVec[11]; + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(row.every(isLessThanMax)); + auto isEven = [](const int & e) { return e % 2 == 0; }; + EXPECT_FALSE(row.every(isEven)); +} + +// ---------------------------------------------------------------------------- +TEST_F(Vector2DTest, colconst_proxy_forEach_iterates) { + const auto & constVec = vec; + auto col = constVec.col(25); + size_t count = 0; + col.forEach([&count](const int &) { ++count; }); + EXPECT_EQ(count, ROWS_COUNT_INIT); +} + +TEST_F(Vector2DTest, colconst_proxy_contains_finds_element) { + const auto & constVec = vec; + auto col = constVec.col(27); + EXPECT_TRUE(col.contains(vec.element(4, 27))); + EXPECT_FALSE(col.contains(-999)); +} + +TEST_F(Vector2DTest, colconst_proxy_entries_counts_occurrences) { + const auto & constVec = vec; + auto col = constVec.col(29); + int val = vec.element(8, 29); + EXPECT_GE(col.entries(val), 1); +} + +TEST_F(Vector2DTest, colconst_proxy_entries_with_predicate_counts_matches) { + const auto & constVec = vec; + auto col = constVec.col(31); + auto isOdd = [](const int & e) { return e % 2 != 0; }; + int expected = 0; + for (size_t r = 0; r < ROWS_COUNT_INIT; ++r) { + if (vec.element(r, 31) % 2 != 0) ++expected; + } + EXPECT_EQ(col.entries(isOdd), expected); +} + +TEST_F(Vector2DTest, colconst_proxy_any_returns_true_if_any_match) { + const auto & constVec = vec; + auto col = constVec.col(33); + auto isNegative = [](const int & e) { return e < 0; }; + EXPECT_FALSE(col.any(isNegative)); + auto isPositive = [](const int & e) { return e >= 0; }; + EXPECT_TRUE(col.any(isPositive)); +} + +TEST_F(Vector2DTest, colconst_proxy_every_returns_true_if_all_match) { + const auto & constVec = vec; + auto col = constVec.col(0); + auto isLessThanMax = [&](const int & e) { return e < static_cast(vec.size()); }; + EXPECT_TRUE(col.every(isLessThanMax)); + auto isNotEven = [](const int & e) { return e % 2 != 0; }; + EXPECT_FALSE(col.every(isNotEven)); +} + // ==================== OUTPUT TESTS ==================== TEST_F(Vector2DTest, picout_operator_works) { // Just test that it compiles and doesn't crash @@ -1010,3 +1704,94 @@ TEST_F(Vector2DTest, iostream_operator_works) { // No assertion, just ensure it runs } #endif + +// ==================== INDEX STRUCTURE ACCESS TESTS ==================== +TEST_F(Vector2DTest, operator_bracket_with_index_allows_read_access) { + PIVector2D::Index idx = {5, 7}; + int value = vec[idx]; + int expected = vec.element(5, 7); + EXPECT_EQ(value, expected); +} + +TEST_F(Vector2DTest, operator_bracket_with_index_allows_write_access) { + PIVector2D::Index idx = {10, 15}; + vec[idx] = 999; + EXPECT_EQ(vec.element(10, 15), 999); +} + +TEST_F(Vector2DTest, operator_bracket_with_const_index_works) { + const auto & constVec = vec; + PIVector2D::Index idx = {3, 12}; + int value = constVec[idx]; + int expected = vec.element(3, 12); + EXPECT_EQ(value, expected); +} + +TEST_F(Vector2DTest, element_function_with_index_works) { + PIVector2D::Index idx = {8, 20}; + int value = vec.element(idx); + int expected = vec.element(8, 20); + EXPECT_EQ(value, expected); +} + +TEST_F(Vector2DTest, element_function_with_index_modifies_value) { + PIVector2D::Index idx = {12, 8}; + vec.element(idx) = 555; + EXPECT_EQ(vec.element(12, 8), 555); +} + +TEST_F(Vector2DTest, element_function_with_const_index_works) { + const auto & constVec = vec; + PIVector2D::Index idx = {7, 5}; + int value = constVec.element(idx); + int expected = vec.element(7, 5); + EXPECT_EQ(value, expected); +} + +TEST_F(Vector2DTest, at_function_with_index_works) { + PIVector2D::Index idx = {4, 11}; + int value = vec.at(idx); + int expected = vec.at(4, 11); + EXPECT_EQ(value, expected); +} + +TEST_F(Vector2DTest, index_structure_with_brace_initialization_works) { + vec[{0, 0}] = 100; + EXPECT_EQ(vec.element(0, 0), 100); + + vec[{static_cast(ROWS_COUNT_INIT - 1), static_cast(COLS_COUNT_INIT - 1)}] = 200; + EXPECT_EQ(vec.element(ROWS_COUNT_INIT - 1, COLS_COUNT_INIT - 1), 200); +} + +TEST(Vector2DIndexTest, default_constructor_initializes_to_invalid) { + PIVector2D::Index idx; + EXPECT_TRUE(idx.isNotValid()); + EXPECT_FALSE(idx.isValid()); + EXPECT_EQ(idx.row, -1); + EXPECT_EQ(idx.col, -1); +} + +TEST(Vector2DIndexTest, parameterized_constructor_initializes_correctly) { + PIVector2D::Index idx(5, 10); + EXPECT_TRUE(idx.isValid()); + EXPECT_FALSE(idx.isNotValid()); + EXPECT_EQ(idx.row, 5); + EXPECT_EQ(idx.col, 10); +} + +TEST(Vector2DIndexTest, isValid_returns_true_for_non_negative_values) { + PIVector2D::Index idx(0, 0); + EXPECT_TRUE(idx.isValid()); + EXPECT_FALSE(idx.isNotValid()); +} + +TEST(Vector2DIndexTest, isNotValid_returns_true_for_negative_values) { + PIVector2D::Index idx1; + EXPECT_TRUE(idx1.isNotValid()); + + PIVector2D::Index idx2(-1, 5); + EXPECT_TRUE(idx2.isNotValid()); + + PIVector2D::Index idx3(5, -1); + EXPECT_TRUE(idx3.isNotValid()); +} From 46f86b6591f24100fb35e98eeed1b1580ed6f1c8 Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 5 Mar 2026 11:58:34 +0300 Subject: [PATCH 13/53] add PIMathVectors JSON serialization --- libs/main/serialization/pijsonserialization.h | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/libs/main/serialization/pijsonserialization.h b/libs/main/serialization/pijsonserialization.h index dfd5288e..b60a8f0c 100644 --- a/libs/main/serialization/pijsonserialization.h +++ b/libs/main/serialization/pijsonserialization.h @@ -38,14 +38,14 @@ template::value, int>::type = 0> inline PIJSON piSerializeJSON(const T & v) { - return PIJSON() = (int)v; + return PIJSON() = (int)v; } template::value, int>::type = 0, typename std::enable_if::value, int>::type = 0> inline PIJSON piSerializeJSON(const T & v) { - return PIJSON() = v; + return PIJSON() = v; } template & v) { return ret; } +template +inline PIJSON piSerializeJSON(const PIMathVector & v) { + PIJSON ret; + for (uint i = 0; i < v.size(); ++i) + ret << piSerializeJSON(v[i]); + return ret; +} + +template +inline PIJSON piSerializeJSON(const PIMathVectorT & v) { + PIJSON ret; + for (uint i = 0; i < v.size(); ++i) + ret << piSerializeJSON(v[i]); + return ret; +} + // --- // deserialize, piDeserializeJSON(T, PIJSON) @@ -229,14 +245,14 @@ inline PIJSON piSerializeJSON(const PIMap & v) { template::value, int>::type = 0> inline void piDeserializeJSON(T & v, const PIJSON & js) { - v = (T)js.toInt(); + v = (T)js.toInt(); } template::value, int>::type = 0, typename std::enable_if::value, int>::type = 0> inline void piDeserializeJSON(T & v, const PIJSON & js) { - v = js.value().value(); + v = js.value().value(); } template & v, const PIJSON & js) { piDeserializeJSON(v[PIVariant(it.key()).value()], it.value()); } +template +inline void piDeserializeJSON(PIMathVector & v, const PIJSON & js) { + v = {}; + if (!js.isArray()) return; + v = PIMathVector(js.size()); + for (int i = 0; i < js.size(); ++i) + piDeserializeJSON(v[i], js[i]); +} + +template +inline void piDeserializeJSON(PIMathVectorT & v, const PIJSON & js) { + v = PIMathVectorT(); + if (!js.isArray()) return; + int cnt = piMini(js.size(), Size); + for (int i = 0; i < cnt; ++i) + piDeserializeJSON(v[i], js[i]); +} + // --- // PIJSON static wrapper From 07ae277f9e2a9836d21242d1506ae2583f15e442 Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 6 Mar 2026 20:31:37 +0300 Subject: [PATCH 14/53] add PIHTTPClient::ignoreSSLErrors() --- libs/http_client/pihttpclient.cpp | 7 +++++++ libs/main/http_client/pihttpclient.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/libs/http_client/pihttpclient.cpp b/libs/http_client/pihttpclient.cpp index ee07034c..b6d3aa52 100644 --- a/libs/http_client/pihttpclient.cpp +++ b/libs/http_client/pihttpclient.cpp @@ -93,6 +93,7 @@ bool PIHTTPClient::init() { // curl_easy_setopt(PRIVATE->handle, CURLOPT_VERBOSE, 1L); // curl_easy_setopt(PRIVATE->handle, CURLOPT_ERRORBUFFER, buffer_error.data()); curl_easy_setopt(PRIVATE->handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(PRIVATE->handle, CURLOPT_SSL_VERIFYHOST, ignore_ssl_errors ? 0L : 1L); if (request.body().isNotEmpty()) { curl_easy_setopt(PRIVATE->handle, CURLOPT_UPLOAD, 1L); curl_easy_setopt(PRIVATE->handle, CURLOPT_INFILESIZE_LARGE, static_cast(request.body().size())); @@ -273,6 +274,12 @@ PIHTTPClient * PIHTTPClient::onAbort(std::functionregisterClient(this); } diff --git a/libs/main/http_client/pihttpclient.h b/libs/main/http_client/pihttpclient.h index 99d14eee..24512c50 100644 --- a/libs/main/http_client/pihttpclient.h +++ b/libs/main/http_client/pihttpclient.h @@ -48,6 +48,10 @@ public: //! \~russian Устанавливает callback для прерывания запроса (с ответом о прерывании). PIHTTPClient * onAbort(std::function f); + //! \~english Setup request to ignore SSL errors. Need to call before \a start(). + //! \~russian Устанавливает игнорирование ошибок SSL. Необходимо вызывать перед \a start(). + PIHTTPClient * ignoreSSLErrors(); + //! \~english Starts the HTTP request execution. //! \~russian Начинает выполнение HTTP-запроса. void start(); @@ -83,6 +87,7 @@ private: PIByteArray buffer_out; PIHTTP::MessageMutable request, reply; std::atomic_bool is_cancel = {false}; + bool ignore_ssl_errors = false; ssize_t read_pos = 0; std::function on_finish, on_error, on_abort; }; From ed138382374f7b2c69e2a8e51a6e942704409aaa Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 12 Mar 2026 14:46:57 +0300 Subject: [PATCH 15/53] merged AI doc, some new pages --- doc/pages/application.md | 26 + doc/pages/chunk_stream.md | 56 ++ doc/pages/client_server.md | 46 ++ doc/pages/code_model.md | 20 + doc/pages/config.md | 14 + doc/pages/connection.md | 50 ++ doc/pages/console.md | 42 ++ doc/pages/examples.md | 52 ++ doc/pages/iostream.md | 16 +- doc/pages/main.md | 14 +- doc/pages/state_machine.md | 69 ++ doc/pages/summary.md | 8 +- doc/pages/threading.md | 28 + doc/pages/using_advanced.md | 64 ++ doc/pages/using_basic.md | 26 +- libs/console/piscreenconsole.cpp | 35 - libs/main/application/piapplicationmodule.h | 20 +- libs/main/application/picli.h | 68 +- libs/main/application/pilog.h | 50 +- libs/main/application/pisingleapplication.h | 45 +- libs/main/application/pisystemmonitor.h | 100 +-- libs/main/application/pitranslator.h | 105 ++- .../client_server/piclientserver_client.h | 47 +- .../piclientserver_client_base.h | 90 ++- .../client_server/piclientserver_server.h | 69 +- .../main/client_server/piclientservermodule.h | 11 +- libs/main/cloud/picloudbase.h | 20 +- libs/main/cloud/picloudclient.h | 51 +- libs/main/cloud/picloudmodule.h | 18 +- libs/main/cloud/picloudserver.h | 48 +- libs/main/cloud/picloudtcp.h | 97 ++- libs/main/code/picodeinfo.h | 206 +++-- libs/main/code/picodemodule.h | 18 +- libs/main/code/picodeparser.h | 229 +++++- libs/main/console/piconsolemodule.h | 17 +- libs/main/console/pikbdlistener.h | 258 ++++--- libs/main/console/piscreen.h | 104 ++- libs/main/console/piscreenconsole.h | 82 -- libs/main/console/piscreendrawer.h | 71 +- libs/main/console/piscreentile.h | 147 +++- libs/main/console/piscreentiles.h | 246 +++++- libs/main/console/piscreentypes.h | 186 +++-- libs/main/console/piterminal.h | 52 +- libs/main/containers/picontainers.h | 8 +- libs/main/containers/picontainersmodule.h | 16 +- libs/main/containers/pideque.h | 2 +- libs/main/containers/pimap.h | 184 ++--- libs/main/containers/pipair.h | 2 +- libs/main/containers/piqueue.h | 72 +- libs/main/containers/piset.h | 198 ++++- libs/main/containers/pistack.h | 2 +- libs/main/containers/pivector.h | 2 +- libs/main/containers/pivector2d.h | 17 +- libs/main/core/pibase.h | 84 ++- libs/main/core/pibase_macros.h | 110 +-- libs/main/core/picollection.h | 76 +- libs/main/core/picoremodule.h | 16 +- libs/main/core/picout.h | 128 +++- libs/main/core/piincludes.h | 43 +- libs/main/core/piinit.h | 42 +- libs/main/core/pimemoryblock.h | 61 +- libs/main/core/piobject.h | 330 +++++--- libs/main/core/piobject_macros.h | 246 +++--- libs/main/crypt/piauth.h | 95 ++- libs/main/crypt/picrypt.h | 163 ++-- libs/main/crypt/picryptmodule.h | 10 + libs/main/digest/pidigest.h | 92 ++- libs/main/geo/piellipsoidmodel.h | 60 +- libs/main/geo/pigeomodule.h | 39 +- libs/main/geo/pigeoposition.h | 251 ++++++- libs/main/http_client/pihttpclient.h | 80 +- libs/main/http_client/pihttpclientmodule.h | 34 +- libs/main/http_common/pihttpconstants.h | 47 +- libs/main/http_common/pihttptypes.h | 180 +++-- libs/main/http_server/microhttpd_server.h | 115 +-- libs/main/http_server/pihttpserver.h | 81 +- libs/main/http_server/pihttpservermodule.h | 50 +- .../main/introspection/piintrospection_base.h | 24 + .../piintrospection_containers.h | 132 +++- .../introspection/piintrospection_server.h | 30 +- .../introspection/piintrospection_threads.h | 114 ++- libs/main/io_devices/pibinarylog.h | 707 +++++++++++++++--- libs/main/io_devices/pican.h | 31 +- libs/main/io_devices/piconfig.h | 521 +++++++++---- libs/main/io_devices/pidir.h | 152 ++-- libs/main/io_devices/piethernet.h | 419 +++++++---- libs/main/io_devices/pifile.h | 314 ++++---- libs/main/io_devices/pigpio.h | 64 +- libs/main/io_devices/piiobytearray.h | 72 +- libs/main/io_devices/piiodevice.h | 449 +++++------ libs/main/io_devices/piiodevicesmodule.h | 17 +- libs/main/io_devices/piiostream.h | 63 +- libs/main/io_devices/piiostring.h | 38 +- libs/main/io_devices/pipeer.h | 187 ++++- libs/main/io_devices/piserial.h | 46 +- libs/main/io_devices/pisharedmemory.h | 220 +++++- libs/main/io_devices/pispi.h | 64 +- libs/main/io_devices/pitransparentdevice.h | 14 +- libs/main/io_devices/piusb.h | 264 ++++++- libs/main/io_utils/pibasetransfer.h | 224 +++++- libs/main/io_utils/pibroadcast.h | 109 ++- libs/main/io_utils/piconnection.h | 352 ++++++--- libs/main/io_utils/pidatatransfer.h | 29 +- libs/main/io_utils/pidiagnostics.h | 147 +++- libs/main/io_utils/piethutilbase.h | 61 +- libs/main/io_utils/pifiletransfer.h | 139 +++- libs/main/io_utils/piioutilsmodule.h | 27 +- libs/main/io_utils/pipackedtcp.h | 61 +- libs/main/io_utils/pipacketextractor.h | 168 +++-- libs/main/io_utils/piparsehelper.h | 92 +-- libs/main/io_utils/pistreampacker.h | 125 +++- libs/main/literals/piliterals.h | 34 +- libs/main/literals/piliterals_bytearray.h | 16 +- libs/main/literals/piliterals_bytes.h | 24 +- .../literals/piliterals_regularexpression.h | 14 +- libs/main/literals/piliterals_string.h | 16 +- libs/main/literals/piliterals_time.h | 29 +- libs/main/lua/piluaprogram.h | 33 +- libs/main/lua/pip_lua.h | 12 +- libs/main/math/picrc.h | 126 +++- libs/main/math/pievaluator.h | 108 ++- libs/main/math/pifft.h | 92 ++- libs/main/math/pigeometry.h | 27 +- libs/main/math/piline.h | 139 ++-- libs/main/math/pimathbase.h | 183 ++++- libs/main/math/pimathcomplex.h | 64 +- libs/main/math/pimathmatrix.h | 83 +- libs/main/math/pimathmodule.h | 13 +- libs/main/math/pimathsolver.h | 104 ++- libs/main/math/pimathvector.h | 325 +++++++- libs/main/math/pipoint.h | 134 ++-- libs/main/math/piquaternion.h | 88 ++- libs/main/math/pirect.h | 229 ++++-- libs/main/math/pistatistic.h | 54 +- libs/main/opencl/piopencl.h | 340 ++++++++- libs/main/pip.h | 12 + libs/main/piplatform.h | 102 +++ libs/main/resources/piresources.h | 48 +- libs/main/resources/piresourcesstorage.h | 47 +- libs/main/serialization/pibinarystream.h | 226 ++++-- libs/main/serialization/pichunkstream.h | 100 ++- libs/main/serialization/pijson.h | 177 +++-- libs/main/serialization/pijsonserialization.h | 124 ++- .../serialization/piserializationmodule.h | 22 +- .../serialization/pivaluetree_conversions.h | 62 +- libs/main/state_machine/pistatemachine.h | 41 +- libs/main/state_machine/pistatemachine_base.h | 15 +- .../main/state_machine/pistatemachine_state.h | 132 +++- .../state_machine/pistatemachine_transition.h | 63 +- .../main/state_machine/pistatemachinemodule.h | 655 +++++++++++++++- libs/main/system/pihidevice.h | 171 ++++- libs/main/system/pilibrary.h | 2 +- libs/main/system/piplugin.h | 106 ++- libs/main/system/piprocess.h | 2 +- libs/main/system/pisignals.h | 74 +- libs/main/system/pisysteminfo.h | 77 +- libs/main/system/pisystemmodule.h | 18 +- libs/main/system/pisystemtests.h | 31 + libs/main/text/pichar.h | 192 ++--- libs/main/text/piconstchars.h | 10 +- libs/main/text/piregularexpression.h | 274 +++---- libs/main/text/pistring.h | 6 +- libs/main/text/pistring_std.h | 48 +- libs/main/text/pistringlist.h | 2 +- libs/main/text/pitextmodule.h | 32 +- libs/main/text/pitextstream.h | 2 +- libs/main/thread/piblockingqueue.h | 144 ++-- libs/main/thread/piconditionvar.h | 230 +++--- libs/main/thread/pigrabberbase.h | 117 ++- libs/main/thread/pimutex.h | 12 +- libs/main/thread/pipipelinethread.h | 126 +++- libs/main/thread/piprotectedvariable.h | 51 +- libs/main/thread/pireadwritelock.h | 59 +- libs/main/thread/pisemaphore.h | 42 +- libs/main/thread/pispinlock.h | 43 +- libs/main/thread/pithread.h | 213 +++--- libs/main/thread/pithreadmodule.h | 14 +- libs/main/thread/pithreadnotifier.h | 26 +- libs/main/thread/pithreadpoolexecutor.h | 52 +- libs/main/thread/pithreadpoolloop.h | 69 +- libs/main/thread/pitimer.h | 147 ++-- libs/main/types/pibitarray.h | 42 +- libs/main/types/pibytearray.h | 46 +- libs/main/types/pidatetime.h | 17 +- libs/main/types/piflags.h | 37 +- libs/main/types/pinetworkaddress.h | 15 +- libs/main/types/pipropertystorage.h | 37 +- libs/main/types/pisystemtime.h | 17 +- libs/main/types/pitime.h | 21 +- libs/main/types/pitime_win.h | 55 +- libs/main/types/pitypesmodule.h | 41 +- libs/main/types/pivaluetree.h | 29 +- libs/main/types/pivariant.h | 242 ++++-- libs/main/types/pivariantsimple.h | 114 ++- libs/main/types/pivarianttypes.h | 81 +- libs/main/units/piunits.h | 17 +- libs/main/units/piunits_base.h | 140 +++- libs/main/units/piunits_class_angle.h | 30 +- libs/main/units/piunits_class_distance.h | 40 +- libs/main/units/piunits_class_information.h | 30 +- libs/main/units/piunits_class_mass.h | 34 +- libs/main/units/piunits_class_pressure.h | 34 +- libs/main/units/piunits_class_temperature.h | 32 +- libs/main/units/piunits_class_time.h | 30 +- libs/main/units/piunits_prefix.h | 77 +- libs/main/units/piunits_value.h | 44 +- 206 files changed, 14088 insertions(+), 5152 deletions(-) create mode 100644 doc/pages/application.md create mode 100644 doc/pages/chunk_stream.md create mode 100644 doc/pages/client_server.md create mode 100644 doc/pages/config.md create mode 100644 doc/pages/connection.md create mode 100644 doc/pages/console.md create mode 100644 doc/pages/examples.md create mode 100644 doc/pages/state_machine.md create mode 100644 doc/pages/threading.md create mode 100644 doc/pages/using_advanced.md delete mode 100644 libs/console/piscreenconsole.cpp delete mode 100644 libs/main/console/piscreenconsole.h diff --git a/doc/pages/application.md b/doc/pages/application.md new file mode 100644 index 00000000..36ff36f5 --- /dev/null +++ b/doc/pages/application.md @@ -0,0 +1,26 @@ +\~english \page application Application-level tools +\~russian \page application Инструменты уровня приложения + +\~english + +The Application module provides classes commonly needed at program startup and runtime: + +* **PICLI** — command-line argument parser. Add named arguments (e.g. \c addArgument("debug") for \c -d / \c --debug), check presence with \a hasArgument(), optionally read values. Used in \ref using_basic for console and debug flags. +* **PILog** — high-level logging with categories and levels. Configure sinks and severity; write log lines from anywhere in the process. +* **PISystemMonitor** — snapshot of system resources (CPU, memory, etc.). Query current stats or subscribe to periodic updates. +* **PISingleApplication** — ensure only one instance of the application runs; optional inter-process messaging when a second instance is started. +* **PITranslator** — translation support: load catalogs, select language, translate strings at runtime. + +All are included via the main PIP library or the Application umbrella (\a piapplicationmodule.h). For CLI and logging, see \ref using_basic; for full API details see the headers \a picli.h, \a pilog.h, \a pisystemmonitor.h, \a pisingleapplication.h, \a pitranslator.h. + +\~russian + +Модуль Application предоставляет классы, часто нужные при запуске и работе приложения: + +* **PICLI** — разбор аргументов командной строки. Добавление именованных аргументов (\c addArgument("debug") для \c -d / \c --debug), проверка наличия \a hasArgument(), при необходимости чтение значений. Используется в \ref using_basic для флагов консоли и отладки. +* **PILog** — логирование с категориями и уровнями. Настройка приёмников и уровня детализации; запись строк лога из любой части процесса. +* **PISystemMonitor** — снимок ресурсов системы (CPU, память и т.д.). Запрос текущей статистики или подписка на периодические обновления. +* **PISingleApplication** — гарантия единственного экземпляра приложения; при необходимости обмен сообщениями между процессами при запуске второго экземпляра. +* **PITranslator** — поддержка перевода: загрузка каталогов, выбор языка, перевод строк в runtime. + +Всё подключается через основную библиотеку PIP или зонтичный заголовок (\a piapplicationmodule.h). Для CLI и лога см. \ref using_basic; детали API — в заголовках \a picli.h, \a pilog.h, \a pisystemmonitor.h, \a pisingleapplication.h, \a pitranslator.h. diff --git a/doc/pages/chunk_stream.md b/doc/pages/chunk_stream.md new file mode 100644 index 00000000..9fd8bc6d --- /dev/null +++ b/doc/pages/chunk_stream.md @@ -0,0 +1,56 @@ +\~english \page chunk_stream Chunk stream and versioned serialization +\~russian \page chunk_stream Поток чанков и версионная сериализация + +\~english + +\a PIChunkStream is a binary stream where data is stored as **chunks**: each chunk has an integer \e id and a value. Reading is id-based, so you can add or reorder fields over time and stay backward compatible: old readers ignore unknown ids, new readers can skip optional ids. + +Two format versions exist (\a PIChunkStream::Version_1 and \a Version_2); the writer chooses the version, the reader detects it automatically. By default new data is written in Version_2. + +# When to use + +Use chunk streams when: +* You need to extend structures without breaking existing stored data (add new fields with new ids). +* You want optional or reordered fields in a single stream. +* You use \ref code_model to generate serialization: with default (chunk) mode, \c pip_cmg emits operators that read/write via \a PIChunkStream and field ids (see \ref code_model "code_model" for PIMETA \c id and \c simple-stream / \c no-stream). + +For fixed, non-extensible layouts, plain \a PIBinaryStream operators (see \ref iostream) are enough. + +# Usage + +Build a \a PIChunkStream from a \a PIByteArray (read or read-write). Write with \c cs << cs.chunk(id, value) or \c add(id, value); read with \c read() to get the next id, then \c get(value). Call \c data() to get the byte buffer for storing or sending. Example (conceptually as in code_model output): + +\code{.cpp} +PIByteArray buf; +PIChunkStream cs(&buf); +cs << cs.chunk(1, i) << cs.chunk(2, s); +// later: +PIChunkStream reader(buf); +while (!reader.atEnd()) { + switch (reader.read()) { + case 1: reader.get(i); break; + case 2: reader.get(s); break; + } +} +\endcode + +Generated operators for structs use the same pattern; see \ref code_model. + +\~russian + +\a PIChunkStream — бинарный поток, в котором данные хранятся **чанками**: у каждого чанка целочисленный \e id и значение. Чтение идёт по id, поэтому можно добавлять или менять порядок полей с сохранением обратной совместимости: старые читатели игнорируют неизвестные id, новые могут пропускать необязательные. + +Есть две версии формата (\a PIChunkStream::Version_1 и \a Version_2); версию выбирает запись, при чтении она определяется автоматически. По умолчанию запись идёт в Version_2. + +# Когда использовать + +Имеет смысл использовать поток чанков, когда: +* Нужно расширять структуры без поломки уже сохранённых данных (новые поля — новые id). +* Нужны необязательные или переставляемые поля в одном потоке. +* Используется \ref code_model для генерации сериализации: в режиме по умолчанию (chunk) \c pip_cmg выдаёт операторы через \a PIChunkStream и id полей (см. \ref code_model по PIMETA \c id и \c simple-stream / \c no-stream). + +Для фиксированных неизменяемых форматов достаточно обычных операторов \a PIBinaryStream (см. \ref iostream). + +# Использование + +Создают \a PIChunkStream из \a PIByteArray (чтение или чтение/запись). Запись: \c cs << cs.chunk(id, value) или \c add(id, value); чтение: \c read() — следующий id, затем \c get(value). \c data() возвращает буфер для сохранения или передачи. Примеры генерации операторов — в \ref code_model. diff --git a/doc/pages/client_server.md b/doc/pages/client_server.md new file mode 100644 index 00000000..9533eaa3 --- /dev/null +++ b/doc/pages/client_server.md @@ -0,0 +1,46 @@ +\~english \page client_server TCP client-server +\~russian \page client_server TCP клиент-сервер + +\~english + +The ClientServer module provides a TCP server that accepts connections and manages per-client objects, and an active client that connects to a server. All in the \a PIClientServer namespace. + +# Server + +\a PIClientServer::Server listens on an address (e.g. \a listenAll(port) for all interfaces). For each accepted connection the server creates a \a PIClientServer::ServerClient. You can override the client type and handle client lifecycle (e.g. data received, disconnected). Use \a getMaxClients() / \a setMaxClients() to limit simultaneous connections. \a listen(addr) starts listening, \a stopServer() stops the server, \a closeAll() closes all current clients. + +# ServerClient + +\a PIClientServer::ServerClient is the server-side representation of one connected client. The server creates and owns these objects. Override \a aboutDelete() if you need cleanup before the client is removed. Use the base API (\a ClientBase) to send and receive data on the connection. + +# Client + +\a PIClientServer::Client is the active client: it connects to a remote server (address/port). After connecting you use the same send/receive API as on the server side. Connect, exchange data, then disconnect when done. + +# Typical flow + +Server: construct \a Server, optionally set max clients and callbacks, call \a listen() or \a listenAll(port). Handle client events (new client, data, disconnect) in your overrides or via the provided hooks. Client: construct \a Client, connect to server address, send/receive, disconnect. + +See \a piclientserver_server.h, \a piclientserver_client.h, \a piclientserver_client_base.h for the full API. + +\~russian + +Модуль ClientServer предоставляет TCP-сервер, принимающий соединения и управляющий объектами клиентов, и активный клиент, подключающийся к серверу. Всё в пространстве имён \a PIClientServer. + +# Сервер + +\a PIClientServer::Server слушает адрес (например \a listenAll(port) на всех интерфейсах). Для каждого принятого соединения создаётся \a PIClientServer::ServerClient. Можно подменить тип клиента и обрабатывать жизненный цикл (данные, отключение). \a getMaxClients() / \a setMaxClients() ограничивают число одновременных соединений. \a listen(addr) — запуск, \a stopServer() — остановка, \a closeAll() — закрытие всех клиентов. + +# Серверный клиент + +\a PIClientServer::ServerClient — серверное представление одного подключённого клиента. Объекты создаёт и владеет сервер. Переопределите \a aboutDelete() при необходимости очистки перед удалением. Отправка и приём данных — через базовый API (\a ClientBase). + +# Клиент + +\a PIClientServer::Client — активный клиент: подключается к удалённому серверу (адрес/порт). После подключения используется тот же API отправки/приёма. Подключение, обмен, отключение. + +# Типичный сценарий + +Сервер: создать \a Server, при необходимости задать лимит клиентов и обработчики, вызвать \a listen() или \a listenAll(port). Обрабатывать события клиентов в переопределениях или через хуки. Клиент: создать \a Client, подключиться к адресу сервера, обмен данными, отключиться. + +Полный API: \a piclientserver_server.h, \a piclientserver_client.h, \a piclientserver_client_base.h. diff --git a/doc/pages/code_model.md b/doc/pages/code_model.md index 61070f4b..c13f0813 100644 --- a/doc/pages/code_model.md +++ b/doc/pages/code_model.md @@ -3,6 +3,26 @@ \~english +# Introduction + +Code generation helps when you need string representation of entities (classes, enums, etc.) or automated serialization/deserialization of structures and classes. For example, you may need a list of "name" = "value" pairs from an enumeration for a UI, or to traverse nested structures with metadata. You can describe a structure of any complexity, assign field IDs, and get ready-made operators for \a PIBinaryStream with versioning and backward compatibility. + +# pip_cmg + +PIP provides the \c pip_cmg utility: it takes source files, include paths, and options, and produces a .h/.cpp pair. Depending on options, the output may include: entity metadata; serialization operators; and the ability to get a \a PIVariant for any member by name. + +Processing options: \c -s (do not follow #include); \c -I (add include path); \c -D (add macro; \c PICODE is always defined). + +Creation options: \c -A (create all); \c -M (metadata); \c -E (enums); \c -S (serialization operators); \c -G (get value by name); \c -o (output base name without extension). + +# CMake + +The \c pip_code_model CMake macro invokes \c pip_cmg and keeps the model up to date. Call format: \c pip_code_model( file0 [file1 ...] [OPTIONS ...] [NAME name]). Parameters: \c out_var receives generated file paths; \c file... are sources; \c OPTIONS are passed to \c pip_cmg (e.g. \c "-Es"); \c NAME sets the model file base (default \c "ccm_${PROJECT_NAME}"). The macro adds PIP include paths. Run \c pip_cmg -v for current options. + +# Details + +Metadata: attach \c PIMETA(...) to types, members or enums; read at runtime via \a PICODEINFO::classes() and \a PICODEINFO::enums(). Serialization: struct-level \c PIMETA(simple-stream) or \c PIMETA(no-stream), or per-member chunk ids (default); see \ref chunk_stream. Add the generated .h/.cpp to your target and \c #include the generated header; metadata loads before \c main(). + \~russian # Введение diff --git a/doc/pages/config.md b/doc/pages/config.md new file mode 100644 index 00000000..13fbdc78 --- /dev/null +++ b/doc/pages/config.md @@ -0,0 +1,14 @@ +\~english \page config Configuration from file +\~russian \page config Конфигурация из файла + +\~english + +\a PIConfig parses and writes configuration from files, strings, or any \a PIIODevice. The internal model is a tree of entries; each node is \a PIConfig::Entry, and a list of entries is \a PIConfig::Branch. Use dotted paths to get values, e.g. \c getValue("section.key.subkey"). Supports INI-style \c [section] prefixes, multiline values, and \c include directives resolved at parse time. + +Typical use: open a file or device, then call \c getValue(name) or \c getValue(name, default) on the root or on a \a PIConfig::Branch. Overloads exist for string, numeric, and bool defaults. To configure an I/O device from config, pass \a PIConfig::Entry pointers to \a PIIODevice::configure(); see \a PIIODevice and device-specific headers. + +\~russian + +\a PIConfig разбирает и записывает конфигурацию из файлов, строк или любого \a PIIODevice. Внутренняя модель — дерево записей; узел — \a PIConfig::Entry, список узлов — \a PIConfig::Branch. Доступ по точечным путям, например \c getValue("section.key.subkey"). Поддерживаются префиксы секций в стиле INI (\c [section]), многострочные значения и директивы \c include, разрешаемые при разборе. + +Типичное использование: открыть файл или устройство, затем вызывать \c getValue(name) или \c getValue(name, default) от корня или от \a PIConfig::Branch. Есть перегрузки для строковых, числовых и булевых значений по умолчанию. Для настройки устройства ввода-вывода из конфига в \a PIIODevice::configure() передают указатели на \a PIConfig::Entry; см. \a PIIODevice и заголовки конкретных устройств. diff --git a/doc/pages/connection.md b/doc/pages/connection.md new file mode 100644 index 00000000..a7a93675 --- /dev/null +++ b/doc/pages/connection.md @@ -0,0 +1,50 @@ +\~english \page connection Complex I/O (PIConnection) +\~russian \page connection Сложный ввод-вывод (PIConnection) + +\~english + +\a PIConnection is an abstract layer over physical I/O devices: it manages a **device pool**, **filters** (packet extraction), **senders** (timed output), and **diagnostics**. Several connections can share one physical device through the pool; each device has an associated read thread that you start/stop with \a startThreadedRead() and \a stopThreadedRead(). + +# Device pool + +The device pool is a single per-application container of unique devices. Each \a PIConnection talks to real hardware through this pool, so one serial port or socket can feed multiple logical connections. + +# Filters + +A filter is a \a PIPacketExtractor plus a set of bound devices or other filters. When the read thread gets data from a device, that data can be passed to one or more filters. Filters have unique names; use \a filter(name) to get the \a PIPacketExtractor*, and \a filterBoundedDevices() for the list of bound devices/filters. One filter can receive from several sources and be bound to several others. + +# Senders + +Senders are named timers that periodically send data to bound devices. Create a sender or add a device to a sender with \a addSender(). Each sender runs a timer and calls the virtual \a senderData(); the returned value is sent to bound devices. Alternatively use \a setSenderFixedData() to send fixed data without calling \a senderData(). + +# Diagnostics + +\a PIConnection creates a \a PIDiagnostics for each device or filter. Access them with \a diagnostic(). + +# Configuration + +You can build a \a PIConnection from a config file section or configure it later with \a configureFromConfig() (see \ref config). Devices are described by full paths (see \a PIIODevice documentation). \a makeConfig() produces a string you can insert into a config file. + +\~russian + +\a PIConnection — абстрактный слой над физическими устройствами ввода-вывода: **пул устройств**, **фильтры** (извлечение пакетов), **отправители** (периодическая отправка) и **диагностика**. Несколько соединений могут использовать одно физическое устройство через пул; у каждого устройства есть поток чтения, запуск и остановка — \a startThreadedRead() и \a stopThreadedRead(). + +# Пул устройств + +Пул устройств — единственный на приложение набор уникальных устройств. \a PIConnection обращается к железу через этот пул, поэтому один порт или сокет может обслуживать несколько логических соединений. + +# Фильтры + +Фильтр — это \a PIPacketExtractor и набор привязанных устройств или других фильтров. Когда поток чтения получает данные с устройства, они могут передаваться в один или несколько фильтров. У фильтров уникальные имена; \a filter(name) возвращает \a PIPacketExtractor*, \a filterBoundedDevices() — список привязанных устройств и фильтров. Один фильтр может получать данные из нескольких источников и быть привязан к нескольким. + +# Отправители (senders) + +Отправители — именованные таймеры, периодически отправляющие данные на привязанные устройства. Создание или добавление устройства — \a addSender(). У каждого отправителя свой таймер и вызов виртуального \a senderData(); возвращённое значение отправляется на устройства. Либо \a setSenderFixedData() — отправка фиксированных данных без вызова \a senderData(). + +# Диагностика + +Для каждого устройства или фильтра создаётся \a PIDiagnostics. Доступ — \a diagnostic(). + +# Конфигурация + +\a PIConnection можно собрать из секции конфига или настроить позже через \a configureFromConfig() (см. \ref config). Устройства задаются полными путями (см. документацию \a PIIODevice). \a makeConfig() формирует строку для вставки в конфиг. diff --git a/doc/pages/console.md b/doc/pages/console.md new file mode 100644 index 00000000..1b8cec63 --- /dev/null +++ b/doc/pages/console.md @@ -0,0 +1,42 @@ +\~english \page console Tiling console (PIScreen) +\~russian \page console Тайлинговая консоль (PIScreen) + +\~english + +\a PIScreen is the console screen manager: it runs a drawing thread, hosts **tiles** (layout regions), and routes keyboard and optionally mouse input to the focused tile. Tiles can contain widgets: text rows, scroll bars, lists, buttons, button groups, check boxes, progress bars, \a PICout output, text input, etc. + +# Basic use + +Create a \a PIScreen (optionally with \c startNow = false and a key callback). Call \a enableExitCapture() so that a key (e.g. 'Q') triggers \a waitForFinish(). Add tiles and attach widgets to them; set focus and layout as needed. \a PIScreen inherits \a PIThread and runs the redraw loop; start it before \a waitForFinish() if you did not use \c startNow. + +# Tiles and layout + +Tiles (\a PIScreenTile) are attached to the screen and arranged in a layout. Each tile can hold widgets. Focus determines which tile receives keyboard input. See \a piscreentile.h, \a piscreentiles.h for composed tile widgets and layout types. + +# Widgets + +Widget types are declared in the console headers: list, button, buttons group, check box, progress bar, text input, terminal (PICout output), etc. Add them to a tile; they draw and react to input within the tile. Details and behavior per widget: \a piscreenconsole.h, \a piscreentiles.h, \a piterminal.h, \a piscreendrawer.h. + +# Notes + +Some widget options or behaviors may be refined in the implementation; when in doubt, check the header and example usage. + +\~russian + +\a PIScreen — менеджер консольного экрана: поток отрисовки, **тайлы** (области раскладки), маршрутизация клавиатуры и при необходимости мыши в активный тайл. В тайлах размещают виджеты: строки текста, скроллбары, списки, кнопки, группы кнопок, галочки, прогрессбары, вывод \a PICout, текстовый ввод и др. + +# Базовое использование + +Создать \a PIScreen (при необходимости \c startNow = false и callback клавиш). Вызвать \a enableExitCapture(), чтобы клавиша (например 'Q') вызывала \a waitForFinish(). Добавлять тайлы и виджеты, настраивать фокус и раскладку. \a PIScreen наследует \a PIThread и выполняет цикл перерисовки; запустить до \a waitForFinish(), если не использовали \c startNow. + +# Тайлы и раскладка + +Тайлы (\a PIScreenTile) присоединяются к экрану и располагаются в раскладке. В каждом тайле — виджеты. Фокус определяет получателя ввода с клавиатуры. Составные виджеты и типы раскладки: \a piscreentile.h, \a piscreentiles.h. + +# Виджеты + +Типы виджетов объявлены в заголовках консоли: список, кнопка, группа кнопок, галочка, прогрессбар, текстовый ввод, терминал (вывод PICout) и др. Их добавляют в тайл; отрисовка и реакция на ввод — внутри тайла. Подробности по виджетам: \a piscreenconsole.h, \a piscreentiles.h, \a piterminal.h, \a piscreendrawer.h. + +# Замечания + +Часть опций или поведения виджетов может уточняться в реализации; при сомнениях смотрите заголовки и примеры. diff --git a/doc/pages/examples.md b/doc/pages/examples.md new file mode 100644 index 00000000..2b1c9fb8 --- /dev/null +++ b/doc/pages/examples.md @@ -0,0 +1,52 @@ +\~english \page examples Examples +\~russian \page examples Примеры + +\~english + +The \c doc/examples directory contains sample code that can be built and run. Below, each file is listed with a short description and a pointer to related documentation where applicable. + +| File | Description | See also | +|------|-------------|----------| +| pibytearray.cpp | \a PIByteArray and binary stream usage | \ref iostream | +| pichunkstream.cpp | \a PIChunkStream read/write | \ref chunk_stream | +| picollection.cpp | Collection helpers | — | +| picontainers.cpp | Containers (\a PIVector, \a PIMap, etc.) | \ref summary | +| piconfig.cpp | \a PIConfig, \a PIConfig::Entry, dotted paths | \ref config | +| picli.cpp | \a PICLI (stub) | \ref application | +| picout.cpp | \a PICout, console output | \ref using_basic | +| pievaluator.cpp | \a PIEvaluator, expression evaluation | \ref summary (Mathematics) | +| piincludes.cpp | Include paths and module discovery | — | +| piiodevice.cpp | \a PIIODevice, custom device and \a PIConfig | \ref config, \ref connection | +| pikbdlistener.cpp | \a PIKbdListener, keyboard input | \ref console | +| pimutex.cpp | \a PIMutex (minimal) | \ref threading | +| piobject.cpp | \a PIObject, events and handlers | \ref PIObject_sec0 | +| piparsehelper.cpp | Parse utilities | — | +| pistatemachine.cpp | \a PIStateMachine, states and transitions | \ref state_machine | +| pitimer.cpp | \a PITimer, periodic callbacks | \ref threading | + +Examples are referenced from the main PIP build when documentation is enabled; paths and build integration may vary by project configuration. + +\~russian + +В каталоге \c doc/examples находятся примеры кода, которые можно собирать и запускать. Ниже перечислены файлы с кратким описанием и ссылкой на связанную документацию. + +| Файл | Описание | См. также | +|------|----------|-----------| +| pibytearray.cpp | \a PIByteArray и бинарный поток | \ref iostream | +| pichunkstream.cpp | Чтение/запись \a PIChunkStream | \ref chunk_stream | +| picollection.cpp | Вспомогательные типы коллекций | — | +| picontainers.cpp | Контейнеры (\a PIVector, \a PIMap и др.) | \ref summary | +| piconfig.cpp | \a PIConfig, \a PIConfig::Entry, точечные пути | \ref config | +| picli.cpp | \a PICLI (заглушка) | \ref application | +| picout.cpp | \a PICout, вывод в консоль | \ref using_basic | +| pievaluator.cpp | \a PIEvaluator, вычисление выражений | \ref summary (Математика) | +| piincludes.cpp | Пути включения и поиск модулей | — | +| piiodevice.cpp | \a PIIODevice, своё устройство и \a PIConfig | \ref config, \ref connection | +| pikbdlistener.cpp | \a PIKbdListener, ввод с клавиатуры | \ref console | +| pimutex.cpp | \a PIMutex (минимальный пример) | \ref threading | +| piobject.cpp | \a PIObject, события и обработчики | \ref PIObject_sec0 | +| piparsehelper.cpp | Утилиты разбора | — | +| pistatemachine.cpp | \a PIStateMachine, состояния и переходы | \ref state_machine | +| pitimer.cpp | \a PITimer, периодические вызовы | \ref threading | + +Примеры подключаются к сборке PIP при включённой документации; пути и способ интеграции зависят от конфигурации проекта. diff --git a/doc/pages/iostream.md b/doc/pages/iostream.md index 272ea97b..5af66050 100644 --- a/doc/pages/iostream.md +++ b/doc/pages/iostream.md @@ -3,8 +3,10 @@ \~english +\a PIBinaryStream is the binary serialization interface. For versioned, extensible formats with chunk ids see \ref chunk_stream. It is not used standalone; only as a mixin or via concrete classes such as \a PIByteArray and \a PIIOBinaryStream. Use it to save or load any data. Trivial types are read/written as memory blocks unless custom operators are defined; non-trivial types must have stream operators or the code will not compile. Containers are supported under the same rules. Enums are treated as int, bool as one byte. Write operators append to the stream; read operators consume from the beginning. Macros: \c BINARY_STREAM_FRIEND(T), \c BINARY_STREAM_WRITE(T), \c BINARY_STREAM_READ(T) (inside them \c s is the stream, \c v is the value). + \~russian -%PIBinaryStream представляет собой интерфейс бинарной сериализации. +%PIBinaryStream представляет собой интерфейс бинарной сериализации. Для версионных расширяемых форматов с id чанков см. \ref chunk_stream. Не может быть использован в чистом виде, только в виде миксина или готовых классов: PIByteArray и PIIOBinaryStream. @@ -28,7 +30,7 @@ * BINARY_STREAM_READ(T) - чтение из потока, "s" - объект потока, "v" - объект типа T. Пример: -\~\code{.cpp} +\code{.cpp} #include class MyType { @@ -69,7 +71,7 @@ int main(int argc, char * argv[]) { \~english Result: \~russian Результат: -\~\code{.cpp} +\code{.cpp} 0a000000040000007400650078007400 10 text @@ -84,7 +86,7 @@ operators of this class simply store/restore data block to/from stream: Для сохранения/извлечения блоков произвольных данных используется класс PIMemoryBlock. Потоковые операторы для него просто сохраняют/извлекают блоки байтов в/из потока: -\~\code{.cpp} +\code{.cpp} float a_read[10], a_write[10]; for (int i = 0; i < 10; ++i) { a_read [i] = 0.f; @@ -103,7 +105,7 @@ for (int i = 0; i < 10; ++i) \~english Result: \~russian Результат: -\~\code{.cpp} +\code{.cpp} 00000000cdcccc3dcdcc4c3e9a99993ecdcccc3e0000003f9a99193f3333333fcdcc4c3f6666663f 0 0.1 @@ -119,7 +121,9 @@ for (int i = 0; i < 10; ++i) \~english +If a read runs out of data (e.g. end of array or file), the stream's \c wasReadError() returns \c true. Check it after reads to handle errors correctly. + \~russian Если при чтении из потока не хватило данных (например, закончился массив или файл), то проверка -объекта потока на wasReadError() вернёт true. Рекомендуется делать эту проверку после чтения +объекта потока на \c wasReadError() вернёт \c true. Рекомендуется делать эту проверку после чтения данных для корректной обработки ошибки. diff --git a/doc/pages/main.md b/doc/pages/main.md index 7800b161..e5612488 100644 --- a/doc/pages/main.md +++ b/doc/pages/main.md @@ -8,19 +8,19 @@ PIP - Platform-Independent Primitives - is crossplatform library for C++ develop This library can help developers write non-GUI projects much more quickly, efficiently and customizable than on pure C++. -Application written on PIP works the same on any system. One can read and write +Applications written on PIP work the same on any system. One can read and write any data types, serialize any types to device channels between any systems. Many common data types, system primitives and devices implemented in this library. -PIP also tightly integrates with [CMake](https://cmake.org/) build system, providing handly search -main library, additional modules of PIP and several utilites. With -CMake with PIP one can easily generate and use code metainformation or -serialize custom types with it versions back-compatability. +PIP also tightly integrates with [CMake](https://cmake.org/) build system, providing handy search for the +main library, additional modules of PIP and several utilities. With +CMake and PIP one can easily generate and use code metainformation or +serialize custom types with version back-compatibility. Summary one can find at \ref summary page. -Basic using of PIP described at \ref using_basic page. +Basic using — \ref using_basic. Further topics — \ref using_advanced. Configuration — \ref config. Code generation — \ref code_model. Streams: \ref iostream, \ref chunk_stream. State machine — \ref state_machine. Complex I/O — \ref connection. TCP client-server — \ref client_server. Tiling console — \ref console. Application tools — \ref application. Threading — \ref threading. Examples — \ref examples. \~russian @@ -41,4 +41,4 @@ PIP также тесно интегрируется с системой сбо Сводку можно найти на странице \ref summary. -Базовое использование PIP описано на странице \ref using_basic. +Базовое использование — \ref using_basic. Дополнительные темы — \ref using_advanced. Конфигурация — \ref config. Кодогенерация — \ref code_model. Потоки: \ref iostream, \ref chunk_stream. Машина состояний — \ref state_machine. Сложный ввод-вывод — \ref connection. TCP клиент-сервер — \ref client_server. Тайлинговая консоль — \ref console. Инструменты приложения — \ref application. Многопоточность — \ref threading. Примеры — \ref examples. diff --git a/doc/pages/state_machine.md b/doc/pages/state_machine.md new file mode 100644 index 00000000..fa65d670 --- /dev/null +++ b/doc/pages/state_machine.md @@ -0,0 +1,69 @@ +\~english \page state_machine State machine +\~russian \page state_machine Машина состояний + +\~english + +The state machine module (\a PIStateMachine) provides hierarchical states, event-driven and timeout-driven transitions, and aligns with the [SCXML](https://www.w3.org/TR/scxml/) idea of state charts. + +# Concepts + +* **State** — a named node with an optional entry handler. You add states with \a PIStateMachine::addState() (or equivalent on the template subclass), giving an enum or id, name, and optional handler. +* **Transition (rule)** — from one state to another, triggered by an event (and optionally guarded). Use \a addRule() to register a transition; you can attach conditions and actions. +* **Event** — an integer id posted with \a postEvent(); the machine delivers it to active states and runs the first matching transition guard. +* **Conditions** — named flags that can be required for a transition (e.g. \a addCondition() on a \a Rule). Call \a performCondition() to set them; \a resetConditions() to clear. +* **Timeout** — a transition can fire after a delay; combine with \a PITimer or internal timeout support in transition classes. + +The machine is a \a PIObject subclass; you can connect timers or other objects to post events. Call \a setInitialState() and \a start() to run. Use \a switchToState() for direct state changes, \a performCondition() to satisfy named conditions. + +# Minimal example + +Define an enum for states, subclass \a PIStateMachine, add states and rules in the constructor, set the initial state, then start. Post events from keyboard, timer, or other handlers. + +\code{.cpp} +enum Mode { Start, Manual, Auto, Finish, End }; + +class Machine : public PIStateMachine { + PIOBJECT_SUBCLASS(Machine, PIObject) +public: + Machine() { + addState(Start, "start", HANDLER(onStart)); + addState(Manual, "manual", HANDLER(onManual)); + addState(Auto, "auto", HANDLER(onAuto)); + addRule(Start, Manual, "init_ok", HANDLER(beginManual)); + addRule(Manual, Auto, HANDLER(toAuto)); + addRule(Auto, Manual, HANDLER(toManual)); + setInitialState(Start); + } + EVENT_HANDLER(void, onStart) { /* entry */ } + EVENT_HANDLER(void, onManual) { /* entry */ } + EVENT_HANDLER(void, onAuto) { /* entry */ } + EVENT_HANDLER(void, beginManual) { /* transition */ } + EVENT_HANDLER(void, toAuto) { } + EVENT_HANDLER(void, toManual) { } +}; + +Machine machine; +// In key handler: machine.performCondition("init_ok"); or machine.switchToState(Manual); +\endcode + +Full example: doc/examples/pistatemachine.cpp. API details: \a PIStateMachine, \a PIStateBase, \a pistatemachine_state.h, \a pistatemachine_transition.h. + +\~russian + +Модуль машины состояний (\a PIStateMachine) предоставляет иерархические состояния, переходы по событиям и по таймауту и ориентирован на идеи [SCXML](https://www.w3.org/TR/scxml/). + +# Концепции + +* **Состояние** — именованный узел с опциональным обработчиком входа. Состояния добавляются через \a PIStateMachine::addState() (или аналог в шаблонном подклассе): enum/id, имя, при необходимости обработчик. +* **Переход (правило)** — из одного состояния в другое по событию (и при выполнении условий). \a addRule() регистрирует переход; можно задать условия и действия. +* **Событие** — целочисленный id, посылаемый через \a postEvent(); машина доставляет его активным состояниям и выполняет первый подходящий переход. +* **Условия** — именованные флаги, требуемые для перехода (например \a addCondition() на \a Rule). Установка через \a performCondition(), сброс — \a resetConditions(). +* **Таймаут** — переход по истечении времени; используется вместе с \a PITimer или встроенной поддержкой таймаутов в классах переходов. + +Машина — подкласс \a PIObject; к ней можно подключать таймеры и другие объекты для посылки событий. Перед запуском задают \a setInitialState() и вызывают \a start(). \a switchToState() — прямая смена состояния, \a performCondition() — выполнение именованного условия. + +# Минимальный пример + +Определяют enum состояний, подкласс \a PIStateMachine, в конструкторе добавляют состояния и правила, задают начальное состояние и запускают. События посылают из обработчика клавиш, таймера и т.д. Код минимального примера приведён выше в англоязычной секции. + +Полный пример: doc/examples/pistatemachine.cpp. Детали API: \a PIStateMachine, \a PIStateBase, \a pistatemachine_state.h, \a pistatemachine_transition.h. diff --git a/doc/pages/summary.md b/doc/pages/summary.md index c7a0dce4..93e297da 100644 --- a/doc/pages/summary.md +++ b/doc/pages/summary.md @@ -35,7 +35,7 @@ * binary log (\a PIBinaryLog) * complex I/O point (\a PIConnection) * peering net node (\a PIPeer) - * connection quality diagnotic (\a PIDiagnostics) + * connection quality diagnostic (\a PIDiagnostics) * Run-time libraries * external process (\a PIProcess) * external library (\a PILibrary) @@ -56,11 +56,13 @@ * single-instance application control (\a PISingleApplication) * high-level log (\a PILog) * translation support (\a PITranslator) -* State machine ([By stantard](https://www.w3.org/TR/scxml/)) (\a PIStateMachine) +* State machine ([By standard](https://www.w3.org/TR/scxml/)) (\a PIStateMachine) * High-level TCP client-server * server (\a PIClientServer::Server, \a PIClientServer::ServerClient) * client (\a PIClientServer::Client) * Crypt support (\a PICrypt, \a PIAuth) +* Cloud (\a PICloudClient, \a PICloudServer) — named endpoints over ethernet +* HTTP client and server (\a PIHTTPClient, \a PIHTTPServer, \a MicrohttpdServer) \~russian @@ -122,3 +124,5 @@ * сервер (\a PIClientServer::Server, \a PIClientServer::ServerClient) * клиент (\a PIClientServer::Client) * Поддержка шифрования (\a PICrypt, \a PIAuth) +* Облако (\a PICloudClient, \a PICloudServer) — именованные конечные точки поверх Ethernet +* HTTP-клиент и сервер (\a PIHTTPClient, \a PIHTTPServer, \a MicrohttpdServer) diff --git a/doc/pages/threading.md b/doc/pages/threading.md new file mode 100644 index 00000000..4f1a133a --- /dev/null +++ b/doc/pages/threading.md @@ -0,0 +1,28 @@ +\~english \page threading Multithreading +\~russian \page threading Многопоточность + +\~english + +The Thread module provides threads, timers, synchronization primitives and task execution: + +* **PIThread** — run a loop or one-off work in a separate thread. Override \a run() or use the default loop; start/stop with \a start() and \a stop(). Can act as the event performer for \a PIObject (see \ref using_advanced "Threading and events"). +* **PITimer** — periodic callbacks at a given frequency (e.g. Hz). Connect to a handler; start/stop the timer. Used in \ref using_basic and in state machine examples. +* **Synchronization** — \a PIMutex, \a PISpinlock, \a PIConditionVariable, \a PISemaphore, \a PIReadWriteLock for protecting shared data and coordinating threads. +* **PIThreadPoolExecutor** — submit tasks to a fixed pool of worker threads; wait for completion or shutdown. +* **PIThreadPoolLoop** — run a function over a range in parallel (parallel-for style). +* **PIBlockingDequeue** — blocking producer-consumer queue for passing work between threads. + +Use \a PIMutexLocker (and similar guards) for exception-safe locking. Events from other threads can be queued and processed in the object's thread via \a callQueuedEvents() (see \ref PIObject_sec0). Full API: \a pithread.h, \a pitimer.h, \a pimutex.h, \a pithreadpoolexecutor.h, \a piblockingqueue.h, \a pithreadmodule.h. + +\~russian + +Модуль Thread предоставляет потоки, таймеры, примитивы синхронизации и выполнение задач: + +* **PIThread** — выполнение цикла или разовой работы в отдельном потоке. Переопределение \a run() или использование цикла по умолчанию; запуск и остановка — \a start() и \a stop(). Может быть исполнителем событий для \a PIObject (см. \ref using_advanced "Потоки и события"). +* **PITimer** — периодические вызовы с заданной частотой (например в Гц). Подключение обработчика; запуск и остановка таймера. Используется в \ref using_basic и в примерах машины состояний. +* **Синхронизация** — \a PIMutex, \a PISpinlock, \a PIConditionVariable, \a PISemaphore, \a PIReadWriteLock для защиты общих данных и согласования потоков. +* **PIThreadPoolExecutor** — отправка задач в пул рабочих потоков; ожидание завершения или остановка пула. +* **PIThreadPoolLoop** — параллельный запуск функции по диапазону (стиль parallel-for). +* **PIBlockingDequeue** — блокирующая очередь производитель–потребитель для передачи работы между потоками. + +Для исключений-безопасной блокировки используйте \a PIMutexLocker и аналогичные охранные классы. События из других потоков можно ставить в очередь и обрабатывать в потоке объекта через \a callQueuedEvents() (см. \ref PIObject_sec0). Полный API: \a pithread.h, \a pitimer.h, \a pimutex.h, \a pithreadpoolexecutor.h, \a piblockingqueue.h, \a pithreadmodule.h. diff --git a/doc/pages/using_advanced.md b/doc/pages/using_advanced.md new file mode 100644 index 00000000..1e48847b --- /dev/null +++ b/doc/pages/using_advanced.md @@ -0,0 +1,64 @@ +\~english \page using_advanced Further topics +\~russian \page using_advanced Дополнительные темы + +\~english + +After \ref using_basic you may want to explore: + +* \ref summary — full list of PIP modules and classes (containers, I/O, threading, math, state machine, etc.). +* \ref config — reading and writing configuration with \a PIConfig (files, dotted paths, INI-style sections). +* \ref code_model — code generation: metadata, serialization operators, PIMETA, \c pip_cmg and CMake integration. +* \ref iostream — binary streams (\a PIBinaryStream, \a PIByteArray), operators and \a PIMemoryBlock. +* \ref chunk_stream — versioned serialization with \a PIChunkStream (chunks by id, backward compatibility). +* \ref state_machine — state machine concepts, states, transitions, conditions, \a PIStateMachine. +* \ref connection — complex I/O: \a PIConnection, device pool, filters, senders, diagnostics. +* \ref client_server — TCP server and client (\a PIClientServer::Server, \a PIClientServer::Client). +* \ref console — tiling console \a PIScreen, tiles, widgets (list, button, progress, input, etc.). +* \ref application — application-level: \a PICLI, \a PILog, \a PISystemMonitor, \a PISingleApplication, \a PITranslator. +* \ref threading — multithreading: \a PIThread, \a PITimer, synchronization, executor, blocking queue. +* \ref examples — index of sample code in doc/examples. + +Events and handlers are documented on the \a PIObject reference page (\ref PIObject_sec0). + +\par Threading and events + +Many PIP classes inherit \a PIObject and use events: handlers can be invoked directly or queued. When events are queued (e.g. from another thread), they are dispatched in the object's thread; call \a callQueuedEvents() or \a maybeCallQueuedEvents() to drain the queue. \a PIThread can act as the performer for such objects. See \ref PIObject_sec0 and \a PIThread. + +\par Introspection + +With \c PIP_INTROSPECTION defined at build time, the introspection module provides macros and APIs to traverse objects and containers at runtime (e.g. for debugging or serialization). Build PIP with this option and link the introspection library; see the introspection headers in \a piintrospection_base.h and related files. + +\par GPU / OpenCL + +The OpenCL module wraps OpenCL for buffers and programs. See \a piopencl.h for the public API. Behavior and limitations may depend on the implementation; check the header and backend when integrating. + +\~russian + +После \ref using_basic имеет смысл перейти к: + +* \ref summary — полный перечень модулей и классов PIP (контейнеры, ввод-вывод, потоки, математика, машина состояний и др.). +* \ref config — чтение и запись конфигурации с помощью \a PIConfig (файлы, точечные пути, секции в стиле INI). +* \ref code_model — кодогенерация: метаинформация, операторы сериализации, PIMETA, утилита \c pip_cmg и интеграция с CMake. +* \ref iostream — бинарные потоки (\a PIBinaryStream, \a PIByteArray), операторы и \a PIMemoryBlock. +* \ref chunk_stream — версионная сериализация с \a PIChunkStream (чанки по id, обратная совместимость). +* \ref state_machine — машина состояний: концепции, состояния, переходы, условия, \a PIStateMachine. +* \ref connection — сложный ввод-вывод: \a PIConnection, пул устройств, фильтры, отправители, диагностика. +* \ref client_server — TCP-сервер и клиент (\a PIClientServer::Server, \a PIClientServer::Client). +* \ref console — тайлинговая консоль \a PIScreen, тайлы, виджеты (список, кнопка, прогресс, ввод и др.). +* \ref application — уровень приложения: \a PICLI, \a PILog, \a PISystemMonitor, \a PISingleApplication, \a PITranslator. +* \ref threading — многопоточность: \a PIThread, \a PITimer, синхронизация, исполнитель, блокирующая очередь. +* \ref examples — перечень примеров в doc/examples. + +События и обработчики описаны на странице \a PIObject (\ref PIObject_sec0). + +\par Потоки и события + +Многие классы PIP наследуют \a PIObject и используют события: обработчики могут вызываться сразу или ставиться в очередь. При постановке в очередь (например из другого потока) они обрабатываются в потоке объекта; вызов \a callQueuedEvents() или \a maybeCallQueuedEvents() обрабатывает очередь. \a PIThread может выступать исполнителем для таких объектов. См. \ref PIObject_sec0 и \a PIThread. + +\par Интроспекция + +При сборке с макросом \c PIP_INTROSPECTION модуль интроспекции предоставляет макросы и API для обхода объектов и контейнеров в runtime (например для отладки или сериализации). Соберите PIP с этой опцией и подключите библиотеку интроспекции; см. заголовки \a piintrospection_base.h и связанные. + +\par GPU / OpenCL + +Модуль OpenCL — обёртка над OpenCL для буферов и программ. Публичный API: \a piopencl.h. Поведение и ограничения зависят от реализации; при интеграции смотрите заголовок и бэкенд. diff --git a/doc/pages/using_basic.md b/doc/pages/using_basic.md index b0f3ee52..b680608b 100644 --- a/doc/pages/using_basic.md +++ b/doc/pages/using_basic.md @@ -3,10 +3,10 @@ \~english -Many novice programmers are solved many common task with system integrity: output to console, -keyboard buttons press detecting, working with serial ports, ethernet or files, and many other. -These tasks can solve this library, and code, based only on PIP will be compile and work -similar on many systems: Windows, any Linux, Red Hat, FreeBSD, MacOS X and QNX. +Many novice programmers face common tasks when interacting with the system: output to console, +detecting keyboard presses, working with serial ports, ethernet or files, and more. +This library addresses these tasks; code based on PIP will compile and work +similarly on many systems: Windows, any Linux, Red Hat, FreeBSD, MacOS X and QNX. Typical application on PIP looks like this: \n \~russian @@ -112,17 +112,17 @@ int main(int argc, char * argv[]) { This code demonstrates simple interactive configurable program, which can be started with console display or not, and with debug or not. \b MainClass is central class that also can be inherited from \a PIThread and reimplement \a run() function. -\n Many PIP classes has events and event handlers, which can be connected one to another. +\n Many PIP classes have events and event handlers, which can be connected one to another. Details you can see at \a PIObject reference page (\ref PIObject_sec0). -\n To configure your program from file use \a PIConfig. -\n If you want more information see \ref using_advanced +\n To configure your program from file use \a PIConfig (see \ref config). +\n For more topics see \ref using_advanced. \~russian Этот код демонстрирует простую конфигурируемую программу, которая может быть запущена с -This code demonstrates simple interactive configurable program, which can be started with console -display or not, and with debug or not. \b MainClass is central class that also can be inherited from -\a PIThread and reimplement \a run() function. -\n Many PIP classes has events and event handlers, which can be connected one to another. -Details you can see at \a PIObject reference page (\ref PIObject_sec0). -\n To configure your program from file use \a PIConfig. +консолью или без неё, с отладочным выводом или без. \b MainClass — центральный класс, который +также может быть унаследован от \a PIThread с переопределением \a run(). +\n У многих классов PIP есть события и обработчики, которые можно связывать между собой. +Подробности — на странице \a PIObject (\ref PIObject_sec0). +\n Для настройки приложения из файла используйте \a PIConfig (см. \ref config). +\n Дополнительные темы — на странице \ref using_advanced. diff --git a/libs/console/piscreenconsole.cpp b/libs/console/piscreenconsole.cpp deleted file mode 100644 index 9983c878..00000000 --- a/libs/console/piscreenconsole.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - PIP - Platform Independent Primitives - Tile for PIScreen with PIConsole API - Andrey Bychkov work.a.b@yandex.ru - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . -*/ - -#include "piscreenconsole.h" - -using namespace PIScreenTypes; - - -TileVars::TileVars(const PIString & n): PIScreenTile(n) { - alignment = Left; -} - - -void TileVars::sizeHint(int & w, int & h) const {} - -void TileVars::drawEvent(PIScreenDrawer * d) {} - - -PIScreenConsoleTile::PIScreenConsoleTile() {} diff --git a/libs/main/application/piapplicationmodule.h b/libs/main/application/piapplicationmodule.h index 024b4061..11fe33de 100644 --- a/libs/main/application/piapplicationmodule.h +++ b/libs/main/application/piapplicationmodule.h @@ -1,3 +1,13 @@ +/*! \file piapplicationmodule.h + * \ingroup Application + * \~\brief + * \~english Application-level classes. + * \~russian Классы уровня "приложение". + * + * \~\details + * \~english Includes the public CLI, logging, single-application, system monitoring, and translation headers. + * \~russian Подключает публичные заголовки CLI, логирования, одиночного приложения, системного мониторинга и перевода. + */ /* PIP - Platform Independent Primitives Module includes @@ -30,14 +40,14 @@ //! target_link_libraries([target] PIP) //! \endcode //! -//! \~english \par Common -//! \~russian \par Общее -//! //! \~english -//! These files provides some classes for help to create application +//! This umbrella header includes public Application classes for command-line +//! parsing, logging, single-instance control, process monitoring and translation. //! //! \~russian -//! Эти файлы предоставляют классы для облегчения создания приложения +//! Этот зонтичный заголовок подключает публичные классы Application для +//! разбора командной строки, ведения лога, контроля одного экземпляра, +//! мониторинга процесса и перевода. //! //! \~\authors //! \~english diff --git a/libs/main/application/picli.h b/libs/main/application/picli.h index e667ffb3..fb0636dc 100644 --- a/libs/main/application/picli.h +++ b/libs/main/application/picli.h @@ -1,9 +1,8 @@ -/*! \file picli.h - * \ingroup Application - * \~\brief - * \~english Command-Line parser - * \~russian Парсер командной строки - */ +//! \~\file picli.h +//! \~\ingroup Application +//! \brief +//! \~english Command-line argument parser +//! \~russian Парсер аргументов командной строки /* PIP - Platform Independent Primitives Command-Line Parser @@ -29,10 +28,15 @@ #include "piset.h" #include "pistringlist.h" -//! \ingroup Application +//! \~\ingroup Application //! \~\brief -//! \~english Command-Line parser. -//! \~russian Парсер командной строки. +//! \~english Command-line argument parser class +//! \~russian Класс парсера аргументов командной строки +//! \details +//! \~english The PICLI class provides convenient parsing of command-line arguments. It supports short keys (e.g., -c), full keys (e.g., +//! --console), and arguments with/without values. +//! \~russian Класс PICLI предоставляет удобный разбор аргументов командной строки. Поддерживаются короткие ключи (например, -c), полные +//! ключи (например, --console) и аргументы с значениями/без значений. class PIP_EXPORT PICLI { public: //! \~english Constructs %PICLI from "argc" and "argv" from "int main()" method. @@ -64,13 +68,25 @@ public: //! \~english Returns unparsed command-line argument by index "index". Index 0 is program execute command. //! \~russian Возвращает исходный аргумент командной строки по индексу "index". Индекс 0 это команда вызова программы. PIString rawArgument(int index); + + //! \~english Returns mandatory positional argument by index. + //! \~russian Возвращает обязательный позиционный аргумент по индексу. PIString mandatoryArgument(int index); + + //! \~english Returns optional positional argument by index. + //! \~russian Возвращает необязательный позиционный аргумент по индексу. PIString optionalArgument(int index); //! \~english Returns unparsed command-line arguments. //! \~russian Возвращает исходные аргументы командной строки. const PIStringList & rawArguments(); + + //! \~english Returns all mandatory positional arguments. + //! \~russian Возвращает все обязательные позиционные аргументы. const PIStringList & mandatoryArguments(); + + //! \~english Returns all optional positional arguments. + //! \~russian Возвращает все необязательные позиционные аргументы. const PIStringList & optionalArguments(); //! \~english Returns program execute command without arguments. @@ -93,18 +109,52 @@ public: //! \~russian Возвращает полный ключ аргумента "name" или пустую строку, если аргумента нет. PIString argumentFullKey(const PIString & name); + //! \~english Returns prefix used for short keys. + //! \~russian Возвращает префикс коротких ключей. const PIString & shortKeyPrefix() const { return _prefix_short; } + + //! \~english Returns prefix used for full keys. + //! \~russian Возвращает префикс полных ключей. const PIString & fullKeyPrefix() const { return _prefix_full; } + + //! \~english Returns expected count of mandatory positional arguments. + //! \~russian Возвращает ожидаемое количество обязательных позиционных аргументов. int mandatoryArgumentsCount() const { return _count_mand; } + + //! \~english Returns expected count of optional positional arguments. + //! \~russian Возвращает ожидаемое количество необязательных позиционных аргументов. int optionalArgumentsCount() const { return _count_opt; } + + //! \~english Sets prefix used for short keys such as "-d". + //! \~russian Устанавливает префикс коротких ключей, например "-d". void setShortKeyPrefix(const PIString & prefix); + + //! \~english Sets prefix used for full keys such as "--debug". + //! \~russian Устанавливает префикс полных ключей, например "--debug". void setFullKeyPrefix(const PIString & prefix); + + //! \~english Sets count of mandatory positional arguments collected before optional ones. + //! \~russian Устанавливает количество обязательных позиционных аргументов, собираемых до необязательных. void setMandatoryArgumentsCount(const int count); + + //! \~english Sets count of optional positional arguments. Negative value means unlimited. + //! \~russian Устанавливает количество необязательных позиционных аргументов. Отрицательное значение означает без ограничения. void setOptionalArgumentsCount(const int count); + //! \~english Returns debug mode flag. + //! \~russian Возвращает флаг режима отладки. bool debug() const { return debug_; } + + //! \~english Enables or disables debug mode. + //! \~russian Включает или выключает режим отладки. void setDebug(bool debug) { debug_ = debug; } + + //! \~english Returns class name. + //! \~russian Возвращает имя класса. PIConstChars className() const { return "PICLI"; } + + //! \~english Returns human-readable object name. + //! \~russian Возвращает читаемое имя объекта. PIString name() const { return PIStringAscii("CLI"); } private: diff --git a/libs/main/application/pilog.h b/libs/main/application/pilog.h index 64fe8ad5..3ffa56d1 100644 --- a/libs/main/application/pilog.h +++ b/libs/main/application/pilog.h @@ -1,12 +1,11 @@ -/*! \file pilog.h - * \ingroup Application - * \~\brief - * \~english High-level log - * \~russian Высокоуровневый лог - */ +//! \~\file pilog.h +//! \~\ingroup Application +//! \~\brief +//! \~english High-level log +//! \~russian Высокоуровневый лог /* PIP - Platform Independent Primitives - High-level log + High-level log Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify @@ -30,15 +29,23 @@ #include "piiostream.h" #include "pithread.h" -//! \ingroup Application +//! \~\ingroup Application //! \~\brief //! \~english High-level log //! \~russian Высокоуровневый лог +//! \~\details +//! \~english Provides log with optional file and console output, with configurable timestamp format and line format. +//! \~russian Предоставляет лог с опциональным выводом в файл и консоль, с настраиваемым форматом метки времени и форматом строки. class PIP_EXPORT PILog: public PIThread { PIOBJECT_SUBCLASS(PILog, PIThread) public: + //! \~english Constructs log with console output, timestamped lines and rotated log files. + //! \~russian Создает лог с выводом в консоль, строками с метками времени и ротацией файлов. PILog(); + + //! \~english Stops logging thread and flushes queued messages. + //! \~russian Останавливает поток логирования и дописывает сообщения из очереди. ~PILog(); //! \~english Message category @@ -58,8 +65,8 @@ public: All /** \~english All \~russian Все */ = 0xFF, }; - //! \~english Set output channel \"o\" to \"on\". - //! \~russian Установить канал вывода \"o\" в \"on\". + //! \~english Enables or disables output channel "o". + //! \~russian Включает или выключает канал вывода "o". void setOutput(Output o, bool on = true) { output.setFlag(o, on); } //! \~english Returns prefix for filename. @@ -80,7 +87,7 @@ public: //! \~english Returns directory for log files. - //! \~russian Возвращает директорию для файлов. + //! \~russian Возвращает директорию файлов лога. PIString dir() const { return log_dir; } //! \~english Set directory for log files. Should be set \b after \a setLogName()! @@ -92,8 +99,8 @@ public: //! \~russian Возвращает время жизни файла. PISystemTime fileSplitTime() const { return split_time; } - //! \~english Set lifetime for file. Each "st" interval new file will be created. - //! \~russian Устанавливает время жизни файла. Каждый интервал "st" будет создан новый файл. + //! \~english Sets log file rotation interval. A new file is created every "st". + //! \~russian Устанавливает интервал ротации файла лога. Новый файл создается каждые "st". void setFileSplitTime(PISystemTime st) { split_time = st; } @@ -110,8 +117,8 @@ public: //! \~russian Возвращает формат строки. PIString lineFormat() const { return line_format; } - //! \~english Set line format. "t" is timestamp, "c" is category and "m" is message. Default is "t - c: m". - //! \~russian Устанавливает формат строки. "t" - метка времени, "c" - категория и "m" - сообщение. По умолчанию "t - c: m". + //! \~english Sets line format. "t" is timestamp, "c" is category and "m" is message. Default is "t - c: m". + //! \~russian Устанавливает формат строки. "t" - метка времени, "c" - категория, "m" - сообщение. По умолчанию "t - c: m". void setLineFormat(const PIString & f); @@ -119,9 +126,8 @@ public: //! \~russian Возвращает максимальную категорию. Level level() const { return max_level; } - //! \~english Set maximum level. All levels greater than \"l\" will be ignored. Default is \a Level::Debug. - //! \~russian Устанавливает максимальную категорию. Все сообщения с большей категорией, чем \"l\", будут игнорироваться. По умолчанию \a - //! Level::Debug. + //! \~english Sets maximum accepted level. Messages above "l" are ignored. Default is \a Level::Debug. + //! \~russian Устанавливает максимальный принимаемый уровень. Сообщения выше "l" игнорируются. По умолчанию \a Level::Debug. void setLevel(Level l); //! \~english Returns \a PICout for \a Level::Error level. @@ -140,12 +146,12 @@ public: //! \~russian Возвращает \a PICout для категории \a Level::Debug. PICout debug(PIObject * context = nullptr); - //! \~english Write all queued lines and stop. Also called in destructor. - //! \~russian Записывает все строки из очереди и останавливается. Также вызывается в деструкторе. + //! \~english Writes all queued lines and stops logging. Also called from destructor. + //! \~russian Записывает все строки из очереди и останавливает логирование. Также вызывается из деструктора. void stop(); - //! \~english Read all previous and current log content and returns them as %PIStringList. - //! \~russian Читает все предыдущие и текущий логи и возвращает их как %PIStringList. + //! \~english Reads all rotated and current log lines and returns them as %PIStringList. + //! \~russian Читает строки из текущего и уже ротированных логов и возвращает их как %PIStringList. PIStringList readAllLogs() const; private: diff --git a/libs/main/application/pisingleapplication.h b/libs/main/application/pisingleapplication.h index 85597c0b..55cc1af1 100644 --- a/libs/main/application/pisingleapplication.h +++ b/libs/main/application/pisingleapplication.h @@ -1,9 +1,8 @@ -/*! \file pisingleapplication.h - * \ingroup Application - * \~\brief - * \~english Single-instance application control - * \~russian Контроль одного экземпляра приложения - */ +//! \~\file pisingleapplication.h +//! \~\ingroup Application +//! \~\brief +//! \~english Single-instance application control +//! \~russian Контроль одного экземпляра приложения /* PIP - Platform Independent Primitives Single application @@ -30,35 +29,44 @@ class PISharedMemory; -//! \ingroup Application +//! \~\ingroup Application //! \~\brief //! \~english Single-instance application control. //! \~russian Контроль одного экземпляра приложения. +//! \~\details +//! \~english The PISingleApplication class provides single-instance application control. It allows detecting if the current application +//! instance is the first one launched and enables inter-process communication between multiple instances. Multiple instances with the same +//! "app_name" can detect each other and communicate. The first instance becomes the server, receiving messages from subsequent instances. +//! \~russian Класс PISingleApplication предоставляет контроль одного экземпляра приложения. Он позволяет определить, является ли текущий +//! экземпляр первым запущенным, и обеспечивает межпроцессное взаимодействие между несколькими экземплярами. Несколько экземпляров с +//! одинаковым "app_name" могут обнаруживать друг друга и обмениваться сообщениями. Первый экземпляр становится сервером, принимающим +//! сообщения от последующих экземпляров. + class PIP_EXPORT PISingleApplication: public PIThread { PIOBJECT_SUBCLASS(PISingleApplication, PIThread); public: - //! \~english Construct %PISingleApplication with name "app_name" - //! \~russian Создает %PISingleApplication с именем "app_name" + //! \~english Constructs %PISingleApplication for application name "app_name". + //! \~russian Создает %PISingleApplication для имени приложения "app_name". PISingleApplication(const PIString & app_name = PIString()); + //! \~english Stops instance monitoring and releases shared resources. + //! \~russian Останавливает мониторинг экземпляра и освобождает общие ресурсы. ~PISingleApplication(); - //! \~english Returns if this application instance is launched first - //! \~russian Возвращает первым ли был запущен этот экземпляр приложения + //! \~english Returns whether this process is the first launched instance. + //! \~russian Возвращает, является ли этот процесс первым запущенным экземпляром. bool isFirst() const; - EVENT_HANDLER1(void, sendMessage, const PIByteArray &, m); - EVENT1(messageReceived, PIByteArray, m); - //! \handlers //! \{ //! \fn void sendMessage(const PIByteArray & m) //! \brief - //! \~english Send message "m" to first launched application - //! \~russian Посылает сообщение "m" первому запущеному приложению + //! \~english Sends message "m" to the first launched application instance + //! \~russian Посылает сообщение "m" первому запущенному экземпляру приложения + EVENT_HANDLER1(void, sendMessage, const PIByteArray &, m); //! \} //! \events @@ -66,8 +74,9 @@ public: //! \fn void messageReceived(PIByteArray m) //! \brief - //! \~english Raise on first launched application receive message from another - //! \~russian Вызывается первым запущеным приложением по приему сообщения от других + //! \~english Emitted by the first launched application when receiving a message from another instance + //! \~russian Вызывается первым запущенным приложением при получении сообщения от другого экземпляра + EVENT1(messageReceived, PIByteArray, m); //! \} diff --git a/libs/main/application/pisystemmonitor.h b/libs/main/application/pisystemmonitor.h index 81daa231..c8bcf731 100644 --- a/libs/main/application/pisystemmonitor.h +++ b/libs/main/application/pisystemmonitor.h @@ -1,9 +1,8 @@ -/*! \file pisystemmonitor.h - * \ingroup Application - * \~\brief - * \~english System resources monitoring - * \~russian Мониторинг ресурсов системы - */ +//! \~\file pisystemmonitor.h +//! \~\ingroup Application +//! \brief +//! \~english System resources monitoring +//! \~russian Мониторинг ресурсов системы /* PIP - Platform Independent Primitives Process resource monitor @@ -30,23 +29,30 @@ #include "pithread.h" -//! \ingroup Application +//! \~\ingroup Application //! \~\brief -//! \~english Process monitoring. -//! \~russian Мониторинг процесса. +//! \~english Process and thread resource monitoring. +//! \~russian Мониторинг ресурсов процесса и его потоков. +//! \details +//! \~english This module provides process and system resource monitoring capabilities including CPU usage, memory consumption, and thread +//! statistics. It supports cross-platform monitoring on Linux, Windows, macOS, and ESP platforms. +//! \~russian Этот модуль предоставляет возможности мониторинга ресурсов процессов и системы, включая использование CPU, потребление памяти +//! и статистику потоков. Поддерживается кроссплатформенный мониторинг на Linux, Windows, macOS и ESP платформах. class PIP_EXPORT PISystemMonitor: public PIThread { PIOBJECT_SUBCLASS(PISystemMonitor, PIThread); friend class PIIntrospectionServer; public: - //! \~english Constructs unassigned %PISystemMonitor - //! \~russian Создает непривязанный %PISystemMonitor + //! \~english Constructs unassigned %PISystemMonitor. + //! \~russian Создает непривязанный %PISystemMonitor. PISystemMonitor(); + //! \~english Stops monitoring and detaches from the current process target. + //! \~russian Останавливает мониторинг и отсоединяет объект от текущей цели. ~PISystemMonitor(); #pragma pack(push, 1) - //! \ingroup Application + //! \~\ingroup Application //! \~\brief //! \~english Process statistics (fixed-size fields). //! \~russian Статистика процесса (фиксированные поля). @@ -95,16 +101,16 @@ public: //! \~russian Память данных в байтах ullong data_memsize = 0; - //! \~english - //! \~russian + //! \~english Total RAM in bytes. + //! \~russian Общий объем RAM в байтах. ullong ram_total = 0; - //! \~english - //! \~russian + //! \~english Free RAM in bytes. + //! \~russian Свободный объем RAM в байтах. ullong ram_free = 0; - //! \~english - //! \~russian + //! \~english Used RAM in bytes. + //! \~russian Используемый объем RAM в байтах. ullong ram_used = 0; //! \~english CPU load in kernel space @@ -116,7 +122,7 @@ public: float cpu_load_user = 0.f; }; - //! \ingroup Application + //! \~\ingroup Application //! \~\brief //! \~english Thread statistics (fixed-size fields). //! \~russian Статистика потока (фиксированные поля). @@ -151,13 +157,13 @@ public: }; #pragma pack(pop) - //! \ingroup Application + //! \~\ingroup Application //! \~\brief //! \~english Process statistics. //! \~russian Статистика процесса. struct PIP_EXPORT ProcessStats: ProcessStatsFixed { - //! \~english Fill human-readable fields - //! \~russian Заполнить читаемые поля + //! \~english Fills human-readable memory size fields. + //! \~russian Заполняет поля с человекочитаемыми размерами памяти. void makeStrings(); //! \~english Execution command @@ -189,7 +195,7 @@ public: PIString data_memsize_readable; }; - //! \ingroup Application + //! \~\ingroup Application //! \~\brief //! \~english Thread statistics. //! \~russian Статистика потока. @@ -201,51 +207,59 @@ public: #ifndef MICRO_PIP - //! \~english Starts monitoring of process with PID "pID" and update interval "interval_ms" milliseconds - //! \~russian Начинает мониторинг процесса с PID "pID" и интервалом обновления "interval_ms" миллисекунд + //! \~english Starts monitoring the process with PID "pID" using the given update interval. + //! \~russian Запускает мониторинг процесса с PID "pID" с указанным интервалом обновления. bool startOnProcess(int pID, PISystemTime interval = PISystemTime::fromSeconds(1.)); #endif - //! \~english Starts monitoring of application process with update interval "interval_ms" milliseconds - //! \~russian Начинает мониторинг процесса приложения с интервалом обновления "interval_ms" миллисекунд + //! \~english Starts monitoring the current application process. + //! \~russian Запускает мониторинг текущего процесса приложения. bool startOnSelf(PISystemTime interval = PISystemTime::fromSeconds(1.)); - //! \~english Stop monitoring - //! \~russian Останавливает мониторинг + //! \~english Stops monitoring. + //! \~russian Останавливает мониторинг. void stop(); - //! \~english Returns monitoring process PID - //! \~russian Возвращает PID наблюдаемого процесса + //! \~english Returns PID of the monitored process. + //! \~russian Возвращает PID наблюдаемого процесса. int pID() const { return pID_; } - //! \~english Returns monitoring process statistics - //! \~russian Возвращает статистику наблюдаемого процесса + //! \~english Returns latest process statistics snapshot. + //! \~russian Возвращает последний снимок статистики процесса. ProcessStats statistic() const; - //! \~english Returns monitoring process threads statistics - //! \~russian Возвращает статистику потоков наблюдаемого процесса + //! \~english Returns latest per-thread statistics snapshot. + //! \~russian Возвращает последний снимок статистики по потокам. PIVector threadsStatistic() const; + //! \~english Replaces current process statistics with external data. + //! \~russian Заменяет текущую статистику процесса внешними данными. void setStatistic(const ProcessStats & s); - //! \~english - //! \~russian + //! \~english Returns total RAM in bytes on supported platforms. + //! \~russian Возвращает общий объем RAM в байтах на поддерживаемых платформах. static ullong totalRAM(); - //! \~english - //! \~russian + //! \~english Returns free RAM in bytes on supported platforms. + //! \~russian Возвращает свободный объем RAM в байтах на поддерживаемых платформах. static ullong freeRAM(); - //! \~english - //! \~russian + //! \~english Returns used RAM in bytes on supported platforms. + //! \~russian Возвращает используемый объем RAM в байтах на поддерживаемых платформах. static ullong usedRAM(); - //! \~english - //! \~russian + //! \~\events + //! \~\{ + + //! \~\fn void measured() + //! \~english Raised after a new statistics snapshot is measured. + //! \~russian Вызывается после измерения нового снимка статисти EVENT(measured); + //! \~\} + private: void run() override; void gatherThread(llong id); diff --git a/libs/main/application/pitranslator.h b/libs/main/application/pitranslator.h index c4435a80..432b1f79 100644 --- a/libs/main/application/pitranslator.h +++ b/libs/main/application/pitranslator.h @@ -1,9 +1,8 @@ -/*! \file pitranslator.h - * \ingroup Application - * \~\brief - * \~english Translation support - * \~russian Поддержка перевода - */ +//! \~\file pitranslator.h +//! \~\ingroup Application +//! \brief +//! \~english Translation support +//! \~russian Поддержка перевода /* PIP - Platform Independent Primitives Translation support @@ -29,24 +28,74 @@ #include "pistring.h" -#define piTr PITranslator::tr -#define piTrNoOp PITranslator::trNoOp +#ifdef DOXYGEN -//! \ingroup Application +//! \relatesalso PITranslator +//! \~\brief +//! \~english Alias to \a PITranslator::tr(). +//! \~russian Алиас к \a PITranslator::tr(). +# define piTr + +//! \relatesalso PITranslator +//! \~\brief +//! \~english Alias to \a PITranslator::trNoOp(). +//! \~russian Алиас к \a PITranslator::trNoOp(). +# define piTrNoOp + +#else + +# define piTr PITranslator::tr +# define piTrNoOp PITranslator::trNoOp + +#endif + +//! \~\ingroup Application //! \~\brief //! \~english Translation support //! \~russian Поддержка перевода +//! \~\details +//! \~english Provides translation support with context-aware string wrappers and multiple loading methods. +//! %PITranslator stores loaded translations in a process-wide singleton. +//! If translation or context is missing, the source string is returned unchanged. +//! \~russian Предоставляет поддержку перевода с контекстно-зависимыми обертками строк и методами загрузки. +//! %PITranslator хранит загруженные переводы в синглтоне процесса. +//! Если перевод или контекст не найдены, исходная строка возвращается без изменений. class PIP_EXPORT PITranslator { public: + //! \~english Returns translated string for "in" in optional "context". + //! \~russian Возвращает перевод строки "in" в необязательном "context". static PIString tr(const PIString & in, const PIString & context = {}); + + //! \~english Converts UTF-8 string literal to %PIString and translates it. + //! \~russian Преобразует UTF-8 строковый литерал в %PIString и переводит его. static PIString tr(const char * in, const PIString & context = {}) { return tr(PIString::fromUTF8(in), context); } + + //! \~english Marks string for translation-aware code paths and returns it unchanged. + //! \~russian Помечает строку для кода, работающего с переводом, и возвращает ее без изменений. static PIString trNoOp(const PIString & in, const PIString & context = {}) { return in; } + + //! \~english UTF-8 overload of \a trNoOp(). + //! \~russian UTF-8 перегрузка для \a trNoOp(). static PIString trNoOp(const char * in, const PIString & context = {}) { return trNoOp(PIString::fromUTF8(in), context); } + //! \~english Clears all loaded translations. + //! \~russian Очищает все загруженные переводы. static void clear(); + + //! \~english Clears current translations and loads language files matching "short_lang" from "dir". + //! \~russian Очищает текущие переводы и загружает языковые файлы, соответствующие "short_lang", из "dir". static void loadLang(const PIString & short_lang, PIString dir = {}); + + //! \~english Loads translations from textual configuration content. + //! \~russian Загружает переводы из текстового конфигурационного содержимого. static void loadConfig(const PIString & content); + + //! \~english Loads translations from binary content in PIP translation format. + //! \~russian Загружает переводы из бинарного содержимого в формате переводов PIP. static bool load(const PIByteArray & content); + + //! \~english Loads translations from file and checks its translation header. + //! \~russian Загружает переводы из файла и проверяет его заголовок переводов. static bool loadFile(const PIString & path); private: @@ -59,10 +108,24 @@ private: }; +//! \~\ingroup Application +//! \~\brief +//! \~english Context-aware string wrapper that automatically translates strings using PITranslator::tr(). +//! Helper returned by \a operator""_tr for optional-context translation. +//! \~russian Контекстно-зависимая обертка строки, автоматически переводящая строки с помощью PITranslator::tr(). +//! Вспомогательный тип, возвращаемый \a operator""_tr для перевода с необязательным контекстом. class PIStringContextTr { public: + //! \~english Stores source string for later translation. + //! \~russian Сохраняет исходную строку для последующего перевода. PIStringContextTr(PIString && s): _s(s) {} + + //! \~english Translates stored string without explicit context. + //! \~russian Переводит сохраненную строку без явного контекста. operator PIString() const { return PITranslator::tr(_s); } + + //! \~english Translates stored string in context "ctx". + //! \~russian Переводит сохраненную строку в контексте "ctx". PIString operator()(const PIString & ctx = {}) const { return PITranslator::tr(_s, ctx); } private: @@ -70,10 +133,24 @@ private: }; +//! \~\ingroup Application +//! \~\brief +//! \~english Context-aware string wrapper that preserves original strings without translation (no-op). +//! Helper returned by \a operator""_trNoOp that keeps source text unchanged. +//! \~russian Контекстно-зависимая обертка строки, сохраняющая оригинальные строки без перевода (заглушка). +//! Вспомогательный тип, возвращаемый \a operator""_trNoOp, который сохраняет исходный текст без изменений. class PIStringContextTrNoOp { public: + //! \~english Stores source string without translating it. + //! \~russian Сохраняет исходную строку без перевода. PIStringContextTrNoOp(PIString && s): _s(s) {} + + //! \~english Returns stored string unchanged. + //! \~russian Возвращает сохраненную строку без изменений. operator PIString() const { return _s; } + + //! \~english Returns stored string unchanged and ignores "ctx". + //! \~russian Возвращает сохраненную строку без изменений и игнорирует "ctx". PIString operator()(const PIString & ctx = {}) const { return _s; } private: @@ -82,15 +159,17 @@ private: //! \~\brief -//! \~english Translate string with \a PITranslator::tr() -//! \~russian Перевести строку с помощью \a PITranslator::tr() +//! \~english User-defined literal that defers translation through \a PITranslator::tr(). +//! \~russian Пользовательский литерал, откладывающий перевод через \a PITranslator::tr(). +//! \~\code "hello"_tr \endcode inline PIStringContextTr operator""_tr(const char * v, size_t sz) { return PIStringContextTr(PIString::fromUTF8(v, sz)); } //! \~\brief -//! \~english Translate string with \a PITranslator::tr() -//! \~russian Перевести строку с помощью \a PITranslator::tr() +//! \~english User-defined literal that keeps source text unchanged via \a PITranslator::trNoOp(). +//! \~russian Пользовательский литерал, сохраняющий исходный текст без изменений через \a PITranslator::trNoOp(). +//! \~\code "hello"_trNoOp \endcode inline PIStringContextTrNoOp operator""_trNoOp(const char * v, size_t sz) { return PIStringContextTrNoOp(PIString::fromUTF8(v, sz)); } diff --git a/libs/main/client_server/piclientserver_client.h b/libs/main/client_server/piclientserver_client.h index 24e45eb0..417da6f1 100644 --- a/libs/main/client_server/piclientserver_client.h +++ b/libs/main/client_server/piclientserver_client.h @@ -1,9 +1,8 @@ -/*! \file piclientserver_client.h - * \ingroup ClientServer - * \~\brief - * \~english - * \~russian - */ +//! \~\file piclientserver_client.h +//! \~\ingroup ClientServer +//! \brief +//! \~english Client-side and server-side client connection classes +//! \~russian Классы клиентского подключения и серверного представления клиента /* PIP - Platform Independent Primitives Ivan Pelipenko peri4ko@yandex.ru @@ -33,18 +32,27 @@ namespace PIClientServer { // ServerClient -//! ~english Server-side client implementation -//! ~russian Серверная реализация клиента +//! \~\ingroup ClientServer +//! \~\brief +//! \~english Server-side representation of one accepted client connection. +//! \~russian Серверное представление одного принятого клиентского соединения. +//! \details +//! \~english Server-side client representation. Created and managed by Server. Used to communicate with remote clients connected to the +//! server. +//! \~russian Представление клиента на стороне сервера. Создаётся и управляется сервером. Используется для коммуникации с удалёнными +//! клиентами, подключёнными к серверу. class PIP_CLIENT_SERVER_EXPORT ServerClient: public ClientBase { friend class Server; NO_COPY_CLASS(ServerClient); public: + //! \~english Constructs an unbound server-side client object. + //! \~russian Создает непривязанный объект серверного клиента. ServerClient() {} protected: - //! ~english Called before client destruction - //! ~russian Вызывается перед уничтожением клиента + //! \~english Called right before the server deletes this client object. + //! \~russian Вызывается непосредственно перед удалением этого объекта сервером. virtual void aboutDelete() {} private: @@ -54,17 +62,28 @@ private: // Client -//! ~english Client implementation for connecting to servers -//! ~russian Клиентская реализация для подключения к серверам +//! \~\ingroup ClientServer +//! \~\brief +//! \~english Active client connection that initiates a connection to a server. +//! \~russian Активное клиентское соединение, которое само подключается к серверу. +//! \details +//! \~english Client implementation for connecting to servers. Provides TCP connection to remote server with diagnostics and packet +//! streaming support. +//! \~russian Реализация клиента для подключения к серверам. Обеспечивает TCP-соединение с удалённым сервером с поддержкой диагностики и +//! потоковой передачи пакетов. class PIP_CLIENT_SERVER_EXPORT Client: public ClientBase { NO_COPY_CLASS(Client); public: + //! \~english Constructs a client ready to connect to a remote server. + //! \~russian Создает клиент, готовый к подключению к удаленному серверу. Client(); + //! \~english Destroys the client and closes its connection if needed. + //! \~russian Уничтожает клиента и при необходимости закрывает его соединение. ~Client(); - //! ~english Connects to specified server address - //! ~russian Подключается к указанному адресу сервера + //! \~english Connects to the server at address "addr". + //! \~russian Подключается к серверу по адресу "addr". void connect(PINetworkAddress addr); protected: diff --git a/libs/main/client_server/piclientserver_client_base.h b/libs/main/client_server/piclientserver_client_base.h index 844da0d1..e4c11b46 100644 --- a/libs/main/client_server/piclientserver_client_base.h +++ b/libs/main/client_server/piclientserver_client_base.h @@ -1,9 +1,11 @@ -/*! \file piclientserver_client_base.h - * \ingroup ClientServer - * \~\brief - * \~english - * \~russian - */ +//! \~\file piclientserver_client_base.h +//! \~\ingroup ClientServer +//! \brief +//! \~english Base class for client-server communication +//! \~russian Базовый класс для клиент-серверного взаимодействия +//! \details +//! \~english Provides base functionality for client-server communication with diagnostics support. +//! \~russian Обеспечивает базовую функциональность для клиент-серверного взаимодействия с поддержкой диагностики. /* PIP - Platform Independent Primitives Ivan Pelipenko peri4ko@yandex.ru @@ -35,77 +37,93 @@ namespace PIClientServer { class Server; -class ClientInterface {}; - -//! ~english Base class for client-server communication with diagnostics support -//! ~russian Базовый класс для клиент-серверного взаимодействия с поддержкой диагностики +//! \~\ingroup ClientServer +//! \~\brief +//! \~english Base class for client and server-side client communication. Provides TCP connection management, diagnostics, and packet +//! streaming capabilities. +//! \~russian Базовый класс для клиентской и серверной коммуникации. Обеспечивает управление TCP-соединением, диагностику и потоковую +//! передачу пакетов. // template class PIP_CLIENT_SERVER_EXPORT ClientBase { friend class Server; NO_COPY_CLASS(ClientBase); public: + //! \~english Constructs a disconnected client connection object. + //! \~russian Создает объект клиентского соединения в отключенном состоянии. ClientBase(); + + //! \~english Destroys the client connection and releases owned resources. + //! \~russian Уничтожает клиентское соединение и освобождает связанные ресурсы. virtual ~ClientBase(); - //! ~english Gets underlying TCP connection - //! ~russian Возвращает TCP-соединение + + //! \~english Returns the underlying TCP transport object. + //! \~russian Возвращает базовый объект TCP-транспорта. const PIEthernet * getTCP() const { return tcp; } - //! ~english Closes the connection - //! ~russian Закрывает соединение + //! \~english Closes the connection immediately. + //! \~russian Немедленно закрывает соединение. void close(); - //! ~english Gracefully stops and waits for completion - //! ~russian Плавно останавливает и ожидает завершения + //! \~english Stops the connection workflow and waits until shutdown completes. + //! \~russian Останавливает работу соединения и ждет полного завершения. void stopAndWait(); - //! ~english Writes byte array to the connection - //! ~russian Записывает массив байтов в соединение + //! \~english Sends raw payload bytes through the stream packer. + //! \~russian Отправляет сырые байты полезной нагрузки через упаковщик потока. int write(const void * d, const size_t s); - //! ~english Writes byte array to the connection - //! ~russian Записывает массив байтов в соединение + //! \~english Sends payload stored in "ba". + //! \~russian Отправляет полезную нагрузку из "ba". int write(const PIByteArray & ba) { return write(ba.data(), ba.size()); } - //! ~english Enables diagnostics collection - //! ~russian Включает сбор диагностики + //! \~english Enables connection diagnostics collection. + //! \~russian Включает сбор диагностики соединения. void enableDiagnostics(); - //! ~english Gets current diagnostics state - //! ~russian Возвращает текущее состояние диагностики + //! \~english Returns a snapshot of current diagnostics counters. + //! \~russian Возвращает снимок текущих диагностических счетчиков. PIDiagnostics::State diagnostics() const; - //! ~english Gets current received packet bytes already received (all bytes count passed in \a receivePacketStart()) - //! ~russian Возвращает сколько байт принимаемого пакета получено (общее количество передается в \a receivePacketStart()) + //! \~english Returns how many payload bytes of the current packet are already received (all bytes count passed in \a receivePacketStart()). + //! \~russian Возвращает, сколько байтов полезной нагрузки текущего пакета уже получено (общее количество передается в \a receivePacketStart()). int receivePacketProgress() const; + //! \~english Returns the current packet framing configuration. + //! \~russian Возвращает текущую конфигурацию пакетирования. const PIStreamPackerConfig & configuration() const { return stream.configuration(); } + + //! \~english Returns the current packet framing configuration for modification. + //! \~russian Возвращает текущую конфигурацию пакетирования для изменения. PIStreamPackerConfig & configuration() { return stream.configuration(); } + + //! \~english Replaces the packet framing configuration. + //! \~russian Заменяет конфигурацию пакетирования. void setConfiguration(const PIStreamPackerConfig & config) { stream.setConfiguration(config); } protected: - //! ~english Called when data is received - //! ~russian Вызывается при получении данных + //! \~english Called when a full payload packet is received. + //! \~russian Вызывается при получении полного пакета полезной нагрузки. virtual void readed(PIByteArray data) {} - //! ~english Called when connection is established - //! ~russian Вызывается при установке соединения + //! \~english Called after the TCP connection becomes active. + //! \~russian Вызывается после перехода TCP-соединения в активное состояние. virtual void connected() {} - //! ~english Called when connection is closed - //! ~russian Вызывается при закрытии соединения + //! \~english Called after the connection is closed. + //! \~russian Вызывается после закрытия соединения. virtual void disconnected() {} - //! ~english Called when packet receiving starts - //! ~russian Вызывается при начале получения пакета + //! \~english Called when reception of a new packet starts. + //! \~russian Вызывается при начале приема нового пакета. virtual void receivePacketStart(int size) {} - //! ~english Called when packet receiving ends - //! ~russian Вызывается при завершении получения пакета + //! \~english Called when reception of the current packet finishes. + //! \~russian Вызывается при завершении приема текущего пакета. virtual void receivePacketEnd() {} void init(); diff --git a/libs/main/client_server/piclientserver_server.h b/libs/main/client_server/piclientserver_server.h index f66eca5f..14bf68bb 100644 --- a/libs/main/client_server/piclientserver_server.h +++ b/libs/main/client_server/piclientserver_server.h @@ -1,9 +1,8 @@ -/*! \file piclientserver_server.h - * \ingroup ClientServer - * \~\brief - * \~english - * \~russian - */ +//! \~\file piclientserver_server.h +//! \~\ingroup ClientServer +//! \brief +//! \~english TCP Server +//! \~russian TCP Сервер /* PIP - Platform Independent Primitives Ivan Pelipenko peri4ko@yandex.ru @@ -38,54 +37,72 @@ namespace PIClientServer { class ServerClient; -//! ~english TCP server for client-server communication -//! ~russian TCP сервер для клиент-серверного взаимодействия +//! \~\ingroup ClientServer +//! \~\brief +//! \~english TCP server implementation for client-server communication. Accepts incoming connections and manages multiple clients with +//! configurable factory for client instance creation. +//! \~russian Реализация TCP сервера для клиент-серверного взаимодействия. Принимает входящие соединения и управляет несколькими клиентами с +//! настраиваемой фабрикой для создания экземпляров клиентов. class PIP_CLIENT_SERVER_EXPORT Server: public PIStreamPackerConfig { friend class ServerClient; NO_COPY_CLASS(Server); public: + //! \~english Constructs a stopped server with default limits. + //! \~russian Создает остановленный сервер с ограничениями по умолчанию. Server(); + + //! \~english Stops the server and releases transport resources. + //! \~russian Останавливает сервер и освобождает транспортные ресурсы. virtual ~Server(); - //! ~english Starts listening on specified address - //! ~russian Начинает прослушивание на указанном адресе + //! \~english Starts listening for clients on address "addr". + //! \~russian Начинает принимать клиентов по адресу "addr". void listen(PINetworkAddress addr); - //! ~english Starts listening on all interfaces - //! ~russian Начинает прослушивание на всех интерфейсах + //! \~english Starts listening on all interfaces at port "port". + //! \~russian Начинает прослушивание на всех интерфейсах на порту "port". + //! \~\details + //! \~english Equivalent to listen({0, port}), binds to all available network interfaces. + //! \~russian Эквивалентно listen({0, port}), привязывается ко всем доступным сетевым интерфейсам. void listenAll(ushort port) { listen({0, port}); } - //! ~english Stops the server - //! ~russian Останавливает сервер + //! \~english Stops accepting clients and shuts the server down. + //! \~russian Прекращает прием клиентов и завершает работу сервера. void stopServer(); - //! ~english Closes all client connections - //! ~russian Закрывает все клиентские соединения + //! \~english Closes all currently connected clients. + //! \~russian Закрывает все текущие клиентские соединения. + //! \~\details + //! \~english Immediately closes all active client connections without stopping the server. + //! \~russian Немедленно закрывает все активные клиентские соединения без остановки сервера. void closeAll(); - //! ~english Gets maximum allowed clients - //! ~russian Возвращает максимальное число клиентов + //! \~english Returns the configured maximum number of simultaneous clients. + //! \~russian Возвращает настроенный максимум одновременных клиентов. int getMaxClients() const { return max_clients; } - //! ~english Sets maximum allowed clients - //! ~russian Устанавливает максимальное число клиентов + //! \~english Sets the maximum number of simultaneous clients. + //! \~russian Устанавливает максимальное число одновременных клиентов. + //! \~\details + //! \~english Sets the maximum number of concurrent client connections. Default is 1000. + //! \~russian Устанавливает максимальное количество одновременных клиентских подключений. По умолчанию 1000. void setMaxClients(int new_max_clients); - //! ~english Gets current clients count - //! ~russian Возвращает текущее количество клиентов + //! \~english Returns the number of currently connected clients. + //! \~russian Возвращает текущее количество подключенных клиентов. int clientsCount() const; - //! ~english Executes function for each connected client - //! ~russian Выполняет функцию для каждого подключённого клиента + //! \~english Calls "func" for each currently connected client. + //! \~russian Вызывает "func" для каждого текущего подключенного клиента. void forEachClient(std::function func); - //! ~english Sets factory for creating new client instances - //! ~russian Устанавливает фабрику для создания клиентских экземпляров + //! \~english Sets the factory used to create accepted client objects. + //! \~russian Устанавливает фабрику, создающую объекты принятых клиентов. void setClientFactory(std::function f) { client_factory = f; } private: diff --git a/libs/main/client_server/piclientservermodule.h b/libs/main/client_server/piclientservermodule.h index fc3f100e..1b55529c 100644 --- a/libs/main/client_server/piclientservermodule.h +++ b/libs/main/client_server/piclientservermodule.h @@ -1,7 +1,12 @@ +//! \~\file piclientservermodule.h +//! \~\ingroup ClientServer +//! \~\brief +//! \~english Umbrella header and module group for TCP client-server helpers +//! \~russian Общий заголовок и группа модуля для TCP-клиент-серверных компонентов /* PIP - Platform Independent Primitives Module includes - Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru + Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -34,10 +39,10 @@ //! \~russian \par Общее //! //! \~english -//! These files provides server with clients dispatching for server-side and client for client-side. +//! These headers provide a TCP server with server-side client objects and an active client for remote connections. //! //! \~russian -//! Эти файлы предоставляют сервер с диспетчеризацией клиентов для серверной стороны и клиента для клиентской стороны. +//! Эти заголовки предоставляют TCP-сервер с серверными объектами клиентов и активного клиента для удаленных подключений. //! //! \~\authors //! \~english diff --git a/libs/main/cloud/picloudbase.h b/libs/main/cloud/picloudbase.h index f6978bd2..6525348b 100644 --- a/libs/main/cloud/picloudbase.h +++ b/libs/main/cloud/picloudbase.h @@ -1,9 +1,8 @@ -/*! \file picloudbase.h - * \ingroup Cloud - * \~\brief - * \~english Base class for PICloudClient and PICloudServer - * \~russian Базовый класс для PICloudClient и PICloudServer - */ +//! \~\file picloudbase.h +//! \~\ingroup Cloud +//! \brief +//! \~english Base class for PICloudClient and PICloudServer. +//! \~russian Базовый класс для PICloudClient и PICloudServer. /* PIP - Platform Independent Primitives PICloud Base - Base class for PICloudClient and PICloud Server @@ -30,11 +29,18 @@ #include "piethernet.h" #include "pistreampacker.h" - +//! \~\ingroup Cloud +//! \brief +//! \~english Base class for cloud client and server. +//! \~russian Базовый класс для облачного клиента и сервера. class PIP_CLOUD_EXPORT PICloudBase { public: + //! \~english Constructs the shared PICloud transport state. + //! \~russian Создает общее транспортное состояние PICloud. PICloudBase(); + //! \~english Returns the logical server name configured for the transport. + //! \~russian Возвращает логическое имя сервера, настроенное для транспорта. PIString serverName() const; protected: diff --git a/libs/main/cloud/picloudclient.h b/libs/main/cloud/picloudclient.h index 505ed027..5214324c 100644 --- a/libs/main/cloud/picloudclient.h +++ b/libs/main/cloud/picloudclient.h @@ -1,9 +1,8 @@ -/*! \file picloudclient.h - * \ingroup Cloud - * \~\brief - * \~english PICloud Client - * \~russian Клиент PICloud - */ +//! \~\file picloudclient.h +//! \~\ingroup Cloud +//! \~\brief +//! \~english Client-side PICloud device for one named server +//! \~russian Клиентское устройство PICloud для одного именованного сервера /* PIP - Platform Independent Primitives PICloud Client @@ -29,27 +28,61 @@ #include "picloudbase.h" #include "piconditionvar.h" - -//! \brief PICloudClient - +//! \~\ingroup Cloud +//! \~\brief +//! \~english %PIIODevice implementation for a logical PICloud client. +//! \~russian Реализация %PIIODevice для логического клиента PICloud. class PIP_CLOUD_EXPORT PICloudClient : public PIIODevice , public PICloudBase { PIIODEVICE(PICloudClient, ""); public: + //! \~english Constructs a client for transport endpoint "path" and mode "mode". + //! \~russian Создает клиент для транспортной точки "path" и режима "mode". explicit PICloudClient(const PIString & path = PIString(), PIIODevice::DeviceMode mode = PIIODevice::ReadWrite); + + //! \~english Destroys the client and closes the underlying transport. + //! \~russian Уничтожает клиент и закрывает нижележащий транспорт. virtual ~PICloudClient(); + + //! \~english Sets the logical server name used during the PICloud handshake. + //! \~russian Устанавливает логическое имя сервера, используемое при рукопожатии PICloud. void setServerName(const PIString & server_name); + + //! \~english Enables or disables automatic reconnect of the underlying TCP link. + //! \~russian Включает или выключает автоматическое переподключение нижележащего TCP-соединения. void setKeepConnection(bool on); + + //! \~english Returns whether the logical PICloud session is established. + //! \~russian Возвращает, установлена ли логическая сессия PICloud. bool isConnected() const { return is_connected; } + + //! \~english Returns the number of payload bytes buffered for \a read(). + //! \~russian Возвращает количество байтов полезной нагрузки, буферизованных для \a read(). ssize_t bytesAvailable() const override { return buff.size(); } + + //! \~english Interrupts pending connection and read waits. + //! \~russian Прерывает ожидающие операции подключения и чтения. void interrupt() override; + + //! \events + //! \{ + + //! \fn void connected() + //! \~english Raised after the logical PICloud session becomes ready. + //! \~russian Вызывается после того, как логическая сессия PICloud готова к работе. EVENT(connected); + + //! \fn void disconnected() + //! \~english Raised when the logical PICloud session is closed. + //! \~russian Вызывается при закрытии логической сессии PICloud. EVENT(disconnected); + //! \} + protected: bool openDevice() override; bool closeDevice() override; diff --git a/libs/main/cloud/picloudmodule.h b/libs/main/cloud/picloudmodule.h index e171c9e3..1feddd86 100644 --- a/libs/main/cloud/picloudmodule.h +++ b/libs/main/cloud/picloudmodule.h @@ -1,3 +1,13 @@ +/*! \file picloudmodule.h + * \ingroup Cloud + * \~\brief + * \~english Umbrella header for the PICloud module + * \~russian Зонтичный заголовок модуля PICloud + * + * \~\details + * \~english Includes the public client, server, and low-level transport helpers of the module. + * \~russian Подключает публичные клиентские, серверные и низкоуровневые транспортные помощники модуля. + */ /* PIP - Platform Independent Primitives Module includes @@ -18,8 +28,8 @@ */ //! \defgroup Cloud Cloud //! \~\brief -//! \~english Cloud transport over ethernet -//! \~russian Облачный транспорт через ethernet +//! \~english Named cloud endpoints over an ethernet transport +//! \~russian Именованные облачные конечные точки поверх Ethernet-транспорта //! //! \~\details //! \~english \section cmake_module_Cloud Building with CMake @@ -34,10 +44,10 @@ //! \~russian \par Общее //! //! \~english -//! These files provides server-side and client-side of PICloud transport. +//! Includes logical client and server devices together with low-level PICloud framing helpers. //! //! \~russian -//! Эти файлы обеспечивают серверную и клиентскую сторону транспорта PICloud. +//! Подключает логические клиентские и серверные устройства вместе с низкоуровневыми помощниками кадрирования PICloud. //! //! \~\authors //! \~english diff --git a/libs/main/cloud/picloudserver.h b/libs/main/cloud/picloudserver.h index cb36cd14..7b97fa16 100644 --- a/libs/main/cloud/picloudserver.h +++ b/libs/main/cloud/picloudserver.h @@ -1,9 +1,8 @@ -/*! \file picloudserver.h - * \ingroup Cloud - * \~\brief - * \~english PICloud Server - * \~russian Сервер PICloud - */ +//! \~\file picloudserver.h +//! \~\ingroup Cloud +//! \~\brief +//! \~english Server-side PICloud device for one named endpoint +//! \~russian Серверное устройство PICloud для одной именованной конечной точки /* PIP - Platform Independent Primitives PICloud Server @@ -29,23 +28,43 @@ #include "picloudbase.h" #include "piconditionvar.h" - +//! \~\ingroup Cloud +//! \~\brief +//! \~english %PIIODevice implementation for a logical PICloud server. +//! \~russian Реализация %PIIODevice для логического сервера PICloud. class PIP_CLOUD_EXPORT PICloudServer : public PIIODevice , public PICloudBase { PIIODEVICE(PICloudServer, ""); public: - //! PICloudServer + //! \~english Constructs a logical server for transport endpoint "path" and mode "mode". + //! \~russian Создает логический сервер для транспортной точки "path" и режима "mode". explicit PICloudServer(const PIString & path = PIString(), PIIODevice::DeviceMode mode = PIIODevice::ReadWrite); + + //! \~english Destroys the server and closes all logical clients. + //! \~russian Уничтожает сервер и закрывает всех логических клиентов. virtual ~PICloudServer(); + + //! \~\ingroup Cloud + //! \~\brief + //! \~english Per-client %PIIODevice exposed by %PICloudServer. + //! \~russian Клиентское %PIIODevice, предоставляемое %PICloudServer. + //! \~\details + //! \~english Instances are created by \a newConnection() and represent one logical cloud client. + //! \~russian Экземпляры создаются через \a newConnection() и представляют одного логического облачного клиента. class Client: public PIIODevice { PIIODEVICE(PICloudServer::Client, ""); friend class PICloudServer; public: + //! \~english Constructs a wrapper for logical client "id" owned by server "srv". + //! \~russian Создает обертку для логического клиента "id", принадлежащего серверу "srv". Client(PICloudServer * srv = nullptr, uint id = 0); + + //! \~english Destroys the client wrapper. + //! \~russian Уничтожает клиентскую обертку. virtual ~Client(); protected: @@ -67,12 +86,25 @@ public: std::atomic_bool is_connected; }; + //! \~english Sets the logical server name announced by this server. + //! \~russian Устанавливает логическое имя сервера, объявляемое этим сервером. void setServerName(const PIString & server_name); + //! \~english Returns a snapshot of the currently connected logical clients. + //! \~russian Возвращает снимок текущих подключенных логических клиентов. PIVector clients() const; + + //! \events + //! \{ + + //! \fn void newConnection(PICloudServer::Client * client) + //! \~english Raised when a new logical client appears for this server name. + //! \~russian Вызывается, когда для этого имени сервера появляется новый логический клиент. EVENT1(newConnection, PICloudServer::Client *, client); + //! \} + protected: bool openDevice() override; bool closeDevice() override; diff --git a/libs/main/cloud/picloudtcp.h b/libs/main/cloud/picloudtcp.h index 43756ec4..a0e0563a 100644 --- a/libs/main/cloud/picloudtcp.h +++ b/libs/main/cloud/picloudtcp.h @@ -1,9 +1,9 @@ -/*! \file picloudtcp.h - * \ingroup Cloud - * \~\brief - * \~english PICloud TCP transport - * \~russian TCP слой PICloud - */ +//! \~\file picloudtcp.h +//! \~\ingroup Cloud +//! \~\brief +//! \~english Low-level TCP framing helpers for PICloud +//! \~russian Низкоуровневые помощники TCP-кадрирования для PICloud +//! /* PIP - Platform Independent Primitives PICloud TCP transport @@ -31,50 +31,113 @@ #include "pistring.h" -class PIEthernet; class PIStreamPacker; +//! \~\ingroup Cloud +//! \~english Namespace for low-level PICloud transport helpers. +//! \~russian Пространство имен для низкоуровневых транспортных помощников PICloud. namespace PICloud { +//! \~\ingroup Cloud +//! \~\brief +//! \~english Builds and parses PICloud frames on top of %PIStreamPacker. +//! \~russian Формирует и разбирает кадры PICloud поверх %PIStreamPacker. class PIP_CLOUD_EXPORT TCP { public: + //! \~english Supported PICloud frame versions. + //! \~russian Поддерживаемые версии кадров PICloud. enum Version { - Version_1 = 1, - Version_2 = 2, + Version_1 = 1 /** \~english First protocol version \~russian Первая версия протокола */, + Version_2 = 2 /** \~english Current protocol version \~russian Текущая версия протокола */ }; + //! \~english Logical destination role of a PICloud frame. + //! \~russian Логическая роль получателя кадра PICloud. enum Role { - InvalidRole = 0, - Server = 1, - Client = 2, + InvalidRole = 0 /** \~english Invalid or unknown role \~russian Некорректная или неизвестная роль */, + Server = 1 /** \~english Frame for a logical server \~russian Кадр для логического сервера */, + Client = 2 /** \~english Frame for a logical client \~russian Кадр для логического клиента */ }; + //! \~english Kind of PICloud frame payload. + //! \~russian Вид полезной нагрузки кадра PICloud. enum Type { - InvalidType = 0, - Connect = 1, - Disconnect = 2, - Data = 3, - Ping = 4, + InvalidType = 0 /** \~english Invalid or unknown frame \~russian Некорректный или неизвестный кадр */, + Connect = 1 /** \~english Connect or registration frame \~russian Кадр подключения или регистрации */, + Disconnect = 2 /** \~english Disconnect notification frame \~russian Кадр уведомления об отключении */, + Data = 3 /** \~english Payload data frame \~russian Кадр с полезными данными */, + Ping = 4 /** \~english Keepalive frame \~russian Кадр поддержания соединения */ }; + + //! \~english Constructs a PICloud frame helper bound to packer "s". + //! \~russian Создает помощник кадров PICloud, связанный с упаковщиком "s". TCP(PIStreamPacker * s); + + //! \~english Sets the logical role written into outgoing frames. + //! \~russian Устанавливает логическую роль, записываемую в исходящие кадры. void setRole(Role r); + + //! \~english Returns the logical role of this helper. + //! \~russian Возвращает логическую роль этого помощника. Role role() const { return (Role)header.role; } + + //! \~english Sets the logical server name used by connect and keepalive frames. + //! \~russian Устанавливает логическое имя сервера, используемое кадрами подключения и поддержания соединения. void setServerName(const PIString & server_name_); + + //! \~english Returns the configured logical server name. + //! \~russian Возвращает настроенное логическое имя сервера. PIString serverName() const; + + //! \~english Sends the initial connect frame for the current server name. + //! \~russian Отправляет начальный кадр подключения для текущего имени сервера. void sendStart(); + + //! \~english Sends a connect frame for logical client "client_id". + //! \~russian Отправляет кадр подключения для логического клиента "client_id". void sendConnected(uint client_id); + + //! \~english Sends a disconnect frame for logical client "client_id". + //! \~russian Отправляет кадр отключения для логического клиента "client_id". void sendDisconnected(uint client_id); + + //! \~english Sends a payload frame for the current logical role. + //! \~russian Отправляет кадр с полезными данными для текущей логической роли. int sendData(const PIByteArray & data); + + //! \~english Sends a payload frame tagged with logical client "client_id". + //! \~russian Отправляет кадр с полезными данными, помеченный логическим клиентом "client_id". int sendData(const PIByteArray & data, uint client_id); + + //! \~english Sends a keepalive frame. + //! \~russian Отправляет кадр поддержания соединения. void sendPing(); + + //! \~english Parses frame header and returns its type and destination role. + //! \~russian Разбирает заголовок кадра и возвращает его тип и роль получателя. PIPair parseHeader(PIByteArray & ba); + + //! \~english Returns whether current role uses direct payload parsing. + //! \~russian Возвращает, использует ли текущая роль прямой разбор полезной нагрузки. bool canParseData(PIByteArray & ba); + + //! \~english Extracts logical client identifier and payload from a server-side data frame. + //! \~russian Извлекает идентификатор логического клиента и полезную нагрузку из серверного кадра данных. PIPair parseDataServer(PIByteArray & ba); + + //! \~english Validates and returns raw connect payload used for server identity exchange. + //! \~russian Проверяет и возвращает сырой payload подключения, используемый при обмене идентичностью сервера. PIByteArray parseConnect_d(PIByteArray & ba); + + //! \~english Extracts logical client identifier from a connect frame. + //! \~russian Извлекает идентификатор логического клиента из кадра подключения. uint parseConnect(PIByteArray & ba); + + //! \~english Extracts logical client identifier from a disconnect frame. + //! \~russian Извлекает идентификатор логического клиента из кадра отключения. uint parseDisconnect(PIByteArray & ba); private: diff --git a/libs/main/code/picodeinfo.h b/libs/main/code/picodeinfo.h index 96c275ae..caf3f80b 100644 --- a/libs/main/code/picodeinfo.h +++ b/libs/main/code/picodeinfo.h @@ -1,9 +1,11 @@ -/*! \file picodeinfo.h - * \ingroup Code - * \~\brief - * \~english C++ code info structs. See \ref code_model. - * \~russian Структуры для C++ кода. Подробнее \ref code_model. - */ +//! \~\file picodeinfo.h +//! \~\ingroup Code +//! \~\brief +//! \~english C++ code info structs. See \ref code_model. +//! \~russian Структуры для C++ кода. Подробнее \ref code_model. +//! \~\details +//! \~english Contains structures for code generation and reflection: TypeInfo, FunctionInfo, ClassInfo, EnumInfo, EnumeratorInfo. +//! \~russian Содержит структуры для кодогенерации и рефлексии: TypeInfo, FunctionInfo, ClassInfo, EnumInfo, EnumeratorInfo. /* PIP - Platform Independent Primitives C++ code info structs @@ -36,34 +38,49 @@ class PIVariant; //! \~english Namespace contains structures for code generation. See \ref code_model. //! \~russian Пространство имен содержит структуры для кодогенерации. Подробнее \ref code_model. +//! \~\details +//! \~english Provides classes and structures for code introspection, including type information, function details, class metadata, and enum +//! information. +//! \~russian Предоставляет классы и структуры для интроспекции кода, включая информацию о типах, детали функций, метаданные классов и +//! информацию о перечислениях. namespace PICodeInfo { -//! \~english -//! Type modifiers -//! \~russian -//! Модификаторы типа +//! \~english Type modifiers. +//! \~russian Модификаторы типа. enum TypeFlag { - NoFlag, - Const /** const */ = 0x01, - Static /** static */ = 0x02, - Mutable /** mutable */ = 0x04, - Volatile /** volatile */ = 0x08, - Inline /** inline */ = 0x10, - Virtual /** virtual */ = 0x20, - Extern /** extern */ = 0x40 + NoFlag /** \~english No modifiers. \~russian Модификаторы отсутствуют. */, + Const = 0x01 /** \~english \c const modifier. \~russian Модификатор \c const. */, + Static = 0x02 /** \~english \c static modifier. \~russian Модификатор \c static. */, + Mutable = 0x04 /** \~english \c mutable modifier. \~russian Модификатор \c mutable. */, + Volatile = 0x08 /** \~english \c volatile modifier. \~russian Модификатор \c volatile. */, + Inline = 0x10 /** \~english \c inline modifier. \~russian Модификатор \c inline. */, + Virtual = 0x20 /** \~english \c virtual modifier. \~russian Модификатор \c virtual. */, + Extern = 0x40 /** \~english \c extern modifier. \~russian Модификатор \c extern. */ }; +//! \~english Bitmask of type modifiers. +//! \~russian Битовая маска модификаторов типа. typedef PIFlags TypeFlags; +//! \~english Custom metadata map produced by \c PIMETA. +//! \~russian Карта пользовательских метаданных, создаваемых \c PIMETA. typedef PIMap MetaMap; +//! \~english Callback returning serialized member data by member name. +//! \~russian Обратный вызов, возвращающий сериализованные данные члена по имени. typedef PIByteArray (*AccessValueFunction)(const void *, const char *); +//! \~english Callback returning a member type name by member name. +//! \~russian Обратный вызов, возвращающий имя типа члена по его имени. typedef const char * (*AccessTypeFunction)(const char *); +//! \~english Callback returning a member offset by member name. +//! \~russian Обратный вызов, возвращающий смещение члена по его имени. typedef int (*AccessOffsetFunction)(const char *); //! \~english Type information //! \~russian Информация о типе struct PIP_EXPORT TypeInfo { + //! \~english Constructs type information for one variable or argument. + //! \~russian Создает описание типа для одной переменной или аргумента. TypeInfo(const PIConstChars & n = PIConstChars(), const PIConstChars & t = PIConstChars(), PICodeInfo::TypeFlags f = 0, int b = -1) { name = n; type = t; @@ -71,8 +88,8 @@ struct PIP_EXPORT TypeInfo { bits = b; } - //! \~english Returns if variable if bitfield - //! \~russian Возвращает битовым ли полем является переменная + //! \~english Returns whether the described variable is a bitfield. + //! \~russian Возвращает, является ли описываемая переменная битовым полем. bool isBitfield() const { return bits > 0; } //! \~english Custom PIMETA content @@ -109,7 +126,7 @@ struct PIP_EXPORT FunctionInfo { PIConstChars name; //! \~english Return type - //! \~russian Возвращаемые тип + //! \~russian Возвращаемый тип TypeInfo return_type; //! \~english Arguments types @@ -121,26 +138,28 @@ struct PIP_EXPORT FunctionInfo { //! \~english Class or struct information //! \~russian Информация о классе или структуре struct PIP_EXPORT ClassInfo { + //! \~english Constructs an empty class description. + //! \~russian Создает пустое описание класса. ClassInfo() { is_anonymous = false; } //! \~english Custom PIMETA content //! \~russian Произвольное содержимое PIMETA MetaMap meta; - //! \~english Anonymous or not - //! \~russian Анонимный или нет + //! \~english Indicates that the type was declared without a name + //! \~russian Показывает, что тип был объявлен без имени bool is_anonymous; - //! \~english Type - //! \~russian Тип + //! \~english Declaration kind, for example \c class or \c struct + //! \~russian Вид объявления, например \c class или \c struct PIConstChars type; //! \~english Name //! \~russian Имя PIConstChars name; - //! \~english Parent names - //! \~russian Имена родителей + //! \~english Base class names + //! \~russian Имена базовых классов PIVector parents; //! \~english Variables @@ -151,8 +170,8 @@ struct PIP_EXPORT ClassInfo { //! \~russian Методы PIVector functions; - //! \~english Subclass list - //! \~russian Список наследников + //! \~english Registered derived class descriptions + //! \~russian Зарегистрированные описания производных классов PIVector children_info; }; @@ -160,10 +179,14 @@ struct PIP_EXPORT ClassInfo { //! \~english Enumerator information //! \~russian Информация об элементе перечисления struct PIP_EXPORT EnumeratorInfo { + //! \~english Constructs one enum member description. + //! \~russian Создает описание одного элемента перечисления. EnumeratorInfo(const PIConstChars & n = PIConstChars(), int v = 0) { name = n; value = v; } + //! \~english Converts the enumerator to the %PIVariantTypes representation. + //! \~russian Преобразует элемент перечисления в представление %PIVariantTypes. PIVariantTypes::Enumerator toPIVariantEnumerator() { return PIVariantTypes::Enumerator(value, name.toString()); } //! \~english Custom PIMETA content @@ -183,16 +206,16 @@ struct PIP_EXPORT EnumeratorInfo { //! \~english Enum information //! \~russian Информация о перечислении struct PIP_EXPORT EnumInfo { - //! \~english Returns member name with value "value" - //! \~russian Возвращает имя элемента со значением "value" + //! \~english Returns the member name for the value \a value. + //! \~russian Возвращает имя элемента для значения \a value. PIString memberName(int value) const; - //! \~english Returns member value with name "name" - //! \~russian Возвращает значение элемента с именем "name" + //! \~english Returns the member value for the name \a name. + //! \~russian Возвращает значение элемента для имени \a name. int memberValue(const PIString & name) const; - //! \~english Returns as PIVariantTypes::Enum - //! \~russian Возвращает как PIVariantTypes::Enum + //! \~english Converts the enum description to %PIVariantTypes::Enum. + //! \~russian Преобразует описание перечисления в %PIVariantTypes::Enum. PIVariantTypes::Enum toPIVariantEnum(); //! \~english Custom PIMETA content @@ -209,6 +232,8 @@ struct PIP_EXPORT EnumInfo { }; +//! \~english Writes a declaration-like view of \a v to \a s. +//! \~russian Записывает в \a s представление \a v в стиле объявления. inline PICout operator<<(PICout s, const PICodeInfo::TypeInfo & v) { if (v.flags[Inline]) s << "inline "; if (v.flags[Virtual]) s << "virtual "; @@ -221,11 +246,15 @@ inline PICout operator<<(PICout s, const PICodeInfo::TypeInfo & v) { return s; } +//! \~english Writes an enum member description to \a s. +//! \~russian Записывает описание элемента перечисления в \a s. inline PICout operator<<(PICout s, const PICodeInfo::EnumeratorInfo & v) { s << v.name << " = " << v.value << " Meta" << v.meta; return s; } +//! \~english Writes a human-readable class description to \a s. +//! \~russian Записывает в \a s человекочитаемое описание класса. inline PICout operator<<(PICout s, const PICodeInfo::ClassInfo & v) { s.saveAndSetControls(0); s << "class " << v.name; @@ -262,6 +291,8 @@ inline PICout operator<<(PICout s, const PICodeInfo::ClassInfo & v) { return s; } +//! \~english Writes a human-readable enum description to \a s. +//! \~russian Записывает в \a s человекочитаемое описание перечисления. inline PICout operator<<(PICout s, const PICodeInfo::EnumInfo & v) { s.saveAndSetControls(0); s << "enum " << v.name << " Meta" << v.meta << " {\n"; @@ -296,71 +327,70 @@ private: NO_COPY_CLASS(__Storage__) }; -class PIP_EXPORT -__StorageAccess__{public: - //! \~english Getter for single storage of PICodeInfo::ClassInfo, access by name - //! \~russian Доступ к единому хранилищу PICodeInfo::ClassInfo, доступ по имени - static const PIMap & classes(){return *(__Storage__::instance()->classesInfo); -} // namespace PICodeInfo +class PIP_EXPORT __StorageAccess__ { +public: + static const PIMap & classes() { + return *(__Storage__::instance()->classesInfo); + } // namespace PICodeInfo -//! \~english Getter for single storage of PICodeInfo::EnumInfo, access by name -//! \~russian Доступ к единому хранилищу хранилище PICodeInfo::EnumInfo, доступ по имени -static const PIMap & enums() { - return *(__Storage__::instance()->enumsInfo); -} + static const PIMap & enums() { return *(__Storage__::instance()->enumsInfo); } -static const PIMap & accessValueFunctions() { - return *(__Storage__::instance()->accessValueFunctions); -} + static const PIMap & accessValueFunctions() { + return *(__Storage__::instance()->accessValueFunctions); + } -static const PIMap & accessTypeFunctions() { - return *(__Storage__::instance()->accessTypeFunctions); -} + static const PIMap & accessTypeFunctions() { + return *(__Storage__::instance()->accessTypeFunctions); + } -static const PIMap & accessOffsetFunctions() { - return *(__Storage__::instance()->accessOffsetFunctions); -} -} -; + static const PIMap & accessOffsetFunctions() { + return *(__Storage__::instance()->accessOffsetFunctions); + } +}; +//! \relatesalso PICodeInfo +//! \~english Macro for the global code model registries. +//! \~russian Макрос для доступа к глобальным реестрам модели кода. #define PICODEINFO PICodeInfo::__StorageAccess__ -class PIP_EXPORT -ClassInfoInterface{public: const PIMap * operator->() const DEPRECATEDM("use PICODEINFO::classes()"){ - return __Storage__::instance() -> classesInfo; -} -} -; +class PIP_EXPORT ClassInfoInterface { +public: + const PIMap * operator->() const DEPRECATEDM("use PICODEINFO::classes()") { + return __Storage__::instance()->classesInfo; + } +}; + static ClassInfoInterface classesInfo; -class PIP_EXPORT -EnumsInfoInterface{public: const PIMap * operator->() const DEPRECATEDM("use PICODEINFO::enums()"){ - return __Storage__::instance() -> enumsInfo; -} -} -; +class PIP_EXPORT EnumsInfoInterface { +public: + const PIMap * operator->() const DEPRECATEDM("use PICODEINFO::enums()") { + return __Storage__::instance()->enumsInfo; + } +}; + static EnumsInfoInterface enumsInfo; -class PIP_EXPORT AccessValueFunctionInterface{ - public: const PIMap * operator->() - const DEPRECATEDM("use PICODEINFO::accessValueFunctions()"){ - return __Storage__::instance() -> accessValueFunctions; -} -} -; +class PIP_EXPORT AccessValueFunctionInterface { +public: + const PIMap * operator->() const DEPRECATEDM("use PICODEINFO::accessValueFunctions()") { + return __Storage__::instance()->accessValueFunctions; + } +}; + static AccessValueFunctionInterface accessValueFunctions; -class PIP_EXPORT AccessTypeFunctionInterface{ - public: const PIMap * operator->() - const DEPRECATEDM("use PICODEINFO::accessTypeFunctions()"){ - return __Storage__::instance() -> accessTypeFunctions; -} -} -; +class PIP_EXPORT AccessTypeFunctionInterface { +public: + const PIMap * operator->() const DEPRECATEDM("use PICODEINFO::accessTypeFunctions()") { + return __Storage__::instance()->accessTypeFunctions; + } +}; + static AccessTypeFunctionInterface accessTypeFunctions; @@ -372,6 +402,8 @@ STATIC_INITIALIZER_BEGIN STATIC_INITIALIZER_END +//! \~english Returns a serialized value of \a member_name from an instance of \a class_name. +//! \~russian Возвращает сериализованное значение \a member_name из экземпляра \a class_name. inline PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name) { if (!p || !class_name || !member_name) return PIByteArray(); AccessValueFunction af = PICODEINFO::accessValueFunctions().value(class_name, (AccessValueFunction)0); @@ -379,6 +411,8 @@ inline PIByteArray getMemberValue(const void * p, const char * class_name, const return af(p, member_name); } +//! \~english Returns the registered type name of \a member_name in \a class_name. +//! \~russian Возвращает зарегистрированное имя типа \a member_name в \a class_name. inline const char * getMemberType(const char * class_name, const char * member_name) { if (!class_name || !member_name) return ""; AccessTypeFunction af = PICODEINFO::accessTypeFunctions().value(class_name, (AccessTypeFunction)0); @@ -386,14 +420,20 @@ inline const char * getMemberType(const char * class_name, const char * member_n return af(member_name); } +//! \~english Returns \a member_name from \a class_name as %PIVariant when accessors are registered. +//! \~russian Возвращает \a member_name из \a class_name как %PIVariant, если зарегистрированы функции доступа. PIP_EXPORT PIVariant getMemberAsVariant(const void * p, const char * class_name, const char * member_name); +//! \~english Serializes assignable values into \a ret through the stream operator. +//! \~russian Сериализует присваиваемые значения в \a ret через оператор потока. template::value, int>::type = 0> void serialize(PIByteArray & ret, const T & v) { ret << v; } +//! \~english Fallback overload for values that cannot be written to the byte-array stream. +//! \~russian Резервная перегрузка для значений, которые нельзя записать в поток массива байт. template::value, int>::type = 0> void serialize(PIByteArray & ret, const T & v) {} diff --git a/libs/main/code/picodemodule.h b/libs/main/code/picodemodule.h index 6fc20f44..26cf0ed1 100644 --- a/libs/main/code/picodemodule.h +++ b/libs/main/code/picodemodule.h @@ -1,3 +1,13 @@ +/*! \file picodemodule.h + * \ingroup Code + * \~\brief + * \~english Umbrella header for the code parsing module + * \~russian Общий заголовок модуля разбора кода + * + * \~\details + * \~english Includes the public code information and parser headers. + * \~russian Подключает публичные заголовки информации о коде и парсера. + */ /* PIP - Platform Independent Primitives Module includes @@ -34,12 +44,12 @@ //! \~russian \par Общее //! //! \~english -//! These files provides parsing C++ code and storage to use results of \a pip_cmg utility. -//! See \ref code_model. +//! This module provides C++ source parsing and the storage types used by the +//! \a pip_cmg utility. See \ref code_model. //! //! \~russian -//! Эти файлы обеспечивают разбор C++ кода и хранение результатов работы утилиты \a pip_cmg. -//! Подробнее \ref code_model. +//! Этот модуль предоставляет разбор исходного кода C++ и типы хранения, +//! используемые утилитой \a pip_cmg. Подробнее \ref code_model. //! //! \~\authors //! \~english diff --git a/libs/main/code/picodeparser.h b/libs/main/code/picodeparser.h index 5444f569..ca1692f4 100644 --- a/libs/main/code/picodeparser.h +++ b/libs/main/code/picodeparser.h @@ -1,9 +1,8 @@ -/*! \file picodeparser.h - * \ingroup Code - * \~\brief - * \~english C++ code parser - * \~russian Разбор C++ кода - */ +//! \~\file picodeparser.h +//! \~\ingroup Code +//! \~\brief +//! \~english C++ code parser +//! \~russian Разбор C++ кода /* PIP - Platform Independent Primitives C++ code parser @@ -37,44 +36,88 @@ inline bool _isCChar(const PIString & c) { return _isCChar(c[0]); } +//! \~\ingroup Code +//! \~\brief +//! \~english Parser of C/C++ declarations used by the code model tools. +//! \~russian Разборщик объявлений C/C++, используемый инструментами модели кода. +//! \~\details +//! \~english Parser for analyzing C++ source files. Extracts information about classes, structures, enums, macros, functions, and members. +//! \~russian Парсер для анализа C++ исходных файлов. Извлекает информацию о классах, структурах, перечислениях, макросах, функциях и +//! членах. class PIP_EXPORT PICodeParser { public: + //! \~english Constructs a parser with built-in PIP macro presets. + //! \~russian Создает разборщик со встроенными предустановками макросов PIP. PICodeParser(); + //! \~english Visibility of a parsed declaration inside the current scope. + //! \~russian Видимость разобранного объявления в текущей области. enum Visibility { - Global, - Public, - Protected, - Private - }; - enum Attribute { - NoAttributes = 0x0, - Const = 0x01, - Static = 0x02, - Mutable = 0x04, - Volatile = 0x08, - Inline = 0x10, - Virtual = 0x20, - Extern = 0x40 + Global /** \~english Global or namespace-level declaration. \~russian Глобальное объявление или объявление уровня пространства имен. + */ + , + Public /** \~english Public class member. \~russian Открытый член класса. */, + Protected /** \~english Protected class member. \~russian Защищенный член класса. */, + Private /** \~english Private class member. \~russian Закрытый член класса. */ }; + //! \~english Parsed declaration attributes. + //! \~russian Атрибуты разобранного объявления. + enum Attribute { + NoAttributes = 0x0 /** \~english No attributes. \~russian Атрибуты отсутствуют. */, + Const = 0x01 /** \~english \c const declaration. \~russian Объявление с \c const. */, + Static = 0x02 /** \~english \c static declaration. \~russian Объявление с \c static. */, + Mutable = 0x04 /** \~english \c mutable declaration. \~russian Объявление с \c mutable. */, + Volatile = 0x08 /** \~english \c volatile declaration. \~russian Объявление с \c volatile. */, + Inline = 0x10 /** \~english \c inline declaration. \~russian Объявление с \c inline. */, + Virtual = 0x20 /** \~english \c virtual declaration. \~russian Объявление с \c virtual. */, + Extern = 0x40 /** \~english \c extern declaration. \~russian Объявление с \c extern. */ + }; + + //! \~english Bitmask of parsed declaration attributes. + //! \~russian Битовая маска атрибутов разобранного объявления. typedef PIFlags Attributes; + + //! \~english Preprocessor define name and value. + //! \~russian Имя и значение макроса \c define. typedef PIPair Define; + + //! \~english Typedef alias and target type. + //! \~russian Псевдоним typedef и целевой тип. typedef PIPair Typedef; + + //! \~english Parsed metadata map. + //! \~russian Карта разобранных метаданных. typedef PIMap MetaMap; + //! \~english Parsed function-like macro. + //! \~russian Разобранный функциональный макрос. struct PIP_EXPORT Macro { Macro(const PIString & n = PIString(), const PIString & v = PIString(), const PIStringList & a = PIStringList()) { name = n; value = v; args = a; } + + //! \~english Expands the macro body with arguments from \a args_. + //! \~russian Разворачивает тело макроса с аргументами из \a args_. PIString expand(PIString args_, bool * ok = 0) const; + + //! \~english Macro name. + //! \~russian Имя макроса. PIString name; + + //! \~english Macro replacement text. + //! \~russian Текст замены макроса. PIString value; + + //! \~english Ordered list of macro argument names. + //! \~russian Упорядоченный список имен аргументов макроса. PIStringList args; }; + //! \~english Parsed member declaration or function signature. + //! \~russian Разобранное объявление члена или сигнатура функции. struct PIP_EXPORT Member { Member() { visibility = Global; @@ -83,20 +126,58 @@ public: is_type_ptr = false; attributes = NoAttributes; } + + //! \~english Returns whether the member is declared as a bitfield. + //! \~russian Возвращает, объявлен ли член как битовое поле. bool isBitfield() const { return bits > 0; } + + //! \~english Parsed metadata attached to the member. + //! \~russian Разобранные метаданные, привязанные к члену. MetaMap meta; + + //! \~english Member type or return type. + //! \~russian Тип члена или возвращаемый тип. PIString type; + + //! \~english Member name. + //! \~russian Имя члена. PIString name; + + //! \~english Full textual argument declarations. + //! \~russian Полные текстовые объявления аргументов. PIStringList arguments_full; + + //! \~english Argument types only. + //! \~russian Только типы аргументов. PIStringList arguments_type; + + //! \~english Parsed array dimensions. + //! \~russian Разобранные размеры массива. PIStringList dims; + + //! \~english Member visibility. + //! \~russian Видимость члена. Visibility visibility; + + //! \~english Member attributes. + //! \~russian Атрибуты члена. Attributes attributes; + + //! \~english Indicates that the parsed type is a pointer. + //! \~russian Показывает, что разобранный тип является указателем. bool is_type_ptr; + + //! \~english Parsed size in bytes when available. + //! \~russian Разобранный размер в байтах, если он известен. int size; + + //! \~english Bit count for bitfields, or \c -1 otherwise. + //! \~russian Количество бит для битового поля или \c -1 в остальных случаях. int bits; }; + //! \~english Parsed class, struct or namespace. + //! \~russian Разобранный класс, структура или пространство имен. struct PIP_EXPORT Entity { Entity() { visibility = Global; @@ -104,57 +185,165 @@ public: size = 0; parent_scope = 0; } + + //! \~english Parsed metadata attached to the entity. + //! \~russian Разобранные метаданные, привязанные к сущности. MetaMap meta; + + //! \~english Entity kind, for example \c class, \c struct or \c namespace. + //! \~russian Вид сущности, например \c class, \c struct или \c namespace. PIString type; + + //! \~english Entity name. + //! \~russian Имя сущности. PIString name; + + //! \~english Source file where the entity was parsed. + //! \~russian Исходный файл, в котором была разобрана сущность. PIString file; + + //! \~english Entity visibility inside its parent scope. + //! \~russian Видимость сущности внутри родительской области. Visibility visibility; + + //! \~english Parsed size in bytes when available. + //! \~russian Разобранный размер в байтах, если он известен. int size; + + //! \~english Indicates that the entity was declared without a name. + //! \~russian Показывает, что сущность объявлена без имени. bool is_anonymous; + + //! \~english Immediate containing entity, or \c nullptr for the root scope. + //! \~russian Непосредственная содержащая сущность или \c nullptr для корневой области. Entity * parent_scope; + + //! \~english Direct base entities. + //! \~russian Непосредственные базовые сущности. PIVector parents; + + //! \~english Parsed member functions. + //! \~russian Разобранные функции-члены. PIVector functions; + + //! \~english Parsed data members. + //! \~russian Разобранные поля данных. PIVector members; + + //! \~english Typedefs declared inside the entity. + //! \~russian Typedef-объявления внутри сущности. PIVector typedefs; }; + //! \~english Parsed enumerator entry. + //! \~russian Разобранный элемент перечисления. struct PIP_EXPORT EnumeratorInfo { EnumeratorInfo(const PIString & n = PIString(), int v = 0, const MetaMap & m = MetaMap()) { name = n; value = v; meta = m; } + + //! \~english Parsed metadata attached to the enumerator. + //! \~russian Разобранные метаданные, привязанные к элементу перечисления. MetaMap meta; + + //! \~english Enumerator name. + //! \~russian Имя элемента перечисления. PIString name; + + //! \~english Enumerator value. + //! \~russian Значение элемента перечисления. int value; }; + //! \~english Parsed enumeration. + //! \~russian Разобранное перечисление. struct PIP_EXPORT Enum { Enum(const PIString & n = PIString()) { name = n; } + + //! \~english Parsed metadata attached to the enum. + //! \~russian Разобранные метаданные, привязанные к перечислению. MetaMap meta; + + //! \~english Enum name. + //! \~russian Имя перечисления. PIString name; + + //! \~english Parsed enumerators. + //! \~russian Разобранные элементы перечисления. PIVector members; }; + //! \~english Parses one source file and optionally follows its includes. + //! \~russian Разбирает один исходный файл и при необходимости следует по его include-зависимостям. void parseFile(const PIString & file, bool follow_includes = true); + + //! \~english Parses several source files into one parser state. + //! \~russian Разбирает несколько исходных файлов в одном состоянии разборщика. void parseFiles(const PIStringList & files, bool follow_includes = true); + + //! \~english Parses source text provided directly in memory. + //! \~russian Разбирает исходный текст, переданный напрямую из памяти. void parseFileContent(PIString fc); + + //! \~english Adds a directory to the include search list. + //! \~russian Добавляет каталог в список поиска include-файлов. void includeDirectory(const PIString & dir) { includes << dir; } + + //! \~english Adds a custom preprocessor definition before parsing. + //! \~russian Добавляет пользовательское препроцессорное определение перед разбором. void addDefine(const PIString & def_name, const PIString & def_value) { custom_defines << Define(def_name, def_value); } + + //! \~english Returns whether an enum with \a name was parsed. + //! \~russian Возвращает, было ли разобрано перечисление с именем \a name. bool isEnum(const PIString & name); + + //! \~english Finds a parsed entity by its full name. + //! \~russian Ищет разобранную сущность по ее полному имени. Entity * findEntityByName(const PIString & en); + + //! \~english Returns the set of files already processed by the parser. + //! \~russian Возвращает набор файлов, уже обработанных разборщиком. PIStringList parsedFiles() const { return PIStringList(proc_files.toVector()); } + + //! \~english Returns the file detected as the main translation unit. + //! \~russian Возвращает файл, определенный как основной единицей трансляции. PIString mainFile() const { return main_file; } + + //! \~english Returns the synthetic global scope entity. + //! \~russian Возвращает синтетическую сущность глобальной области. const PICodeParser::Entity * global() const { return &root_; } + + //! \~english Returns the maximum number of macro substitution passes. + //! \~russian Возвращает максимальное число проходов подстановки макросов. int macrosSubstitutionMaxIterations() const { return macros_iter; } + + //! \~english Sets the maximum number of macro substitution passes. + //! \~russian Задает максимальное число проходов подстановки макросов. void setMacrosSubstitutionMaxIterations(int value) { macros_iter = value; } + + //! \~english Parsed \c define directives, including built-in and custom ones. + //! \~russian Разобранные директивы \c define, включая встроенные и пользовательские. PIVector defines, custom_defines; + + //! \~english Parsed function-like macros. + //! \~russian Разобранные функциональные макросы. PIVector macros; + + //! \~english Parsed enums from the processed files. + //! \~russian Разобранные перечисления из обработанных файлов. PIVector enums; + + //! \~english Parsed top-level typedef declarations. + //! \~russian Разобранные typedef-объявления верхнего уровня. PIVector typedefs; + + //! \~english Parsed entities discovered in the processed files. + //! \~russian Разобранные сущности, найденные в обработанных файлах. PIVector entities; private: diff --git a/libs/main/console/piconsolemodule.h b/libs/main/console/piconsolemodule.h index 766d2d64..57ea06d2 100644 --- a/libs/main/console/piconsolemodule.h +++ b/libs/main/console/piconsolemodule.h @@ -1,3 +1,12 @@ +//! \~\file piconsolemodule.h +//! \~\ingroup Console +//! \~\brief +//! \~english Console module headers +//! \~russian Заголовочные файлы модуля консоли +//! \~\details +//! \~english This file includes all Console module headers providing keyboard input, screen management, and tile-based UI. +//! \~russian Этот файл включает все заголовочные файлы модуля консоли, обеспечивающие ввод с клавиатуры, управление экраном и UI на основе +//! тайлов. /* PIP - Platform Independent Primitives Module includes @@ -18,8 +27,8 @@ */ //! \defgroup Console Console //! \~\brief -//! \~english Console graphic -//! \~russian Графика в консоли +//! \~english Console screen, input, and terminal utilities +//! \~russian Средства консольного экрана, ввода и терминала //! //! \~\details //! \~english \section cmake_module_Console Building with CMake @@ -34,10 +43,10 @@ //! \~russian \par Общее //! //! \~english -//! These files provides grab keyboard from console, simple tiling manager and virtual terminal. +//! These files provide keyboard capture from the console, a simple tile-based screen API and a virtual terminal. //! //! \~russian -//! Эти файлы обеспечивают захват клавиатуры в консоли, простой тайловый менеджер и виртуальный терминал. +//! Эти файлы предоставляют захват клавиатуры из консоли, простой экранный API на тайлах и виртуальный терминал. //! //! \~\authors //! \~english diff --git a/libs/main/console/pikbdlistener.h b/libs/main/console/pikbdlistener.h index 6ca61042..b79fb85e 100644 --- a/libs/main/console/pikbdlistener.h +++ b/libs/main/console/pikbdlistener.h @@ -1,9 +1,8 @@ -/*! \file pikbdlistener.h - * \ingroup Console - * \~\brief - * \~english Keyboard console input listener - * \~russian Консольный захват клавиатуры - */ +//! \~\file pikbdlistener.h +//! \~\ingroup Console +//! \~\brief +//! \~english Console keyboard and mouse input listener +//! \~russian Слушатель клавиатурного и мышиного консольного ввода /* PIP - Platform Independent Primitives Keyboard grabber for console @@ -29,6 +28,10 @@ #include "pithread.h" #include "pitime.h" +//! \relatesalso PIKbdListener +//! \~\brief +//! \~english Waits until the active listener captures the configured exit key and then stops it. +//! \~russian Ожидает, пока активный слушатель перехватит настроенную клавишу выхода, и затем останавливает его. #define WAIT_FOR_EXIT \ while (!PIKbdListener::exiting) \ piMSleep(PIP_MIN_MSLEEP * 5); \ @@ -37,87 +40,107 @@ } +//! \~\ingroup Console +//! \~\brief +//! \~english Console input listener for keyboard and mouse events. +//! \~russian Слушатель консольного ввода для событий клавиатуры и мыши. class PIP_EXPORT PIKbdListener: public PIThread { PIOBJECT_SUBCLASS(PIKbdListener, PIThread); friend class PIConsole; friend class PITerminal; public: - //! Special keyboard keys + //! \~english Keyboard keys reported as non-character codes. + //! \~russian Клавиши, передаваемые как несимвольные коды. enum SpecialKey { - Tab /** Tab key */ = 0x09, - Return /** Enter key */ = 0x0a, - Esc /** Escape key */ = 0x1b, - Space /** Space key */ = 0x20, - Backspace /** Backspace key */ = 0x7f, - UpArrow /** Up arrow key */ = -1, - DownArrow /** Down arrow key */ = -2, - RightArrow /** Right arrow key */ = -3, - LeftArrow /** Left arrow key */ = -4, - Home /** Home key */ = -5, - End /** End key */ = -6, - PageUp /** Page up key */ = -7, - PageDown /** Page down key */ = -8, - Insert /** Delete key */ = -9, - Delete /** Delete key */ = -10, - F1 /** F1 key */ = -11, - F2 /** F2 key */ = -12, - F3 /** F3 key */ = -13, - F4 /** F4 key */ = -14, - F5 /** F5 key */ = -15, - F6 /** F6 key */ = -16, - F7 /** F7 key */ = -17, - F8 /** F8 key */ = -18, - F9 /** F9 key */ = -19, - F10 /** F10 key */ = -20, - F11 /** F11 key */ = -21, - F12 /** F12 key */ = -22 + Tab = 0x09 /** \~english Tab key \~russian Клавиша Tab */, + Return = 0x0a /** \~english Enter key \~russian Клавиша Enter */, + Esc = 0x1b /** \~english Escape key \~russian Клавиша Escape */, + Space = 0x20 /** \~english Space key \~russian Клавиша пробела */, + Backspace = 0x7f /** \~english Backspace key \~russian Клавиша Backspace */, + UpArrow = -1 /** \~english Up arrow key \~russian Стрелка вверх */, + DownArrow = -2 /** \~english Down arrow key \~russian Стрелка вниз */, + RightArrow = -3 /** \~english Right arrow key \~russian Стрелка вправо */, + LeftArrow = -4 /** \~english Left arrow key \~russian Стрелка влево */, + Home = -5 /** \~english Home key \~russian Клавиша Home */, + End = -6 /** \~english End key \~russian Клавиша End */, + PageUp = -7 /** \~english Page Up key \~russian Клавиша Page Up */, + PageDown = -8 /** \~english Page Down key \~russian Клавиша Page Down */, + Insert = -9 /** \~english Insert key \~russian Клавиша Insert */, + Delete = -10 /** \~english Delete key \~russian Клавиша Delete */, + F1 = -11 /** \~english F1 key \~russian Клавиша F1 */, + F2 = -12 /** \~english F2 key \~russian Клавиша F2 */, + F3 = -13 /** \~english F3 key \~russian Клавиша F3 */, + F4 = -14 /** \~english F4 key \~russian Клавиша F4 */, + F5 = -15 /** \~english F5 key \~russian Клавиша F5 */, + F6 = -16 /** \~english F6 key \~russian Клавиша F6 */, + F7 = -17 /** \~english F7 key \~russian Клавиша F7 */, + F8 = -18 /** \~english F8 key \~russian Клавиша F8 */, + F9 = -19 /** \~english F9 key \~russian Клавиша F9 */, + F10 = -20 /** \~english F10 key \~russian Клавиша F10 */, + F11 = -21 /** \~english F11 key \~russian Клавиша F11 */, + F12 = -22 /** \~english F12 key \~russian Клавиша F12 */ }; - //! Keyboard modifiers + //! \~english Keyboard modifier bit flags. + //! \~russian Битовые флаги модификаторов клавиатуры. enum KeyModifier { - Ctrl /** Control key */ = 0x1, - Shift /** Shift key */ = 0x2, - Alt /** Alt key */ = 0x4 - // Meta /** Meta (windows) key */ = 0x8 + Ctrl = 0x1 /** \~english Control key \~russian Клавиша Control */, + Shift = 0x2 /** \~english Shift key \~russian Клавиша Shift */, + Alt = 0x4 /** \~english Alt key \~russian Клавиша Alt */, + // Meta = 0x8 /** Meta (windows) key */ }; + //! \~english Combination of \a KeyModifier flags. + //! \~russian Комбинация флагов \a KeyModifier. typedef PIFlags KeyModifiers; - //! This struct contains information about pressed keyboard key + //! \~english Information about one keyboard event. + //! \~russian Информация об одном событии клавиатуры. struct PIP_EXPORT KeyEvent { + //! \~english Constructs an empty event or initializes it with key and modifiers. + //! \~russian Создает пустое событие или инициализирует его клавишей и модификаторами. KeyEvent(int k = 0, KeyModifiers m = 0) { key = k; modifiers = m; } - //! Pressed key. It can be simple \b char or special key (see PIKbdListener::SpecialKey) + //! \~english Pressed key code. It can be a character code or one of \a SpecialKey values. + //! \~russian Код нажатой клавиши. Это может быть код символа или одно из значений \a SpecialKey. int key; - //! Active keyboard modifiers. It contains PIKbdListener::KeyModifier bitfields + //! \~english Active keyboard modifiers as a combination of \a KeyModifier flags. + //! \~russian Активные модификаторы клавиатуры как комбинация флагов \a KeyModifier. KeyModifiers modifiers; }; - //! Mouse buttons + //! \~english Mouse button bit flags. + //! \~russian Битовые флаги кнопок мыши. enum MouseButton { - MouseLeft /** Left button */ = 0x01, - MouseRight /** Right button */ = 0x02, - MouseMiddle /** Middle button */ = 0x04 + MouseLeft = 0x01 /** \~english Left button \~russian Левая кнопка */, + MouseRight = 0x02 /** \~english Right button \~russian Правая кнопка */, + MouseMiddle = 0x04 /** \~english Middle button \~russian Средняя кнопка */ }; - //! Mouse actions + //! \~english Mouse action kind. + //! \~russian Вид действия мыши. enum MouseAction { - MouseButtonPress /** Mouse button pressed */, - MouseButtonRelease /** Mouse button released */, - MouseButtonDblClick /** Mouse button double click */, - MouseMove /** Mouse moved */, - MouseWheel /** Mouse wheel rotated */ + MouseButtonPress /** \~english Mouse button pressed \~russian Нажатие кнопки мыши */, + MouseButtonRelease /** \~english Mouse button released \~russian Отпускание кнопки мыши */, + MouseButtonDblClick /** \~english Mouse button double-click \~russian Двойной щелчок кнопкой мыши */, + MouseMove /** \~english Mouse moved \~russian Перемещение мыши */, + MouseWheel /** \~english Mouse wheel rotated \~russian Прокрутка колеса мыши */ }; + //! \~english Combination of pressed \a MouseButton flags. + //! \~russian Комбинация нажатых флагов \a MouseButton. typedef PIFlags MouseButtons; - //! This struct contains information about mouse action + //! \~english Information about one mouse event. + //! \~russian Информация об одном событии мыши. struct PIP_EXPORT MouseEvent { + //! \~english Constructs an event with coordinates at the origin. + //! \~russian Создает событие с координатами в начале области. MouseEvent(MouseAction a = MouseButtonPress, MouseButtons b = 0, KeyModifiers m = 0) { x = y = 0; action = a; @@ -125,107 +148,156 @@ public: modifiers = m; } - //! Event X coordinate in view-space, from 0 + //! \~english Event X coordinate in screen space, starting from zero. + //! \~russian Координата X события в экранном пространстве, начиная с нуля. int x; - //! Event Y coordinate in view-space, from 0 + //! \~english Event Y coordinate in screen space, starting from zero. + //! \~russian Координата Y события в экранном пространстве, начиная с нуля. int y; - //! Mouse action type + //! \~english Mouse action kind. + //! \~russian Вид действия мыши. MouseAction action; - //! Pressed buttons. It contains PIKbdListener::MouseButton bitfields + //! \~english Pressed mouse buttons as a combination of \a MouseButton flags. + //! \~russian Нажатые кнопки мыши как комбинация флагов \a MouseButton. MouseButtons buttons; - //! Active keyboard modifiers. It contains PIKbdListener::KeyModifier bitfields + //! \~english Active keyboard modifiers as a combination of \a KeyModifier flags. + //! \~russian Активные модификаторы клавиатуры как комбинация флагов \a KeyModifier. KeyModifiers modifiers; }; - //! This struct contains information about mouse wheel action + //! \~english Information about one mouse wheel event. + //! \~russian Информация об одном событии колеса мыши. struct PIP_EXPORT WheelEvent: public MouseEvent { + //! \~english Constructs a wheel event with downward direction by default. + //! \~russian Создает событие колеса мыши; по умолчанию направление вниз. WheelEvent(): MouseEvent() { direction = false; } - //! Wheel direction, /b true - up, /b fasle - down + //! \~english Wheel direction: \b true for up, \b false for down. + //! \~russian Направление прокрутки: \b true вверх, \b false вниз. bool direction; }; + //! \~english Callback receiving a key event and user data. + //! \~russian Обратный вызов, принимающий событие клавиши и пользовательские данные. typedef std::function KBFunc; - //! Constructs keyboard listener with external function "slot" and custom data "data" + //! \~english Constructs a listener with optional callback, user data, and auto-start mode. + //! \~russian Создает слушатель с необязательным обратным вызовом, пользовательскими данными и автозапуском. explicit PIKbdListener(KBFunc slot = 0, void * data = 0, bool startNow = true); + //! \~english Stops the listener and restores the console state. + //! \~russian Останавливает слушатель и восстанавливает состояние консоли. ~PIKbdListener(); - //! Returns custom data + //! \~english Returns the user data passed back with callbacks and events. + //! \~russian Возвращает пользовательские данные, передаваемые обратно в обратные вызовы и события. void * data() { return kbddata_; } - //! Set custom data to "_data" + //! \~english Sets the user data passed back with callbacks and events. + //! \~russian Задает пользовательские данные, возвращаемые в обратных вызовах и событиях. void setData(void * _data) { kbddata_ = _data; } - //! Set external function to "slot" + //! \~english Sets the callback receiving both key event and user data. + //! \~russian Устанавливает обратный вызов, получающий событие клавиши и пользовательские данные. void setSlot(KBFunc slot) { ret_func = slot; } - //! Set external function to "slot" + //! \~english Sets the callback that only receives the key event and ignores user data. + //! \~russian Устанавливает обратный вызов, получающий только событие клавиши и игнорирующий пользовательские данные. void setSlot(std::function slot) { ret_func = [slot](KeyEvent e, void *) { slot(e); }; } - //! Returns if exit key if awaiting + //! \~english Returns whether the exit key is currently being captured. + //! \~russian Возвращает, включен ли сейчас перехват клавиши выхода. bool exitCaptured() const { return exit_enabled; } - //! Returns exit key, default 'Q' + //! \~english Returns the configured exit key. The default is \c 'Q'. + //! \~russian Возвращает настроенную клавишу выхода. По умолчанию это \c 'Q'. int exitKey() const { return exit_key; } + //! \~english Returns the double-click interval in milliseconds. + //! \~russian Возвращает интервал двойного щелчка в миллисекундах. double doubleClickInterval() const { return dbl_interval; } + + //! \~english Sets the mouse double-click interval in milliseconds. + //! \~russian Задает интервал двойного щелчка мыши в миллисекундах. void setDoubleClickInterval(double v) { dbl_interval = v; } + //! \~english Performs one low-level polling cycle and dispatches decoded input events. + //! \~russian Выполняет один цикл низкоуровневого опроса и отправляет декодированные события ввода. void readKeyboard(); + //! \~english Requests listener shutdown and interrupts a pending wait for console input. + //! \~russian Запрашивает остановку слушателя и прерывает текущее ожидание консольного ввода. void stop(); + //! \~english Requests shutdown and waits until console capture is restored or the timeout expires. + //! \~russian Запрашивает остановку и ждет восстановления режима консоли до истечения таймаута. bool stopAndWait(PISystemTime timeout = {}); - //! Returns if keyboard listening is active (not running!) + //! \~english Returns whether low-level console capture is currently enabled. + //! \~russian Возвращает, включен ли сейчас низкоуровневый захват консольного ввода. bool isActive() { return is_active; } - EVENT_HANDLER(void, enableExitCapture) { enableExitCapture('Q'); } - EVENT_HANDLER1(void, enableExitCapture, int, key) { - exit_enabled = true; - exit_key = key; - } - EVENT_HANDLER(void, disableExitCapture) { exit_enabled = false; } - EVENT_HANDLER(void, setActive) { setActive(true); } - EVENT_HANDLER1(void, setActive, bool, yes); - - EVENT2(keyPressed, PIKbdListener::KeyEvent, key, void *, data); - EVENT2(mouseEvent, PIKbdListener::MouseEvent, mouse, void *, data); - EVENT2(wheelEvent, PIKbdListener::WheelEvent, wheel, void *, data); //! \handlers //! \{ //! \fn void enableExitCapture(int key = 'Q') - //! \brief Enable exit key "key" awaiting + //! \~english Enables capture of exit key \a key. + //! \~russian Включает перехват клавиши выхода \a key. + EVENT_HANDLER(void, enableExitCapture) { enableExitCapture('Q'); } + EVENT_HANDLER1(void, enableExitCapture, int, key) { + exit_enabled = true; + exit_key = key; + } //! \fn void disableExitCapture() - //! \brief Disable exit key awaiting + //! \~english Disables exit key capture. + //! \~russian Выключает перехват клавиши выхода. + EVENT_HANDLER(void, disableExitCapture) { exit_enabled = false; } //! \fn void setActive(bool yes = true) - //! \brief Set keyboard listening is active or not + //! \~english Enables or disables low-level console input capture. + //! \~russian Включает или выключает низкоуровневый захват консольного ввода. + EVENT_HANDLER(void, setActive) { setActive(true); } + EVENT_HANDLER1(void, setActive, bool, yes); //! \} //! \events //! \{ //! \fn void keyPressed(PIKbdListener::KeyEvent key, void * data) - //! \brief Raise on key "key" pressed, "data" is custom data + //! \~english Raised when a key event is decoded. \a data is the user data pointer. + //! \~russian Вызывается, когда декодировано событие клавиши. \a data содержит указатель на пользовательские данные. + EVENT2(keyPressed, PIKbdListener::KeyEvent, key, void *, data); + + //! \fn void mouseEvent(PIKbdListener::MouseEvent mouse, void * data) + //! \~english Raised when a mouse button or move event is decoded. \a data is the user data pointer. + //! \~russian Вызывается, когда декодировано событие кнопки мыши или перемещения. \a data содержит указатель на пользовательские данные. + EVENT2(mouseEvent, PIKbdListener::MouseEvent, mouse, void *, data); + + //! \fn void wheelEvent(PIKbdListener::WheelEvent wheel, void * data) + //! \~english Raised when a mouse wheel event is decoded. \a data is the user data pointer. + //! \~russian Вызывается, когда декодировано событие колеса мыши. \a data содержит указатель на пользовательские данные. + EVENT2(wheelEvent, PIKbdListener::WheelEvent, wheel, void *, data); //! \} - static bool exiting; + + //! \~english Returns the listener instance currently registered by the console subsystem. + //! \~russian Возвращает экземпляр слушателя, который сейчас зарегистрирован консольной подсистемой. static PIKbdListener * instance() { return _object; } + //! \~english Becomes \b true after the configured exit key is captured. + //! \~russian Становится \b true после перехвата настроенной клавиши выхода. + static bool exiting; + private: void begin() override; void run() override { readKeyboard(); } @@ -268,16 +340,16 @@ private: //! \relatesalso PIBinaryStream -//! \~english Store operator -//! \~russian Оператор сохранения +//! \~english Store operator for MouseEvent. +//! \~russian Оператор сохранения для MouseEvent. BINARY_STREAM_WRITE(PIKbdListener::MouseEvent) { s << v.x << v.y << v.action << v.buttons << v.modifiers; return s; } //! \relatesalso PIBinaryStream -//! \~english Restore operator -//! \~russian Оператор извлечения +//! \~english Restore operator for MouseEvent. +//! \~russian Оператор извлечения для MouseEvent. BINARY_STREAM_READ(PIKbdListener::MouseEvent) { s >> v.x >> v.y >> v.action >> v.buttons >> v.modifiers; return s; @@ -285,16 +357,16 @@ BINARY_STREAM_READ(PIKbdListener::MouseEvent) { //! \relatesalso PIBinaryStream -//! \~english Store operator -//! \~russian Оператор сохранения +//! \~english Store operator for WheelEvent. +//! \~russian Оператор сохранения для WheelEvent. BINARY_STREAM_WRITE(PIKbdListener::WheelEvent) { s << (*(PIKbdListener::MouseEvent *)&v) << v.direction; return s; } //! \relatesalso PIBinaryStream -//! \~english Restore operator -//! \~russian Оператор извлечения +//! \~english Restore operator for WheelEvent. +//! \~russian Оператор извлечения для WheelEvent. BINARY_STREAM_READ(PIKbdListener::WheelEvent) { s >> (*(PIKbdListener::MouseEvent *)&v) >> v.direction; return s; diff --git a/libs/main/console/piscreen.h b/libs/main/console/piscreen.h index d9d50187..438f4034 100644 --- a/libs/main/console/piscreen.h +++ b/libs/main/console/piscreen.h @@ -1,9 +1,8 @@ -/*! \file piscreen.h - * \ingroup Console - * \~\brief - * \~english Console tiling manager - * \~russian Консольный тайловый менеджер - */ +//! \~\file piscreen.h +//! \~\ingroup Console +//! \~\brief +//! \~english Console screen manager and tile host +//! \~russian Менеджер консольного экрана и контейнер тайлов /* PIP - Platform Independent Primitives Console GUI @@ -31,6 +30,10 @@ #include "piscreentile.h" +//! \~\ingroup Console +//! \~\brief +//! \~english Console screen manager with tile layout, drawing, and input routing. +//! \~russian Менеджер консольного экрана с раскладкой тайлов, отрисовкой и маршрутизацией ввода. class PIP_CONSOLE_EXPORT PIScreen : public PIThread , public PIScreenTypes::PIScreenBase { @@ -38,69 +41,122 @@ class PIP_CONSOLE_EXPORT PIScreen class SystemConsole; public: - //! Constructs %PIScreen with key handler "slot" and if "startNow" start it + //! \~english Constructs a screen with an internal keyboard listener, optional callback, and auto-start mode. + //! \~russian Создает экран со встроенным слушателем клавиатуры, необязательным обратным вызовом и режимом автозапуска. + //! \~\details + //! \~english Constructs a new PIScreen instance with optional immediate start. + //! \~russian Создает новый экземпляр PIScreen с опциональным немедленным запуском. + //! \~\param startNow + //! \~english Start immediately if true. + //! \~russian Запустить немедленно, если true. + //! \~\param slot + //! \~english Keyboard handler function. + //! \~russian Функция обработчика клавиатуры. PIScreen(bool startNow = true, PIKbdListener::KBFunc slot = 0); + //! \~english Destroys PIScreen and cleans up resources. + //! \~russian Уничтожает PIScreen и очищает ресурсы. ~PIScreen(); - //! Directly call function from \a PIKbdListener + //! \~english Enables catching the exit key (default is 'Q') to stop the screen. + //! \~russian Включает захват клавиши выхода (по умолчанию 'Q') для остановки экрана. void enableExitCapture(int key = 'Q') { listener->enableExitCapture(key); } - //! Directly call function from \a PIKbdListener + //! \~english Disables catching the exit key. + //! \~russian Отключает захват клавиши выхода. void disableExitCapture() { listener->disableExitCapture(); } - //! Directly call function from \a PIKbdListener + //! \~english Returns whether exit key capture is enabled. + //! \~russian Возвращает, включен ли перехват клавиши выхода. bool exitCaptured() const { return listener->exitCaptured(); } - //! Directly call function from \a PIKbdListener + //! \~english Returns the configured exit key. + //! \~russian Возвращает настроенную клавишу выхода. int exitKey() const { return listener->exitKey(); } + //! \~english Returns the current console width in characters. + //! \~russian Возвращает текущую ширину консоли в символах. int windowWidth() const { return console.width; } + + //! \~english Returns the current console height in characters. + //! \~russian Возвращает текущую высоту консоли в символах. int windowHeight() const { return console.height; } + //! \~english Returns whether mouse hit-testing and routing are enabled. + //! \~russian Возвращает, включены ли проверка попадания и маршрутизация событий мыши. bool isMouseEnabled() const { return mouse_; } + + //! \~english Enables or disables mouse routing and tile hit-testing. + //! \~russian Включает или выключает маршрутизацию мыши и проверку попадания по тайлам. void setMouseEnabled(bool on); + //! \~english Returns the root tile covering the whole screen. + //! \~russian Возвращает корневой тайл, покрывающий весь экран. PIScreenTile * rootTile() { return &root; } + + //! \~english Searches the root tile subtree by object name. + //! \~russian Ищет тайл по имени объекта в поддереве корневого тайла. + //! \~\return + //! \~english Tile pointer if found, otherwise nullptr. + //! \~russian Указатель на тайл, если найден, иначе nullptr. PIScreenTile * tileByName(const PIString & name); + //! \~english Sets a dialog tile drawn above the root tree, centered on screen, and focused first. Pass \c nullptr to remove it. + //! \~russian Задает диалоговый тайл, рисуемый поверх корневого дерева, центрируемый на экране и первым получающий фокус. Передайте \c + //! nullptr, чтобы убрать его. void setDialogTile(PIScreenTile * t); + + //! \~english Returns the currently active dialog tile or \c nullptr. + //! \~russian Возвращает активный диалоговый тайл или \c nullptr. PIScreenTile * dialogTile() const { return tile_dialog; } + //! \~english Returns the drawer used to fill the off-screen cell buffer for the next frame. + //! \~russian Возвращает рисовальщик, используемый для заполнения внеэкранного буфера ячеек следующего кадра. PIScreenDrawer * drawer() { return &drawer_; } + + //! \~english Clears the off-screen cell buffer. The terminal is updated on the next draw cycle. + //! \~russian Очищает внеэкранный буфер ячеек. Терминал обновится на следующем цикле отрисовки. void clear() { drawer_.clear(); } + + //! \~english Resizes the internal console buffers used for subsequent frames. + //! \~russian Изменяет размер внутренних консольных буферов, используемых в следующих кадрах. void resize(int w, int h) { console.resize(w, h); } - EVENT_HANDLER0(void, waitForFinish); - EVENT_HANDLER0(void, start) { start(false); } - EVENT_HANDLER1(void, start, bool, wait); - EVENT_HANDLER0(void, stop) { stop(false); } - EVENT_HANDLER1(void, stop, bool, clear); - - EVENT2(keyPressed, PIKbdListener::KeyEvent, key, void *, data); - EVENT2(tileEvent, PIScreenTile *, tile, PIScreenTypes::TileEvent, e); //! \handlers //! \{ //! \fn void waitForFinish() - //! \brief block until finished (exit key will be pressed) + //! \~english Blocks until the captured exit key is pressed and then stops the screen. + //! \~russian Блокирует выполнение, пока не будет нажата перехватываемая клавиша выхода, затем останавливает экран. + EVENT_HANDLER0(void, waitForFinish); //! \fn void start(bool wait = false) - //! \brief Start console output and if "wait" block until finished (exit key will be pressed) + //! \~english Starts the screen thread and optionally waits until the configured exit key is captured. + //! \~russian Запускает поток экрана и при необходимости ждет, пока не будет перехвачена настроенная клавиша выхода. + EVENT_HANDLER0(void, start) { start(false); } + EVENT_HANDLER1(void, start, bool, wait); //! \fn void stop(bool clear = false) - //! \brief Stop console output and if "clear" clear the screen + //! \~english Stops the screen thread, restores console state, and optionally clears the terminal. + //! \~russian Останавливает поток экрана, восстанавливает состояние консоли и при необходимости очищает терминал. + EVENT_HANDLER0(void, stop) { stop(false); } + EVENT_HANDLER1(void, stop, bool, clear); //! \} //! \events //! \{ //! \fn void keyPressed(PIKbdListener::KeyEvent key, void * data) - //! \brief Raise on key "key" pressed, "data" is pointer to %PIConsole object + //! \~english Raised when a key was not consumed by focus navigation or the focused tile. \a data is the screen user data pointer. + //! \~russian Вызывается, когда клавиша не была поглощена навигацией фокуса или тайлом с фокусом. \a data содержит пользовательский + //! указатель экрана. + EVENT2(keyPressed, PIKbdListener::KeyEvent, key, void *, data); //! \fn void tileEvent(PIScreenTile * tile, PIScreenTypes::TileEvent e) - //! \brief Raise on some event "e" from tile "tile" + //! \~english Raised when a tile reports a custom event \a e. + //! \~russian Вызывается, когда тайл сообщает пользовательское событие \a e. + EVENT2(tileEvent, PIScreenTile *, tile, PIScreenTypes::TileEvent, e); //! \} diff --git a/libs/main/console/piscreenconsole.h b/libs/main/console/piscreenconsole.h deleted file mode 100644 index ad2f7d6c..00000000 --- a/libs/main/console/piscreenconsole.h +++ /dev/null @@ -1,82 +0,0 @@ -/*! \file piscreenconsole.h - * \ingroup Console - * \~\brief - * \~english Tile for PIScreen with PIConsole API - * \~russian Тайл для PIScreen с API PIConsole - */ -/* - PIP - Platform Independent Primitives - Tile for PIScreen with PIConsole API - Andrey Bychkov work.a.b@yandex.ru - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . -*/ - -#ifndef PISCREENCONSOLE_H -#define PISCREENCONSOLE_H - -#include "pip_console_export.h" -#include "piscreentiles.h" - -/// NOTE: incomplete class -/// TODO: write TileVars - -class PIP_CONSOLE_EXPORT TileVars: public PIScreenTile { -public: - TileVars(const PIString & n = PIString()); - -protected: - struct PIP_CONSOLE_EXPORT Variable { - Variable() { - nx = ny = type = offset = bitFrom = bitCount = size = 0; - format = PIScreenTypes::CellFormat(); - ptr = 0; - } - bool isEmpty() const { return (ptr == 0); } - PIString name; - PIScreenTypes::CellFormat format; - int nx; - int ny; - int type; - int offset; - int bitFrom; - int bitCount; - int size; - const void * ptr; - /*void operator =(const Variable & src) { - name = src.name; - format = src.format; - nx = src.nx; - ny = src.ny; - type = src.type; - offset = src.offset; - bitFrom = src.bitFrom; - bitCount = src.bitCount; - size = src.size; - ptr = src.ptr; - }*/ - }; - PIVector variables; - PIScreenTypes::Alignment alignment; - void sizeHint(int & w, int & h) const override; - void drawEvent(PIScreenDrawer * d) override; -}; - - -class PIP_CONSOLE_EXPORT PIScreenConsoleTile: public PIScreenTile { -public: - PIScreenConsoleTile(); -}; - -#endif // PISCREENCONSOLE_H diff --git a/libs/main/console/piscreendrawer.h b/libs/main/console/piscreendrawer.h index 81b7c91a..c5c14c44 100644 --- a/libs/main/console/piscreendrawer.h +++ b/libs/main/console/piscreendrawer.h @@ -1,9 +1,8 @@ -/*! \file piscreendrawer.h - * \ingroup Console - * \~\brief - * \~english Drawer for PIScreen - * \~russian Отрисовщик для PIScreen - */ +//! \~\file piscreendrawer.h +//! \~\ingroup Console +//! \~\brief +//! \~english Drawing helpers for %PIScreen cell buffers +//! \~russian Вспомогательные средства рисования для буферов ячеек %PIScreen /* PIP - Platform Independent Primitives Drawer for PIScreen @@ -30,31 +29,54 @@ #include "piscreentypes.h" #include "pistring.h" +//! \~\ingroup Console +//! \~\brief +//! \~english Helper that draws primitives and text into a %PIScreen cell buffer. +//! \~russian Вспомогательный класс для рисования примитивов и текста в буфере ячеек %PIScreen. +//! \~\details +//! \~english Provides methods for drawing primitives such as pixels, lines, rectangles, and text on console screen. +//! \~russian Предоставляет методы для рисования примитивов, таких как пиксели, линии, прямоугольники и текст на консольном экране. class PIP_CONSOLE_EXPORT PIScreenDrawer { friend class PIScreen; PIScreenDrawer(PIVector> & c); public: + //! \~english Predefined pseudographic and widget-state symbols. + //! \~russian Предопределенные псевдографические символы и символы состояний виджетов. + //! \~\details + //! \~english Defines available characters for drawing ASCII art primitives. + //! \~russian Определяет доступные символы для рисования ASCII-арта примитивов. enum ArtChar { - LineVertical = 1, - LineHorizontal, - Cross, - CornerTopLeft, - CornerTopRight, - CornerBottomLeft, - CornerBottomRight, - Unchecked, - Checked + LineVertical = 1 /** \~english Vertical line symbol. \~russian Символ вертикальной линии. */, + LineHorizontal /** \~english Horizontal line symbol. \~russian Символ горизонтальной линии. */, + Cross /** \~english Line intersection symbol. \~russian Символ пересечения линий. */, + CornerTopLeft /** \~english Top-left frame corner. \~russian Левый верхний угол рамки. */, + CornerTopRight /** \~english Top-right frame corner. \~russian Правый верхний угол рамки. */, + CornerBottomLeft /** \~english Bottom-left frame corner. \~russian Левый нижний угол рамки. */, + CornerBottomRight /** \~english Bottom-right frame corner. \~russian Правый нижний угол рамки. */, + Unchecked /** \~english Unchecked box symbol. \~russian Символ неотмеченного флажка. */, + Checked /** \~english Checked box symbol. \~russian Символ отмеченного флажка. */ }; + //! \~english Clears the whole target buffer. + //! \~russian Очищает весь целевой буфер. void clear(); + + //! \~english Clears a rectangular area in the target buffer with spaces. + //! \~russian Очищает прямоугольную область целевого буфера пробелами. void clearRect(int x0, int y0, int x1, int y1) { fillRect(x0, y0, x1, y1, ' '); } + + //! \~english Draws one cell at position `(x, y)`. + //! \~russian Рисует одну ячейку в позиции `(x, y)`. void drawPixel(int x, int y, const PIChar & c, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0); + + //! \~english Draws a line between two points. + //! \~russian Рисует линию между двумя точками. void drawLine(int x0, int y0, int x1, @@ -63,6 +85,9 @@ public: PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0); + + //! \~english Draws a rectangular outline with the specified symbol. + //! \~russian Рисует контур прямоугольника указанным символом. void drawRect(int x0, int y0, int x1, @@ -71,6 +96,9 @@ public: PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0); + + //! \~english Draws a frame using predefined art symbols. + //! \~russian Рисует рамку предопределенными псевдографическими символами. void drawFrame(int x0, int y0, int x1, @@ -78,12 +106,18 @@ public: PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0); + + //! \~english Draws text starting at `(x, y)`. + //! \~russian Рисует текст, начиная с позиции `(x, y)`. void drawText(int x, int y, const PIString & s, PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Transparent, PIScreenTypes::CharFlags flags_char = 0); + + //! \~english Fills a rectangular area with one symbol and cell format. + //! \~russian Заполняет прямоугольную область одним символом и форматом ячейки. void fillRect(int x0, int y0, int x1, @@ -92,10 +126,17 @@ public: PIScreenTypes::Color col_char = PIScreenTypes::Default, PIScreenTypes::Color col_back = PIScreenTypes::Default, PIScreenTypes::CharFlags flags_char = 0); + + //! \~english Copies a cell matrix into a rectangular area. + //! \~russian Копирует матрицу ячеек в прямоугольную область. void fillRect(int x0, int y0, int x1, int y1, PIVector> & content); + //! \~english Returns a predefined art symbol. + //! \~russian Возвращает предопределенный псевдографический символ. PIChar artChar(const ArtChar type) const { return arts_.value(type, PIChar(' ')); } + //! \~english Fills an arbitrary cell buffer with default cells. + //! \~russian Заполняет произвольный буфер ячеек значениями по умолчанию. static void clear(PIVector> & cells); private: diff --git a/libs/main/console/piscreentile.h b/libs/main/console/piscreentile.h index be420e18..c34427b5 100644 --- a/libs/main/console/piscreentile.h +++ b/libs/main/console/piscreentile.h @@ -1,9 +1,8 @@ -/*! \file piscreentile.h - * \ingroup Console - * \~\brief - * \~english Basic PIScreen tile - * \~russian Базовый тайл для PIScreen - */ +//! \~\file piscreentile.h +//! \~\ingroup Console +//! \brief +//! \~english Basic PIScreen tile +//! \~russian Базовый тайл для PIScreen /* PIP - Platform Independent Primitives Basic PIScreen tile @@ -32,27 +31,74 @@ class PIScreenDrawer; +//! \~\ingroup Console +//! \~\brief +//! \~english Base tile in the console screen tree. +//! \~russian Базовый тайл в дереве консольного экрана. +//! \details +//! \~english Base class for all screen tiles providing layout and event handling. +//! \~russian Базовый класс для всех экранных тайлов, обеспечивающий компоновку и обработку событий. class PIP_CONSOLE_EXPORT PIScreenTile: public PIObject { friend class PIScreen; PIOBJECT_SUBCLASS(PIScreenTile, PIObject); public: + //! \~english Constructs a tile with name, child layout direction, and size policy. + //! \~russian Создает тайл с именем, направлением раскладки дочерних элементов и политикой размера. PIScreenTile(const PIString & n = PIString(), PIScreenTypes::Direction d = PIScreenTypes::Vertical, PIScreenTypes::SizePolicy p = PIScreenTypes::Preferred); + + //! \~english Destroys the tile and its owned child tiles. + //! \~russian Уничтожает тайл и принадлежащие ему дочерние тайлы. virtual ~PIScreenTile(); + //! \~english Adds child tile \a t, makes this tile its parent, and attaches the subtree to the same screen bridge. + //! \~russian Добавляет дочерний тайл \a t, делает этот тайл его родителем и подключает поддерево к тому же экранному мосту. void addTile(PIScreenTile * t); + + //! \~english Detaches child tile \a t without deleting it and removes its screen association. + //! \~russian Отсоединяет дочерний тайл \a t без удаления и снимает его связь с экраном. void takeTile(PIScreenTile * t); + + //! \~english Removes and deletes child tile \a t. + //! \~russian Удаляет дочерний тайл \a t и уничтожает его. void removeTile(PIScreenTile * t); + + //! \~english Returns the parent tile or \c nullptr for the root. + //! \~russian Возвращает родительский тайл или \c nullptr для корня. PIScreenTile * parentTile() const { return parent; } + + //! \~english Returns all descendant tiles. Hidden tiles can be skipped with \a only_visible. + //! \~russian Возвращает все дочерние тайлы по дереву. Скрытые тайлы можно пропустить через \a only_visible. PIVector children(bool only_visible = false); + + //! \~english Returns the first visible direct child covering screen point \a x, \a y. + //! \~russian Возвращает первый видимый прямой дочерний тайл, покрывающий экранную точку \a x, \a y. PIScreenTile * childUnderMouse(int x, int y); + + //! \~english Makes the tile visible for subsequent layout, hit-testing, and drawing passes. + //! \~russian Делает тайл видимым для последующих проходов компоновки, проверки попадания и отрисовки. void show() { visible = true; } + + //! \~english Hides the tile from layout, hit-testing, and drawing passes. + //! \~russian Скрывает тайл из проходов компоновки, проверки попадания и отрисовки. void hide() { visible = false; } + + //! \~english Requests focus for this tile if it is attached to a screen and allows focus. + //! \~russian Запрашивает фокус для этого тайла, если он подключен к экрану и допускает получение фокуса. void setFocus(); + + //! \~english Returns whether this tile currently owns focus. + //! \~russian Возвращает, принадлежит ли этому тайлу текущий фокус. bool hasFocus() const { return has_focus; } + + //! \~english Sets all margins to \a m cells. + //! \~russian Устанавливает все отступы в \a m ячеек. void setMargins(int m) { marginLeft = marginRight = marginTop = marginBottom = m; } + + //! \~english Sets left, right, top, and bottom margins in cells. + //! \~russian Устанавливает левый, правый, верхний и нижний отступы в ячейках. void setMargins(int l, int r, int t, int b) { marginLeft = l; marginRight = r; @@ -60,52 +106,129 @@ public: marginBottom = b; } + //! \~english Returns the tile X coordinate in screen space. + //! \~russian Возвращает координату X тайла в экранном пространстве. int x() const { return x_; } + + //! \~english Returns the tile Y coordinate in screen space. + //! \~russian Возвращает координату Y тайла в экранном пространстве. int y() const { return y_; } + + //! \~english Returns the current tile width in cells. + //! \~russian Возвращает текущую ширину тайла в ячейках. int width() const { return width_; } + + //! \~english Returns the current tile height in cells. + //! \~russian Возвращает текущую высоту тайла в ячейках. int height() const { return height_; } + //! \~english Direction used to lay out child tiles. + //! \~russian Направление раскладки дочерних тайлов. PIScreenTypes::Direction direction; + + //! \~english Size policy used by the parent during layout. + //! \~russian Политика размера, используемая родителем при компоновке. PIScreenTypes::SizePolicy size_policy; + + //! \~english Focus and navigation flags for the tile. + //! \~russian Флаги фокуса и навигации для тайла. PIScreenTypes::FocusFlags focus_flags; + + //! \~english Background format used to prefill the tile area before drawing. + //! \~russian Формат фона, которым предварительно заполняется область тайла перед отрисовкой. PIScreenTypes::CellFormat back_format; + + //! \~english Background symbol used to prefill the tile area before drawing. + //! \~russian Символ фона, которым предварительно заполняется область тайла перед отрисовкой. PIChar back_symbol; + + //! \~english Minimum size limits accepted during layout. + //! \~russian Минимальные ограничения размера, допускаемые при компоновке. int minimumWidth, minimumHeight; + + //! \~english Maximum size limits accepted during layout. + //! \~russian Максимальные ограничения размера, допускаемые при компоновке. int maximumWidth, maximumHeight; + + //! \~english Outer margins in cells. + //! \~russian Внешние отступы в ячейках. int marginLeft, marginRight, marginTop, marginBottom; + + //! \~english Spacing between visible child tiles in cells. + //! \~russian Интервал между видимыми дочерними тайлами в ячейках. int spacing; + + //! \~english Whether the tile participates in layout, hit-testing, and drawing. + //! \~russian Участвует ли тайл в компоновке, проверке попадания и отрисовке. bool visible; protected: - //! Returns desired tile size in "w" and "h" + //! \~english Returns the preferred tile size in \a w and \a h. The base implementation derives it from visible children, spacing, and margins. + //! \~russian Возвращает предпочтительный размер тайла в \a w и \a h. Базовая реализация вычисляет его по видимым дочерним тайлам, интервалам и отступам. virtual void sizeHint(int & w, int & h) const; - //! Tile has been resized to "w"x"h" + //! \~english Called after the tile size changes to \a w by \a h during layout. + //! \~russian Вызывается после изменения размера тайла до \a w на \a h во время компоновки. virtual void resizeEvent(int w, int h) {} - //! Draw tile with drawer "d" in world-space coordinates + //! \~english Draws the tile with drawer \a d in screen coordinates. + //! \~russian Отрисовывает тайл через рисовальщик \a d в экранных координатах. virtual void drawEvent(PIScreenDrawer * d) {} - //! Return "true" if you process key + //! \~english Handles keyboard input and returns \b true when the event is consumed. + //! \~russian Обрабатывает клавиатурный ввод и возвращает \b true, если событие поглощено. virtual bool keyEvent(PIKbdListener::KeyEvent key) { return false; } - //! Return "true" if you process event + //! \~english Handles mouse input and returns \b true when the event is consumed. + //! \~russian Обрабатывает событие мыши и возвращает \b true, если событие поглощено. virtual bool mouseEvent(PIKbdListener::MouseEvent me) { return false; } - //! Return "true" if you process wheel + //! \~english Handles mouse wheel input and returns \b true when the event is consumed. + //! \~russian Обрабатывает колесо мыши и возвращает \b true, если событие поглощено. virtual bool wheelEvent(PIKbdListener::WheelEvent we) { return false; } + //! \~english Raises tile event + //! \~russian Вызывает событие тайла void raiseEvent(PIScreenTypes::TileEvent e); + + //! \~english Sets screen reference + //! \~russian Устанавливает ссылку на экран void setScreen(PIScreenTypes::PIScreenBase * s); + + //! \~english Deletes all owned child tiles. + //! \~russian Удаляет все принадлежащие дочерние тайлы. void deleteChildren(); + + //! \~english Draws background, tile contents, and then child tiles. + //! \~russian Отрисовывает фон, содержимое тайла и затем дочерние тайлы. void drawEventInternal(PIScreenDrawer * d); + + //! \~english Recomputes child geometry according to size hints, margins, and policies. + //! \~russian Пересчитывает геометрию дочерних тайлов по предпочтительным размерам, отступам и политикам. void layout(); + + //! \~english Returns whether this tile should participate in automatic layout. Tiles with policy \a PIScreenTypes::Ignore are skipped. + //! \~russian Возвращает, должен ли тайл участвовать в автоматической компоновке. Тайлы с политикой \a PIScreenTypes::Ignore пропускаются. bool needLayout() { return size_policy != PIScreenTypes::Ignore; } + //! \~english Owned direct child tiles. + //! \~russian Принадлежащие прямые дочерние тайлы. PIVector tiles; + + //! \~english Parent tile or \c nullptr for the root or detached tiles. + //! \~russian Родительский тайл или \c nullptr для корня и отсоединенных тайлов. PIScreenTile * parent; + + //! \~english Screen pointer, receiving tile notifications. + //! \~russian Ссылка на экран, принимающий уведомления от тайла. PIScreenTypes::PIScreenBase * screen; + + //! \~english Tile position and size in screen cells. + //! \~russian Положение и размер тайла в экранных ячейках. int x_, y_, width_, height_; + + //! \~english Whether this tile currently owns focus. + //! \~russian Принадлежит ли этому тайлу текущий фокус. bool has_focus; private: diff --git a/libs/main/console/piscreentiles.h b/libs/main/console/piscreentiles.h index 51dbde43..2bc31f8d 100644 --- a/libs/main/console/piscreentiles.h +++ b/libs/main/console/piscreentiles.h @@ -1,9 +1,11 @@ -/*! \file piscreentiles.h - * \ingroup Console - * \~\brief - * \~english Various tiles for PIScreen - * \~russian Различные тайлы для PIScreen - */ +//! \~\file piscreentiles.h +//! \~\ingroup Console +//! \brief +//! \~english Various tiles for PIScreen +//! \~russian Различные тайлы для PIScreen +//! \details +//! \~english Provides ready-to-use tile implementations for common UI elements. +//! \~russian Обеспечивает готовые к использованию реализации тайлов для общих элементов UI. /* PIP - Platform Independent Primitives Various tiles for PIScreen @@ -30,15 +32,36 @@ #include "piscreentile.h" +//! \~\ingroup Console +//! \~\brief +//! \~english Simple text tile with per-row formatting. +//! \~russian Простой текстовый тайл с форматированием по строкам. class PIP_CONSOLE_EXPORT TileSimple: public PIScreenTile { PIOBJECT_SUBCLASS(TileSimple, PIScreenTile); public: + //! \~english Row text with cell format. + //! \~russian Текст строки с форматом ячеек. typedef PIPair Row; + + //! \~english Constructs an empty text tile. + //! \~russian Создает пустой текстовый тайл. TileSimple(const PIString & n = PIString()); + + //! \~english Constructs a text tile with one row. + //! \~russian Создает текстовый тайл с одной строкой. TileSimple(const Row & r); + + //! \~english Destroys the text tile. + //! \~russian Уничтожает текстовый тайл. virtual ~TileSimple() {} + + //! \~english Rows displayed by the tile. + //! \~russian Строки, отображаемые тайлом. PIVector content; + + //! \~english Horizontal text alignment inside the tile. + //! \~russian Горизонтальное выравнивание текста внутри тайла. PIScreenTypes::Alignment alignment; protected: @@ -49,19 +72,49 @@ protected: class TileList; +//! \~\ingroup Console +//! \~\brief +//! \~english Scroll bar tile used by list-like widgets. +//! \~russian Тайловая полоса прокрутки для списковых виджетов. class PIP_CONSOLE_EXPORT TileScrollBar: public PIScreenTile { PIOBJECT_SUBCLASS(TileScrollBar, PIScreenTile); friend class TileList; public: + //! \~english Constructs a scroll bar tile. + //! \~russian Создает тайл полосы прокрутки. TileScrollBar(const PIString & n = PIString()); + + //! \~english Destroys the scroll bar tile. + //! \~russian Уничтожает тайл полосы прокрутки. virtual ~TileScrollBar() {} + + //! \~english Sets the minimum scroll value. + //! \~russian Устанавливает минимальное значение прокрутки. void setMinimum(int v); + + //! \~english Sets the maximum scroll value. + //! \~russian Устанавливает максимальное значение прокрутки. void setMaximum(int v); + + //! \~english Sets the current scroll value. + //! \~russian Устанавливает текущее значение прокрутки. void setValue(int v); + + //! \~english Returns the minimum scroll value. + //! \~russian Возвращает минимальное значение прокрутки. int minimum() const { return minimum_; } + + //! \~english Returns the maximum scroll value. + //! \~russian Возвращает максимальное значение прокрутки. int maximum() const { return maximum_; } + + //! \~english Returns the current scroll value. + //! \~russian Возвращает текущее значение прокрутки. int value() const { return value_; } + + //! \~english Thickness of the drawn bar in cells, perpendicular to the scroll direction. + //! \~russian Толщина отрисовываемой полосы в ячейках поперек направления прокрутки. int thickness; protected: @@ -74,29 +127,68 @@ protected: }; +//! \~\ingroup Console +//! \~\brief +//! \~english Scrollable list tile with optional row selection. +//! \~russian Прокручиваемый тайл списка с необязательным выбором строк. class PIP_CONSOLE_EXPORT TileList: public PIScreenTile { PIOBJECT_SUBCLASS(TileList, PIScreenTile); public: + //! \~english Selection policy for list rows. + //! \~russian Режим выбора строк списка. enum SelectionMode { - NoSelection, - SingleSelection, - MultiSelection - }; - enum EventType { - SelectionChanged, - RowPressed + NoSelection /** \~english Rows are not selectable. \~russian Выбор строк отключен. */, + SingleSelection /** \~english At most one row can be selected. \~russian Можно выбрать не более одной строки. */, + MultiSelection /** \~english Multiple rows can be selected. \~russian Можно выбрать несколько строк. */ }; + //! \~english Events emitted by the list tile. + //! \~russian События, генерируемые тайлом списка. + enum EventType { + SelectionChanged /** \~english Selection set changed. \~russian Изменился набор выбранных строк. */, + RowPressed /** \~english Current row was activated; event data stores the row index. \~russian Текущая строка была активирована; данные события содержат индекс строки. */ + }; + + //! \~english Constructs a list tile with the specified selection mode. + //! \~russian Создает тайл списка с указанным режимом выбора. TileList(const PIString & n = PIString(), SelectionMode sm = NoSelection); + + //! \~english Destroys the list tile. + //! \~russian Уничтожает тайл списка. virtual ~TileList() {} + //! \~english Row text with cell format. + //! \~russian Текст строки с форматом ячеек. typedef PIPair Row; + + //! \~english Rows displayed by the list. + //! \~russian Строки, отображаемые списком. PIDeque content; + + //! \~english Alignment used to draw row text. + //! \~russian Выравнивание, используемое при рисовании текста строк. PIScreenTypes::Alignment alignment; + + //! \~english Active row selection mode. + //! \~russian Текущий режим выбора строк. SelectionMode selection_mode; + + //! \~english Indexes of selected rows. + //! \~russian Индексы выбранных строк. PISet selected; - int lhei, cur, offset; + + //! \~english Cached count of visible content rows between the top and bottom scroll markers. + //! \~russian Кэшированное количество видимых строк содержимого между верхней и нижней метками прокрутки. + int lhei; + + //! \~english Index of the current row used for focus and activation. + //! \~russian Индекс текущей строки, используемой для фокуса и активации. + int cur; + + //! \~english Index of the first row currently visible in the viewport. + //! \~russian Индекс первой строки, видимой в текущей области просмотра. + int offset; protected: void sizeHint(int & w, int & h) const override; @@ -110,16 +202,34 @@ protected: }; +//! \~\ingroup Console +//! \~\brief +//! \~english Push button tile. +//! \~russian Тайл кнопки. class PIP_CONSOLE_EXPORT TileButton: public PIScreenTile { PIOBJECT_SUBCLASS(TileButton, PIScreenTile); public: + //! \~english Constructs a button tile. + //! \~russian Создает тайл кнопки. TileButton(const PIString & n = PIString()); + + //! \~english Destroys the button tile. + //! \~russian Уничтожает тайл кнопки. virtual ~TileButton() {} + + //! \~english Events emitted by the button. + //! \~russian События, генерируемые кнопкой. enum EventType { - ButtonClicked + ButtonClicked /** \~english Button was activated. \~russian Кнопка была активирована. */ }; + + //! \~english Text format of the button label. + //! \~russian Формат текста надписи кнопки. PIScreenTypes::CellFormat format; + + //! \~english Button caption. + //! \~russian Подпись кнопки. PIString text; protected: @@ -130,18 +240,42 @@ protected: }; +//! \~\ingroup Console +//! \~\brief +//! \~english Group of selectable buttons arranged in one tile. +//! \~russian Группа выбираемых кнопок, размещенных в одном тайле. class PIP_CONSOLE_EXPORT TileButtons: public PIScreenTile { PIOBJECT_SUBCLASS(TileButtons, PIScreenTile); public: + //! \~english Constructs a button group tile. + //! \~russian Создает тайл группы кнопок. TileButtons(const PIString & n = PIString()); + + //! \~english Destroys the button group tile. + //! \~russian Уничтожает тайл группы кнопок. virtual ~TileButtons() {} + + //! \~english Events emitted by the button group. + //! \~russian События, генерируемые группой кнопок. enum EventType { - ButtonSelected + ButtonSelected /** \~english A button was selected; event data stores the button index. \~russian Кнопка была выбрана; данные события содержат индекс кнопки. */ }; + + //! \~english Button caption with cell format. + //! \~russian Подпись кнопки с форматом ячеек. typedef PIPair Button; + + //! \~english Alignment of the whole button group inside the tile bounds. + //! \~russian Выравнивание всей группы кнопок внутри границ тайла. PIScreenTypes::Alignment alignment; + + //! \~english Button definitions shown by the tile. + //! \~russian Описания кнопок, отображаемых тайлом. PIVector