- Журнал изменений в версиях системы Papyrus
- PAPYRUS (Демо)
- Papyrus: development
- ppd-chapter-000
- Введение #2
- Инструментарий
- Сборка проекта
- Code style
- Общие функции
- Базовые типы данных
- Базовые структуры данных
- Базовые алгоритмы
- Управление строковыми ресурсами
- Контроллеры анализа данных (классы семейства PPView)
- Соображения по вопросу неотрицательности товарных остатков
- Котировки
- Модель объемной оптимизации товарных запасов
- Проект SARTR
- Протокол взаимодействия
- Зарезервированные объекты
- Регламентированные задания
- Papyrus: возможности системы
- Papyrus: руководство пользователя
- Библиотека
- Все об обязательной маркировке 2021
- Все об онлайн-кассах
- Презентации
- Руководства
- Свидетельство о регистрации системы Papyrus
- Технологии и функции
Общие функции
В этой главе обсуждаются общеупотребимые функции и методы, а именно:
Распределение памяти
Функции malloc, calloc, realloc, free, определенные в стандартной библиотеке не используются в проекте на прямую. Вместо них применяются статические функции класса SAlloc. Ниже приведено упрощенное определение этого класса.
class SAlloc { public: // // Descr: Функция, замещающая malloc. // static void * FASTCALL M(size_t sz); // // Descr: Функция, замещающая calloc. // static void * FASTCALL C(size_t n, size_t sz); // // Descr: Функция, замещающая realloc. // static void * FASTCALL R(void * ptr, size_t sz); // // Descr: Функция, замещающая free. // static void FASTCALL F(void * ptr); };Существуют две причины, по которым реализовано такое замещение:
- Благодаря соглашению вызова FASTCALL код, использующий эти функции (а это очень значительное количество вызовов) становится компактнее. Отметим, что выигрыша в скорости вызова нет (чуть-чуть медленнее за счет избыточной вложенности).
- Появляется возможность (увы, пока не осуществленная) централизованно собирать статистику об обращениях к аллокатору памяти с целью реализации высокооптимизированного аллокатора.
Проверка на null освобождаемой памяти
Специальное замечание относительно освобождения памяти функциями free(), SAlloc::F() и оператором delete: стандарты c/c++ гарантируют, что эти функции игнорируют нулевые указатели. Поэтому не следует проверять на null указатели перед вызовом этих и родственных функций - не надо загромождать и без того гигантский код.Единственное исключение из вышеприведенного правила: если вы абсолютно уверены, что участок кода, где осуществляется освобождение распределенной памяти, критичен по быстродействию (очень часто вызывается, например, в циклах и т.д.) то проверка на null перед вызовом функции (оператора) освобождения памяти поможет сэкономить “пару тактов” процессора. Пометьте такую проверку комментарием // @speedcritical чтобы как-то оправдаться.
Манипуляции с участками памяти
Функции memcpy, memmove, memset, memcmp опеределены в slib.h как макросы, для использования реализации из библиотеки Agner Fog (www.agner.org).Для инициализации памяти нулевыми байтами вместо общеупотребимого memset(ptr, 0, size) используется собственная функция memzero(ptr, size). Мотивацией для этого является удаление лишнего параметра в огромном числе вызовов, что в купе с соглашением вызова FASTCALL дает заметное снижение размера кода. Кроме того, функция memzero отдельно обрабатывает некоторые размеры инициализируемых участков памяти для ускорения.Для обнуления участков памяти, размер которых может быть вычислен на этапе компиляции оператором sizeof используется макрос MEMSZERO(). Пример:
void foo(void * ptr, size_t size) { char local_buf[256]; MEMSZERO(local_buf); // Правильно: sizeof(local_buf) возвращает адекватный результат // MEMSZERO(ptr); // ! Ни в коем случае - будут обнулены // только первые 4 байта (8 на x64) memzero(ptr, size); // Правильно }
Еще один макрос THISZERO() используется для полного обнуления экземпляра структуры или класса. Часто используется в конструкторах.Осторожно: этот макрос ни в коем случае не следует использовать для классов, имеющих виртуальные функции - он обнулит указатель на таблицу виртуальных функций в результате чего программа обязательно аварийно завершится при попытке обращения к любой из виртуальных функций класса.
class SomeClass { public: SomeClass() { THISZERO(); // Здесь можно использовать. // Класс не определяет ни одной виртуальной функции. } int A; int B; }; class SomeClassWithVirtualFunctions { public: SomeClass() { THISZERO(); // ! Ни за что не делайте так - виртуальный // деструктор или иные виртуальные функции заставляют // компилятор вставить первым членом в экземпляре класса // указатель на таблицу виртуальных функций. // В результате обнуления этот указатель станет нулевым. } virtual SomeClass() { } int A; int B; };
Функция smemchr
const void * smemchr(const void * pHaystack, int n, size_t len);Функция smemchr следует использовать вместо memchr. Единственным обоснованием этого является то, что smemchr значительно быстрее нежели memchr.
Тестирование и бенчмаркинг функции реализованы в тестовом кейсе SLTEST_R(memchr).
Функции для работы со строками
В этой секции мы обсудим ряд часто используемых функций для манипуляции с z-строками (то есть, классическими строками, состоящими из цепочки символов, завершающихся нулевым байтом).
Обнуление строки
Как правило для обрезания z-строки до нулевой длины применяют присваивание первому байту нуля.
char str[64]; str[0] = 0;
Определение длины строки
Выяснение не является ли строка пустой
При определении не является ли указатель на строку, переданный в качестве аргумента функции, пустой строкой следует использовать функцию isempty() вместо простого сравнения первого байта с нулем. Это намного безопаснее из-за того, что ликвидирует проблему потенциального обращения к нулевому указателю, который может быть использован как параметр при вызове функции.
isempty(const char * pStr);Например:
void foo(const char * pStr, int a) { if(!isempty(pStr)) { // do something with pStr; } }
sstrlen и sstrleni
Стандартная функция strlen почти идеальна в плане быстродействия, но имеет небольшие недостатки:
Эта функция определена для аргументов типа (const char *), (const uchar *) и (const wchar_t *).Дополнительно в slib.h определены аналогичные 3 функции с именем strleni, возвращающие int, а не size_t. Это сделано из-за того что во многих случаях требуется получить именно знаковое значение (это - просто трюк, реализованный для того, чтоб обойтись без лишних преобразований типов).
Копирование строк
sstrcpy
Несмотря на то, что очевидным решением для копирования одной z-строки в другую является стандартная функция strcpy, мы чаще всего применяем собственную реализации sstrcpy().Основная причина для такой подмены - небезопасность strcpy: эта функция не проверяет аргументы на 0 (в общем случае). Кроме того, в библиотеке SLIB присутствуют 3 переопределенных функции для параметров типа char *, uchar *, wchar *.Вместе с вышесказанным следует отметить, что sstrcpy сильно уступает стандартной strcpy по быстродействию из-за того, что компилятор превосходно оптимизирует вызовы strcpy.
strnzcpy
char * FASTCALL strnzcpy(char * pDest, const char * pSrc, size_t maxlen); char * FASTCALL strnzcpy(char * pDest, const uchar * pSrc, size_t maxlen); char * FASTCALL strnzcpy(char * pDest, const SString & rSrc, size_t maxlen); wchar_t * FASTCALL strnzcpy(wchar_t * pDest, const wchar_t * pSrc, size_t maxlen);
Копирование строки в буфер с ограниченной длиной. Функция strncpy из стандартной библиотеки не используется из-за странной особенности: если исходная длина превышает размер буфера назначения, то завершающий ноль в буфере установлен не будет.У функций семейства strnzcpy есть и собственная несколько необычная особенность: если параметр maxlen равен нулю, то функции считают буфер назначения (pDest) бесконечным (то есть, работают как sstrcpy).
Вариант strnzcpy(char *, const SString &, size_t) реализован ради унификации в случае, если источником данных является экземпляр основного высокоуровневого класса представления строк SString.
Точное сравнение строк
bool FASTCALL sstreq(const char * pS1, const char * pS2); bool FASTCALL sstreq(const uchar * pS1, const uchar * pS2); bool FASTCALL sstreq(const uchar * pS1, const char * pS2); bool FASTCALL sstreq(const wchar_t * pS1, const wchar_t * pS2); bool STDCALL sstrneq(const char * pS1, const char * pS2, uint len); bool STDCALL sstrneq(const uchar * pS1, const uchar * pS2, uint len);Функции с сигнатурой sstreq сравнивают две строки на равенство. Они используются для замены часто применяемой конструкции, в которой результат вызова strcmp сравнивается с нулем.
То есть:
// Вместо if(strcmp(s1, s2) == 0) { } // Следует использовать if(sstreq(s1, s2)) { }
Кроме сокращения кода функции sstreq применимы к аргументам типа const unsigned char *, const wchar_t * и к смешанной паре (const char *, const unsigned char *).Функции с сигнатурой sstrneq применяются в случае, если необходимо проверить равенство префиксов двух строк заданной длины.
Cравнение строк без учета регистра символов
bool FASTCALL sstreqi_ascii(const char * pS1, const char * pS2); bool FASTCALL sstreqi_ascii(const uchar * pS1, const uchar * pS2); bool FASTCALL sstreqi_ascii(const wchar_t * pS1, const wchar_t * pS2); bool FASTCALL sstreqi_ascii(const wchar_t * pS1, const char * pS2); bool STDCALL sstreqni_ascii(const char * pS1, const char * pS2, size_t maxlen);
Функции с сигнатурой sstreqi_ascii применяются для проверки эквивалентности двух строк с игнорированием разницы в регистрах символов, но только для ascii-символов (до 0x7f включительно).Самый частый паттерн использования этих функций - сравнение некоторой переменной строки с константным строковым литералом. Поскольку по коду обычно видно что литерал ограничен только ascii-символами.
Например:
if(sstreqi_ascii(s, "Some string with a Mixed Case")) { }
Битовые операции
Примитивные битовые операции (кроме очевидных |, &, ^, leading zeroes. Подсчитывает число старших нулевых бит до первого встреченного единичного бита. Если аргумент нулевой, то возвращает общее количество бит в аргументе.
Примитив Ctz
Count trailing zeroes. Подсчитывает число младших нулевых бит до первого встреченного единичного бита. Если аргумент нулевой, то возвращает общее количество бит в аргументе.
Примитив Cpop
Count population. Этот примитив чаще всего в различных программах и библиотеках называют popcount. Мы используем символ Cpop для того, чтобы он был в том же стиле, что и Clz и Ctz (C означает count).Подсчитывает общее число установленных бит в аргументе.
Примитив Rotr
Rotate right. Циклически перемещает все биты аргумента вправо (от старших - к младшим) на заданное вторым аргументом количество шагов. 'Циклически' означает, что биты, которые уходят за правый край значения возвращаются с левого его края. То есть, в отличии от обычного сдвига, циклический сдвиг обратимый.Примитив Rotl
Rotate left.