CUtils v2.0.0

от автора

Привет, Хабр!

Во время разработки ядра игрового движка Case Engine нам понадобились различные утилиты — от парсера INI файлов до удобных инструментов для работы с потоками. Чтобы всё это было удобно и лаконично, а другие разработчики могли легко интегрировать эти решения в свои проекты, мы создали кроссплатформенную коллекцию утилит CUtils. На данный момент она достигла версии 2.0.0!

В этой статье мы не будем подробно останавливаться на компонентах первой версии CUtils, о которых мы рассказывали в предыдущем материале. Вместо этого мы сосредоточимся на новых возможностях, которые появились в версии 2.0.0.

Статья о первой версии CUtilshttps://habr.com/ru/articles/857398/

Собранная библиотека для Windows — https://github.com/case-tech/CUtils-Win
Исходники библиотеки — https://github.com/case-tech/CUtils-Sources

Приятного чтения)

Что нового?

В версии 2.0.0 CUtils мы исключили компонент Hashed.hpp, так как его использование оказалось не слишком удобным. Однако вместо этого добавили множество других компонентов:

  1. Coroutine

  2. DataTime

  3. Encryption

  4. Filesystem

  5. Fusion

  6. Macroses

  7. Multithread

  8. Plugins

  9. Random

  10. StringHelper

Common

Для того чтобы подключение заголовков в компоненты выглядело аккуратнее и удобнее, мы выделили единый заголовочный файл — Common.hpp. Он уже подключён ко многим компонентам, и вам нужно будет подключить его вручную только при использовании компонента Macroses.hpp.

Содержимое Common.hpp:

// The MIT License(MIT) // Copyright © 2024 Case Technologies  #pragma once  // C++ libraries #include <coroutine> #include <iostream> #include <string> #include <filesystem> #include <fstream> #include <map>  #ifdef _WIN32 || _WIN64 #include <windows.h>  #else #include <dlfcn.h>   #endif  #include <future> #include <random> #include <typeindex> #include <vector> #include <iomanip> #include <cstring> #include <variant> #include <thread> #include <mutex>

Coroutine

Для удобной работы с сопрограммами мы решили написать компонент Coroutine.hpp.
Пример использования:

#include <Coroutine.hpp> // Пример корутины, которая выполняет несколько шагов и приостанавливается CUtils::Coroutine exampleCoroutine() {  // Вывод сообщения о начале выполнения корутины  std::cout << "Coroutine started" << std::endl;     // Приостановка корутины с помощью co_await  // std::suspend_always{} - это встроенный awaiter, который всегда приостанавливает выполнение  co_await std::suspend_always{};     // Вывод сообщения о возобновлении корутины  std::cout << "Coroutine resumed" << std::endl;     // Еще одна приостановка  co_await std::suspend_always{};     // Вывод сообщения о завершении корутины  std::cout << "Coroutine finished" << std::endl; }  int main() {  // Создаем корутину, вызывая функцию exampleCoroutine  // Обратите внимание, что exampleCoroutine возвращает объект типа Coroutine  CUtils::Coroutine coroutine = exampleCoroutine();     // Цикл для управления выполнением корутины  // Метод done() возвращает true, если корутина завершена  while (!coroutine.done())  {  // Вывод сообщения о том, что мы возобновляем корутину  std::cout << "Resuming coroutine..." << std::endl;      // Метод resume() возобновляет выполнение корутины  // Если корутина приостановлена, она продолжит выполнение с того места, где была остановлена  coroutine.resume();  }     // Вывод сообщения о завершении работы с корутиной  std::cout << "Coroutine completed" << std::endl;  return 0; }

DataTime

Для получения и форматирования даты и времени в CUtils есть компонент DataTime.hpp.
Пример использования:

