Привет, Хабр!
Во время разработки ядра игрового движка Case Engine нам понадобились различные утилиты — от парсера INI файлов до удобных инструментов для работы с потоками. Чтобы всё это было удобно и лаконично, а другие разработчики могли легко интегрировать эти решения в свои проекты, мы создали кроссплатформенную коллекцию утилит CUtils. На данный момент она достигла версии 2.0.0!
В этой статье мы не будем подробно останавливаться на компонентах первой версии CUtils, о которых мы рассказывали в предыдущем материале. Вместо этого мы сосредоточимся на новых возможностях, которые появились в версии 2.0.0.
Статья о первой версии CUtils — https://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
, так как его использование оказалось не слишком удобным. Однако вместо этого добавили множество других компонентов:
-
Coroutine
-
DataTime
-
Encryption
-
Filesystem
-
Fusion
-
Macroses
-
Multithread
-
Plugins
-
Random
-
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/
Добавить комментарий