#include "DateTime.hpp" int main() {  // Создаем объект с текущим временем  CUtils::DateTime now;     // Получаем текущую дату и время  std::string currentDateTime = now.GetCurrentDateTime();  std::string currentDate = now.GetCurrentDate();  std::string currentTime = now.GetCurrentTime();   // Выводим на экран  std::cout << "Current DateTime: " << currentDateTime << std::endl;  std::cout << "Current Date: " << currentDate << std::endl;  std::cout << "Current Time: " << currentTime << std::endl;     // Устанавливаем конкретное время  time_t specificTime = 1633024800; // Например, 1 октября 2021 года  now.SetTime(specificTime);     // Получаем время в формате time_t  time_t currentTime_ = now.GetTime();  std::cout << "Current Time (time_t): " << currentTime << std::endl;     // Форматируем время в произвольном формате  std::string formattedTime = now.Format("%Y/%m/%d %H-%M-%S");  std::cout << "Formatted Time: " << formattedTime << std::endl;     // Сравниваем объекты DateTime  CUtils::DateTime date1;  CUtils::DateTime date2;  if (date1 == date2)  {  std::cout << "date1 and date2 are the same." << std::endl;  }     else  {  std::cout << "date1 and date2 are different." << std::endl;  }  return 0; }

Encryption

В CUtils есть компонент Encryption.hpp, который предоставляет 5 алгоритмов для шифрования текста.
Пример использования:

#include "Encryption.hpp" int main() {  std::string plaintext = "Hello, World!";     // Алгоритм Цезаря  CUtils::Encryption caesarEncryptor(CUtils::Alg::Caesar);  std::string caesarCipher = caesarEncryptor.Encrypt(plaintext);  std::cout << "Caesar Encrypted: " << caesarCipher << std::endl;  std::cout << "Caesar Decrypted: " << caesarEncryptor.Decrypt(caesarCipher) << std::endl;     // Алгоритм Виженера  CUtils::Encryption vigenereEncryptor(CUtils::Alg::Vigenere, "key");  std::string vigenereCipher = vigenereEncryptor.Encrypt(plaintext);  std::cout << "Vigenere Encrypted: " << vigenereCipher << std::endl;  std::cout << "Vigenere Decrypted: " << vigenereEncryptor.Decrypt(vigenereCipher) << std::endl;     // Алгоритм XOR  CUtils::Encryption xorEncryptor(CUtils::Alg::XOR, "key");  std::string xorCipher = xorEncryptor.Encrypt(plaintext);  std::cout << "XOR Encrypted: " << xorCipher << std::endl;  std::cout << "XOR Decrypted: " << xorEncryptor.Decrypt(xorCipher) << std::endl;     // Алгоритм подстановки  CUtils::Encryption substitutionEncryptor(CUtils::Alg::Substitution);  std::string substitutionCipher = substitutionEncryptor.Encrypt(plaintext);  std::cout << "Substitution Encrypted: " << substitutionCipher << std::endl;  std::cout << "Substitution Decrypted: " << substitutionEncryptor.Decrypt(substitutionCipher) << std::endl;     // Алгоритм транспозиции  CUtils::Encryption transpositionEncryptor(CUtils::Alg::Transposition, "key");  std::string transpositionCipher = transpositionEncryptor.Encrypt(plaintext);  std::cout << "Transposition Encrypted: " << transpositionCipher << std::endl;  std::cout << "Transposition Decrypted: " << transpositionEncryptor.Decrypt(transpositionCipher) << std::endl;  return 0; }

Filesystem

Компонент Filesystem.hpp предоставляет полный функционал для работы с файловой системой.
Пример использования:

#include "Filesystem.hpp" int main() {  CUtils::Filesystem fs;     // Создание директории  fs.CreateDirectory("test_dir");     // Проверка существования  if (fs.Exists("test_dir"))   {  std::cout << "Directory exists." << std::endl;  }     // Создание файла (например, с помощью std::ofstream)  std::ofstream file("test_dir/test_file.txt");  file << "Hello, World!";  file.close();   // Проверка, является ли путь файлом  if (fs.IsFile("test_dir/test_file.txt"))   {  std::cout << "This is a file." << std::endl;  }     // Получение размера файла  try  {  uintmax_t size = fs.FileSize("test_dir/test_file.txt");  std::cout << "File size: " << size << " bytes." << std::endl;  }  catch (const std::runtime_error& e)  {  std::cerr << e.what() << std::endl;  }     // Копирование файла  fs.Copy("test_dir/test_file.txt", "test_dir/test_file_copy.txt");     // Удаление файла  fs.Remove("test_dir/test_file.txt");     // Удаление директории  fs.Remove("test_dir");  return 0; }

Fusion

Данный компонент предоставляет полный функционал для удобной работы с кортежами.
Пример использования:

#include "Fusion.hpp" int main() {  // Создаем два кортежа  CUtils::Fusion::Tuple<int, double> t1 = { 1, 2.5 };  CUtils::Fusion::Tuple<char, std::string> t2 = { 'a', "hello" };     // Объединяем кортежи  auto merged = CUtils::Fusion::Merge(t1, t2);  std::cout << "Merged tuple: ";  CUtils::Fusion::Print(merged); // Выводит: (1, 2.5, a, hello)     // Получаем элемент по индексу  auto elementByIndex = CUtils::Fusion::Get<1>(merged); // Получаем элемент с индексом 1(2.5)  std::cout << "Element at index 1: " << elementByIndex << std::endl; // Выводит: 2.5     // Получаем элемент по типу  auto elementByType = CUtils::Fusion::GetByType<std::string>(merged); // Получаем элемент типа std::string ("hello")  std::cout << "Element of type std::string: " << elementByType << std::endl; // Выводит: hello     // Получаем элемент по динамическому индексу  size_t index = 2;  auto elementDynamicIndex = CUtils::Fusion::GetByIndex(merged, index); // Получаем элемент с индексом 2 ('a')  std::visit([](auto&& arg) { std::cout << "Element at index 2: " << arg << std::endl; }, elementDynamicIndex); // Выводит: a     // Вычисляем размер кортежа  std::cout << "Size of merged tuple: " << CUtils::Fusion::Size(merged) << std::endl; // Выводит: 4     // Сравниваем кортежи  CUtils::Fusion::Tuple<int, double> t3 = { 1, 2.5 };  CUtils::Fusion::Tuple<int, double> t4 = { 1, 3.0 };  std::cout << "t1 == t3: " << CUtils::Fusion::AreEqual(t1, t3) << std::endl; // Выводит: true  std::cout << "t1 == t4: " << CUtils::Fusion::AreEqual(t1, t4) << std::endl; // Выводит: false     // Печатаем кортеж  std::cout << "Printing merged tuple: ";  CUtils::Fusion::Print(merged); // Выводит: (1, 2.5, a, hello)  return 0; }

Macroses

В заголовочный файл Macroses.hpp мы поместили множество полезных макросов. При использовании данного компонента нужно помнить, что подключение файла Common.hpp обязательно.
Пример использования:

#include <Common.hpp> #include <Macroses.hpp>  // Пример использования FORCE_INLINE FORCE_INLINE int add(int a, int b) {  return a + b; }  // Пример структуры с выравниванием struct ALIGNAS(16) MyStruct {  int a;  int b; };  // Пример функции с отключением предупреждений DISABLE_WARNINGS(4267) // Отключение предупреждения 4267 void someFunction() {  // Код, который может вызвать предупреждение 4267  std::cout << "Warning 4267 is disabled here." << std::endl; } ENABLE_WARNINGS // Включение предупреждений обратно    int main() {  // Вывод информации о компиляторе и платформе  std::cout << "Compiler: " << COMPILER_NAME << std::endl;  std::cout << "Platform: " << PLATFORM_NAME << std::endl;     // Условные проверки для компилятора и платформы #if defined(COMPILER_MSVC)  std::cout << "Using MSVC compiler." << std::endl; #elif defined(COMPILER_GCC)  std::cout << "Using GCC compiler." << std::endl; #elif defined(COMPILER_CLANG)  std::cout << "Using Clang compiler." << std::endl; #endif #if defined(PLATFORM_WINDOWS)  std::cout << "Running on Windows." << std::endl; #elif defined(PLATFORM_MAC)  std::cout << "Running on macOS." << std::endl; #elif defined(PLATFORM_LINUX)  std::cout << "Running on Linux." << std::endl; #endif     // пример использования FORCE_INLINE  int result = add(3, 4);  std::cout << "Result of add(3, 4): " << result << std::endl;     // Пример структуры с выравниванием  MyStruct obj;  std::cout << "Size of MyStruct: " << sizeof(obj) << std::endl;     // Пример отключения предупреждений  someFunction();     // Пример использования ASSERT  int x = 5;  ASSERT(x > 0); // Условие выполняется     // ASSERT(x < 0); // Условие не выполняется, программа завершится с ошибкой  // Пример определения размера массива  int arr[] = { 1, 2, 3, 4, 5 };  std::cout << "Array size: " << ARRAY_SIZE(arr) << std::endl;     // Пример объединения токенов и преобразования в строку #define MY_MACRO(x) CONCATENATE(my_prefix_, x)  std::cout << TO_STRING(MY_MACRO(value)) << std::endl; // Выведет "my_prefix_value"  return 0; }

Multithread

В данный компонент мы добавили функционал для удобной работы с потоками.
Пример использования:

#include "Multithread.hpp" int main() {  CUtils::Multithread multithread;     // Добавляем задачи  multithread.AddTask([]()  {  std::cout << "Task 1 is running" << std::endl;  });     multithread.AddTask([]()  {  std::cout << "Task 2 is running" << std::endl;  });     // Запускаем задачи в отдельных потоках  multithread.Run();     // Ожидаем завершения всех задач  multithread.JoinAll();  std::cout << "All tasks completed" << std::endl;  return 0; }

Plugins

Заголовочный файл Plugins.hpp имеет полный функционал для создания и работы с плагинами.

1. Создание плагина

Для начала вам нужно создать плагин, который будет загружаться и выполняться вашей системой. Плагин должен реализовывать интерфейс PluginInterface, который определен в файле Plugins.hpp.

Пример реализации плагина:

// MyPlugin.hpp #pragma once #include "Plugins.hpp" class MyPlugin : public CUtils::PluginInterface { public:  void execute() override  {    std::cout << "MyPlugin is executing!" << std::endl;  } };  // MyPlugin.cpp #include "MyPlugin.hpp" extern "C" CUtils::PluginInterface* createPlugin() {  return new MyPlugin(); }

2. Компиляция плагина

Вам нужно скомпилировать этот плагин в динамическую библиотеку (DLL на Windows или SO на Linux).

3. Использование компонента в основном приложении

Теперь вы можете использовать компонент Plugins.hpp в вашем основном приложении для загрузки и выполнения плагина.

Пример использования:

#include "Plugins.hpp" int main() {  CUtils::Plugins plugins;     // Укажите путь к скомпилированному плагину  std::wstring pluginPath = L"path/to/libMyPlugin.so"; // или L"path/to/MyPlugin.dll" на Windows  if (plugins.LoadPlugin(pluginPath))  {  std::cout << "Plugin loaded and executed successfully!" << std::endl;  }     else  {  std::cerr << "Failed to load plugin." << std::endl;  }  return 0; }

Random

Заголовочный файл Random.hpp содержит в себе множество рандомайзеров.
Пример использования:

#include "Random.hpp" int main() {  CUtils::Random randomGenerator;  std::cout << "Random Integer: " << randomGenerator.RandomInt(1, 100) << std::endl;  std::cout << "Random Double: " << randomGenerator.RandomDouble(0.0, 1.0) << std::endl;  std::cout << "Normal Double: " << randomGenerator.NormalDouble(5.0, 2.0) << std::endl;  std::cout << "Binomial Integer: " << randomGenerator.BinomialInt(10, 0.5) << std::endl;  std::cout << "Bernoulli Bool: " << (randomGenerator.BernoulliBool(0.7) ? "true" : "false") << std::endl;  std::cout << "Poisson Integer: " << randomGenerator.PoissonInt(4.0) << std::endl;  return 0; }

StringHelper

Заголовочный файл StringHelper.hpp содержит в себе весь функционал для работы со строковой информацией.
Пример использования:

#include "StringHelper.hpp" int main() {  // Создаем объект StringHelper с исходной строкой  CUtils::StringHelper str(" Hello, World! ");     // Убираем пробелы в начале и конце строки  std::string trimmed = str.Trim();  std::cout << "Trimmed: " << trimmed << std::endl;     // Преобразуем строку в нижний регистр  std::string lower = str.ToLowerCase();  std::cout << "Lower: " << lower << std::endl;     // Преобразуем строку в верхний регистр  std::string upper = str.ToUpperCase();  std::cout << "Upper: " << upper << std::endl;     // Разделяем строку на подстроки по пробелу  std::vector<std::string> tokens = str.Split(' ');  std::cout << "Tokens: ";  for (const auto& token : tokens)  {  std::cout << token << " ";  }  std::cout << std::endl;     // Проверяем, содержит ли строка подстроку "World"  bool contains = str.Contains("World");  std::cout << "Contains 'World': " << contains << std::endl;     // Проверяем, начинается ли строка с подстроки "Hello"  bool startsWith = str.StartsWith("Hello");  std::cout << "StartsWith 'Hello': " << startsWith << std::endl;     // Проверяем, заканчивается ли строка на подстроку "!"  bool endsWith = str.EndsWith("!");  std::cout << "EndsWith '!': " << endsWith << std::endl;     // Заменяем все вхождения "World" на "Universe"  std::string replaced = str.Replace("World", "Universe");  std::cout << "Replaced: " << replaced << std::endl;     // Находим индекс первого вхождения подстроки "World"  int index = str.IndexOf("World");  std::cout << "Index of 'World': " << index << std::endl;     // Находим индекс последнего вхождения символа 'o'  int lastIndex = str.LastIndexOf("o");  std::cout << "Last index of 'o': " << lastIndex << std::endl;     // Извлекаем подстроку, начиная с индекса 7 длиной 5 символов  std::string substr = str.Substring(7, 5);  std::cout << "Substring: " << substr << std::endl;     // Извлекаем числовую часть из строки (если она есть)  std::string numericPart = str.ExtractNumericPart();  std::cout << "Numeric part: " << numericPart << std::endl;     // Проверяем, является ли строка пустой  bool isEmpty = str.IsEmpty();  std::cout << "Is empty: " << isEmpty << std::endl;     // Получаем длину строки  size_t length = str.Length();  std::cout << "Length: " << length << std::endl;     // Преобразуем строку в целое число  std::optional<int> intValue = str.ToInt();  std::cout << "ToInt: " << (intValue.has_value() ? std::to_string(intValue.value()) : "Invalid") << std::endl;     // Преобразуем строку в число с плавающей точкой  std::optional<double> doubleValue = str.ToDouble();  std::cout << "ToDouble: " << (doubleValue.has_value() ? std::to_string(doubleValue.value()) : "Invalid") << std::endl;     // Преобразуем строку в логическое значение  std::optional<bool> boolValue = str.ToBool();  std::cout << "ToBool: " << (boolValue.has_value() ? (boolValue.value() ? "true" : "false") : "Invalid") << std::endl;  return 0; }

Конец

Кроссплатформенная коллекция утилитных компонентов CUtils распространяется под лицензией MIT. Вы можете делать с её исходным кодом абсолютно всё!

Ссылки

Наш сайт — https://case-technologies.ru/
Наш GitHub — https://github.com/case-tech


ссылка на оригинал статьи https://habr.com/ru/articles/868574/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *