├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.adoc ├── android ├── .gitignore ├── app │ ├── .gitignore │ ├── CMakeLists.txt │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── cppfw │ │ │ └── utki_tests │ │ │ └── MainActivity.java │ │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── utki-$(version).pom.in └── utki │ ├── .gitignore │ ├── CMakeLists.txt │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ └── main │ ├── AndroidManifest.xml │ └── res │ └── values │ └── strings.xml ├── archlinux └── PKGBUILD ├── build ├── cmake │ └── CMakeLists.txt ├── conan │ ├── conanfile.py │ └── test_package │ │ ├── CMakeLists.txt │ │ ├── conanfile.py │ │ └── example.cpp ├── debian │ ├── changelog │ ├── compat │ ├── control.in │ ├── libutki$(soname)-dbgsrc.install.in │ ├── libutki$(soname).install.in │ ├── libutki-dbg$(soname).install.in │ ├── libutki-dev.install │ ├── libutki-doc.install │ ├── rules │ └── source │ │ └── format ├── emscripten │ └── conan.profile └── vcpkg │ ├── portfile.cmake.in │ ├── test │ ├── CMakeLists.txt │ ├── main.cpp │ ├── vcpkg-configuration.json │ └── vcpkg.json │ ├── usage │ └── vcpkg.json.in ├── cocoapods └── utki.podspec.in ├── config ├── asan.mk ├── base │ └── base.mk ├── cpp20.mk ├── dbg.mk ├── default.mk ├── dev.mk ├── emsc.mk ├── gcov.mk ├── gprof.mk └── rel.mk ├── doc ├── doxygen.cfg.in └── makefile ├── homebrew └── libutki.rb.in ├── makefile ├── msvs_solution ├── clargs │ ├── clargs.vcxproj │ ├── clargs.vcxproj.filters │ └── clargs.vcxproj.user ├── libutki │ ├── libutki.vcxproj │ ├── libutki.vcxproj.filters │ └── libutki.vcxproj.user ├── msvs_solution.sln ├── tst │ ├── tst.vcxproj │ ├── tst.vcxproj.filters │ └── tst.vcxproj.user └── unit_tests │ ├── unit_tests.vcxproj │ ├── unit_tests.vcxproj.filters │ └── unit_tests.vcxproj.user ├── msys2 └── PKGBUILD.in ├── nuget ├── build_nuget.ps1 └── nuget.autopkg.in ├── pkg-config ├── makefile └── utki.pc.in ├── src ├── makefile ├── soname.txt └── utki │ ├── base64.cpp │ ├── base64.hpp │ ├── config.hpp │ ├── debug.cpp │ ├── debug.hpp │ ├── deserializer.cpp │ ├── deserializer.hpp │ ├── destructable.hpp │ ├── enum_array.hpp │ ├── enum_iterable.hpp │ ├── exception.cpp │ ├── exception.hpp │ ├── flags.hpp │ ├── linq.hpp │ ├── macros.hpp │ ├── math.hpp │ ├── shared.hpp │ ├── shared_ref.hpp │ ├── signal.hpp │ ├── singleton.hpp │ ├── sort.hpp │ ├── span.hpp │ ├── spin_lock.hpp │ ├── string.cpp │ ├── string.hpp │ ├── time.cpp │ ├── time.hpp │ ├── tree.hpp │ ├── type_traits.hpp │ ├── types.hpp │ ├── unicode.cpp │ ├── unicode.hpp │ ├── util.hpp │ ├── utility.cpp │ ├── utility.hpp │ ├── variant.hpp │ ├── views.hpp │ └── windows.hpp ├── src_deps └── fast_float │ ├── LICENSE-MIT │ ├── README.adoc │ └── fast_float.h ├── tests ├── .clang-tidy ├── harness │ └── makefile ├── makefile ├── singleton_over_shared_library │ ├── makefile │ ├── singleton_test.cpp │ ├── test_singleton.hpp_ │ ├── testso.cpp │ └── testso.hpp_ └── unit │ ├── makefile │ └── src │ ├── base64.cpp │ ├── config.cpp │ ├── debug.cpp │ ├── deserializer.cpp │ ├── destructable.cpp │ ├── enum_array.cpp │ ├── enum_iterable.cpp │ ├── exception.cpp │ ├── flags.cpp │ ├── linq.cpp │ ├── math.cpp │ ├── shared.cpp │ ├── shared_ref.cpp │ ├── signal.cpp │ ├── singleton.cpp │ ├── sort.cpp │ ├── span.cpp │ ├── string.cpp │ ├── tree.cpp │ ├── type_traits.cpp │ ├── unicode.cpp │ ├── utility.cpp │ ├── variant.cpp │ └── views.cpp ├── wiki ├── MainPage.adoc └── SingletonUsage.md └── xcode ├── Podfile └── utki └── utki.xcodeproj ├── project.pbxproj ├── project.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── ivan.xcuserdatad │ └── UserInterfaceState.xcuserstate └── xcuserdata └── ivan.xcuserdatad └── xcschemes └── xcschememanagement.plist /.clang-format: -------------------------------------------------------------------------------- 1 | tool-configs/clang-format/.clang-format -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | tool-configs/clang-tidy/.clang-tidy -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # disable line endings conversion for all files 2 | * -text 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [igagis] 4 | patreon: igagis 5 | custom: ["https://paypal.me/igagis"] 6 | # open_collective: # Replace with a single Open Collective username 7 | # ko_fi: # Replace with a single Ko-fi username 8 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | # liberapay: # Replace with a single Liberapay username 11 | # issuehunt: # Replace with a single IssueHunt username 12 | # otechie: # Replace with a single Otechie username 13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/out/* 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/harness/clargs"] 2 | path = tests/harness/clargs 3 | url = ../clargs 4 | branch = main 5 | # ignore = dirty 6 | [submodule "tests/harness/tst"] 7 | path = tests/harness/tst 8 | url = ../tst 9 | branch = main 10 | # ignore = dirty 11 | [submodule "tool-configs"] 12 | path = tool-configs 13 | url = ../tool-configs 14 | branch = main 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "unit", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/tests/unit/out/dev/tests", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}/tests/unit/", 15 | "environment": [{"name": "LD_LIBRARY_PATH", "value": "../../src/out/dev"}], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | } 24 | ], 25 | "preLaunchTask": "build-dev", 26 | "miDebuggerPath": "/usr/bin/gdb" 27 | }, 28 | { 29 | "name": "g++ test string", 30 | "type": "cppdbg", 31 | "request": "launch", 32 | "program": "${workspaceFolder}/tests/string/tests", 33 | "args": [], 34 | "stopAtEntry": false, 35 | "cwd": "${workspaceFolder}/tests/string/", 36 | "environment": [{"name": "LD_LIBRARY_PATH", "value": "../../src/build/"}], 37 | "externalConsole": false, 38 | "MIMode": "gdb", 39 | "setupCommands": [ 40 | { 41 | "description": "Enable pretty-printing for gdb", 42 | "text": "-enable-pretty-printing", 43 | "ignoreFailures": true 44 | } 45 | ], 46 | "preLaunchTask": "build", 47 | "miDebuggerPath": "/usr/bin/gdb" 48 | }, 49 | { 50 | "name": "g++ test tree", 51 | "type": "cppdbg", 52 | "request": "launch", 53 | "program": "${workspaceFolder}/tests/tree/tests", 54 | "args": [], 55 | "stopAtEntry": false, 56 | "cwd": "${workspaceFolder}/tests/tree/", 57 | "environment": [{"name": "LD_LIBRARY_PATH", "value": "../../src/build/"}], 58 | "externalConsole": false, 59 | "MIMode": "gdb", 60 | "setupCommands": [ 61 | { 62 | "description": "Enable pretty-printing for gdb", 63 | "text": "-enable-pretty-printing", 64 | "ignoreFailures": true 65 | } 66 | ], 67 | "preLaunchTask": "build", 68 | "miDebuggerPath": "/usr/bin/gdb" 69 | }, 70 | { 71 | "name": "g++ test flags", 72 | "type": "cppdbg", 73 | "request": "launch", 74 | "program": "${workspaceFolder}/tests/flags/out/rel/tests", 75 | "args": [], 76 | "stopAtEntry": false, 77 | "cwd": "${workspaceFolder}/tests/flags/", 78 | "environment": [{"name": "LD_LIBRARY_PATH", "value": "../../src/build/"}], 79 | "externalConsole": false, 80 | "MIMode": "gdb", 81 | "setupCommands": [ 82 | { 83 | "description": "Enable pretty-printing for gdb", 84 | "text": "-enable-pretty-printing", 85 | "ignoreFailures": true 86 | } 87 | ], 88 | "preLaunchTask": "build", 89 | "miDebuggerPath": "/usr/bin/gdb" 90 | }, 91 | ] 92 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "format", 6 | "type": "shell", 7 | "command": "make apply-format", 8 | "problemMatcher": [], 9 | "group": "build" 10 | }, 11 | { 12 | "label": "test_", 13 | "type": "shell", 14 | "command": "make test", 15 | "dependsOn": "build", 16 | "problemMatcher": [], 17 | "group": "build" 18 | }, 19 | { 20 | "label": "test-dev", 21 | "type": "shell", 22 | "command": "make test config=dev", 23 | "dependsOn": "build-dev", 24 | "problemMatcher": [], 25 | "group": "build" 26 | }, 27 | { 28 | "label": "test_gcov", 29 | "type": "shell", 30 | "command": "make test config=gcov", 31 | "dependsOn": "build-dev", 32 | "problemMatcher": [], 33 | "group": "build" 34 | }, 35 | { 36 | "label": "test_tree", 37 | "type": "shell", 38 | "command": "make --directory tests/tree test", 39 | "problemMatcher": [ 40 | "$gcc" 41 | ], 42 | "dependsOn": "build", 43 | "group": "build" 44 | }, 45 | { 46 | "label": "test_singleton_over_shared_library", 47 | "type": "shell", 48 | "command": "make --directory tests/singleton_over_shared_library test", 49 | "problemMatcher": [ 50 | "$gcc" 51 | ], 52 | "dependsOn": "build", 53 | "group": "build" 54 | }, 55 | { 56 | "label": "clean-all", 57 | "type": "shell", 58 | "command": "make clean-all", 59 | "problemMatcher": [], 60 | "group": "build" 61 | }, 62 | { 63 | "label": "clean", 64 | "type": "shell", 65 | "command": "make clean", 66 | "problemMatcher": [], 67 | "group": "build" 68 | }, 69 | { 70 | "label": "clean-dev", 71 | "type": "shell", 72 | "command": "make clean config=dev", 73 | "problemMatcher": [], 74 | "group": "build" 75 | }, 76 | { 77 | "label": "build", 78 | "type": "shell", 79 | "command": "make", 80 | "args": [], 81 | "problemMatcher": [ 82 | "$gcc" 83 | ], 84 | "group": "build" 85 | }, 86 | { 87 | "label": "build-clang", 88 | "type": "shell", 89 | "command": "CXX=clang++ make", 90 | "args": [], 91 | "problemMatcher": [ 92 | "$gcc" 93 | ], 94 | "group": "build" 95 | }, 96 | { 97 | "label": "build-dev", 98 | "type": "shell", 99 | "command": "make config=dev", 100 | "args": [], 101 | "problemMatcher": [ 102 | "$gcc" 103 | ], 104 | "group": "build" 105 | }, 106 | { 107 | "label": "build_asan", 108 | "type": "shell", 109 | "command": "make config=asan", 110 | "args": [], 111 | "problemMatcher": [ 112 | "$gcc" 113 | ], 114 | "group": "build" 115 | } 116 | ] 117 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | utki - Utility Kit for C++. 4 | 5 | Copyright (c) 2015-2025 Ivan Gagis 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | :name: utki 2 | 3 | = {name} 4 | 5 | |==== 6 | | link:https://github.com/cppfw/{name}/releases[image:https://img.shields.io/github/tag/cppfw/{name}.svg[releases]] | link:https://github.com/cppfw/{name}/actions[image:https://github.com/cppfw/{name}/workflows/ci/badge.svg[ci status]] | link:https://codecov.io/gh/cppfw/{name}[image:https://codecov.io/gh/cppfw/{name}/branch/main/graph/badge.svg?token=LKA3SRSkc3[codecov.io]] 7 | |==== 8 | 9 | C++ utility library. Stuff missing from `std::` namespace. 10 | 11 | See link:wiki/MainPage.adoc[WiKi] for installation instructions and tutorials. 12 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android/app/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/CMakeLists.txt -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | defaultConfig { 6 | applicationId "io.github.cppfw.utki_tests" 7 | minSdkVersion 15 8 | targetSdkVersion 29 9 | 10 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | } 19 | 20 | dependencies { 21 | implementation fileTree(dir: 'libs', include: ['*.jar']) 22 | implementation 'com.android.support:appcompat-v7:28.0.0' 23 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 24 | testImplementation 'junit:junit:4.12' 25 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 26 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 27 | } 28 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/github/cppfw/utki_tests/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.cppfw.utki_tests; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | public class MainActivity extends AppCompatActivity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_main); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | utki_tests 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.4.0' 10 | classpath 'io.github.howardpang:androidNativeBundle:1.1.3' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | task clean(type: Delete) { 25 | delete rootProject.buildDir 26 | } 27 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 27 23:54:12 EET 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':utki' 2 | -------------------------------------------------------------------------------- /android/utki-$(version).pom.in: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | io.github.cppfw 6 | utki 7 | $(version) 8 | aar 9 | 10 | -------------------------------------------------------------------------------- /android/utki/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android/utki/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Sets the minimum version of CMake required to build your native library. 2 | # This ensures that a certain set of CMake features is available to 3 | # your build. 4 | 5 | cmake_minimum_required(VERSION 3.4.1) 6 | 7 | # Specifies a library name, specifies whether the library is STATIC or 8 | # SHARED, and provides relative paths to the source code. You can 9 | # define multiple libraries by adding multiple add_library() commands, 10 | # and CMake builds them for you. When you build your app, Gradle 11 | # automatically packages shared libraries with your APK. 12 | 13 | set(name utki) 14 | 15 | file(GLOB_RECURSE srcs "../../src/utki/*.cpp") 16 | 17 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 18 | 19 | add_library( 20 | ${name} 21 | STATIC 22 | ${srcs} 23 | ) 24 | 25 | target_include_directories( 26 | ${name} 27 | PRIVATE 28 | "../../src_deps" 29 | ) 30 | 31 | target_link_libraries( 32 | ${name} 33 | android log 34 | ) 35 | -------------------------------------------------------------------------------- /android/utki/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.ydq.android.gradle.native-aar.export' // must go after android gradle plugin 3 | 4 | android { 5 | compileSdkVersion 29 6 | 7 | defaultConfig { 8 | minSdkVersion 21 9 | targetSdkVersion 29 10 | 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | 13 | externalNativeBuild { 14 | cmake { 15 | targets "utki" 16 | } 17 | } 18 | } 19 | 20 | nativeBundleExport { 21 | headerDir = "${project.projectDir}/../../src/" 22 | bundleStatic = true 23 | includeHeaderFilter.add("**/*.hpp") 24 | } 25 | 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | 33 | // Encapsulates your external native build configurations. 34 | externalNativeBuild { 35 | // Encapsulates your CMake build configurations. 36 | cmake { 37 | // Provides a relative path to your CMake build script. 38 | path "CMakeLists.txt" 39 | } 40 | } 41 | } 42 | 43 | dependencies { 44 | 45 | } 46 | 47 | // copy and rename release AAR to unified name 48 | task copy_aar(type: Copy) { 49 | from file("build/outputs/aar/") 50 | into file("../") 51 | include("*-static-release.aar") 52 | rename { String fileName -> 53 | fileName.replace("static-release.aar", "\$(version).aar.in") 54 | } 55 | } 56 | 57 | afterEvaluate { 58 | copy_aar.dependsOn(assembleRelease) 59 | copy_aar.dependsOn(bundleStaticLibRelease) 60 | } 61 | -------------------------------------------------------------------------------- /android/utki/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android/utki/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /android/utki/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | utki 3 | 4 | -------------------------------------------------------------------------------- /archlinux/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Ivan Gagis 2 | 3 | pkgname=utki 4 | pkgver=$PACKAGE_VERSION 5 | pkgrel=1 6 | arch=('x86_64' 'armv7h' 'aarch64') 7 | epoch= 8 | pkgdesc="C++ utilities" 9 | url="http://github.com/cppfw/${pkgname}" 10 | license=('MIT') 11 | groups=() 12 | 13 | depends=() 14 | 15 | makedepends=( 16 | 'myci' 17 | 'prorab' 18 | 'prorab-extra' 19 | 'doxygen' 20 | "clang" # for clang-format and clang-tidy 21 | ) 22 | checkdepends=() 23 | optdepends=() 24 | provides=() 25 | conflicts=() 26 | replaces=() 27 | backup=() 28 | options=() 29 | install= 30 | changelog= 31 | source=() # Do not download any sources 32 | noextract=() 33 | md5sums=() 34 | validpgpkeys=() 35 | 36 | rootDir=$(pwd)/.. # project root directory 37 | 38 | prepare() { 39 | cd "$rootDir" 40 | } 41 | 42 | build() { 43 | cd "$rootDir" 44 | # TODO: turn on lint when arch adopts more modern clang-tidy 45 | make lint=off 46 | } 47 | 48 | check() { 49 | cd "$rootDir" 50 | make test 51 | } 52 | 53 | package() { 54 | cd "$rootDir" 55 | make DESTDIR="$pkgdir" PREFIX=/usr install 56 | } 57 | -------------------------------------------------------------------------------- /build/cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(name utki) 4 | project(${name}) 5 | 6 | # !!! find_package must go after project() declaration !!! 7 | # Otherwise VCPKG does not set the CMAKE_PREFIX_PATH to find packages. 8 | find_package(myci CONFIG REQUIRED) 9 | 10 | set(srcs) 11 | myci_add_source_files(srcs 12 | DIRECTORY 13 | ../../src/${name} 14 | RECURSIVE 15 | ) 16 | 17 | myci_declare_library(${name} 18 | SOURCES 19 | ${srcs} 20 | PUBLIC_INCLUDE_DIRECTORIES 21 | ../../src 22 | PRIVATE_INCLUDE_DIRECTORIES 23 | ../../src_deps 24 | INSTALL_INCLUDE_DIRECTORIES 25 | ../../src/${name} 26 | ) 27 | -------------------------------------------------------------------------------- /build/conan/conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | from conan import ConanFile 3 | from conan.tools.scm import Git 4 | from conan.tools.files import load, update_conandata, copy 5 | from conan.tools.layout import basic_layout 6 | 7 | class UtkiConan(ConanFile): 8 | name = "utki" 9 | license = "MIT" 10 | author = "Ivan Gagis " 11 | url = "http://github.com/cppfw/" + name 12 | description = "Utility C++ library" 13 | topics = ("C++", "cross-platform") 14 | settings = "os", "compiler", "build_type", "arch" 15 | package_type = "library" 16 | options = {"shared": [False], "fPIC": [True, False]} 17 | default_options = {"shared": False, "fPIC": True} 18 | generators = "AutotoolsDeps" # this will set CXXFLAGS etc. env vars 19 | 20 | def build_requirements(self): 21 | self.tool_requires("prorab/[>=2.0.27]@cppfw/main") 22 | self.tool_requires("prorab-extra/[>=0.2.57]@cppfw/main") 23 | 24 | def config_options(self): 25 | if self.settings.os == "Windows": 26 | del self.options.fPIC 27 | 28 | # save commit and remote URL to conandata.yml for packaging 29 | def export(self): 30 | git = Git(self) 31 | scm_url = git.get_remote_url() 32 | # NOTE: Git.get_commit() doesn't work properly, 33 | # it gets latest commit of the folder in which conanfile.py resides. 34 | # So, we use "git rev-parse HEAD" instead as it gets the actual HEAD 35 | # commit regardless of the current working directory within the repo. 36 | scm_commit = git.run("rev-parse HEAD") # get current commit 37 | update_conandata(self, {"sources": {"commit": scm_commit, "url": scm_url}}) 38 | 39 | def source(self): 40 | git = Git(self) 41 | sources = self.conan_data["sources"] 42 | # shallow fetch commit 43 | git.fetch_commit(url=sources["url"], commit=sources['commit']) 44 | # shallow clone submodules 45 | git.run("submodule update --init --remote --depth 1") 46 | 47 | def build(self): 48 | if self.settings.os == "Emscripten": 49 | self.run("make $MAKE_INCLUDE_DIRS_ARG config=emsc --directory=src") 50 | else: 51 | self.run("make $MAKE_INCLUDE_DIRS_ARG lint=off") 52 | self.run("make $MAKE_INCLUDE_DIRS_ARG lint=off test") 53 | 54 | def package(self): 55 | if self.settings.os == "Emscripten": 56 | src_rel_dir = os.path.join(self.build_folder, "src/out/emsc") 57 | else: 58 | src_rel_dir = os.path.join(self.build_folder, "src/out/rel") 59 | 60 | src_dir = os.path.join(self.build_folder, "src") 61 | dst_include_dir = os.path.join(self.package_folder, "include") 62 | dst_lib_dir = os.path.join(self.package_folder, "lib") 63 | dst_bin_dir = os.path.join(self.package_folder, "bin") 64 | copy(conanfile=self, pattern="*.h", dst=dst_include_dir, src=src_dir, keep_path=True) 65 | copy(conanfile=self, pattern="*.hpp", dst=dst_include_dir, src=src_dir, keep_path=True) 66 | 67 | if self.options.shared: 68 | copy(conanfile=self, pattern="*" + self.name + ".lib", dst=dst_lib_dir, src="", keep_path=False) 69 | copy(conanfile=self, pattern="*.dll", dst=dst_bin_dir, src=src_rel_dir, keep_path=False) 70 | copy(conanfile=self, pattern="*.so", dst=dst_lib_dir, src=src_rel_dir, keep_path=False) 71 | copy(conanfile=self, pattern="*.so.*", dst=dst_lib_dir, src=src_rel_dir, keep_path=False) 72 | copy(conanfile=self, pattern="*.dylib", dst=dst_lib_dir, src=src_rel_dir, keep_path=False) 73 | else: 74 | copy(conanfile=self, pattern="*" + self.name + ".lib", dst=dst_lib_dir, src="", keep_path=False) 75 | copy(conanfile=self, pattern="*.a", dst=dst_lib_dir, src=src_rel_dir, keep_path=False) 76 | 77 | def package_info(self): 78 | self.cpp_info.libs = [self.name] 79 | 80 | def package_id(self): 81 | # change package id only when minor or major version changes, i.e. when ABI breaks 82 | self.info.requires.minor_mode() 83 | -------------------------------------------------------------------------------- /build/conan/test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(PackageTest CXX) 3 | 4 | # set(CMAKE_VERBOSE_MAKEFILE on) 5 | 6 | find_package(utki CONFIG REQUIRED) 7 | 8 | add_executable(example example.cpp) 9 | target_link_libraries(example utki::utki) 10 | -------------------------------------------------------------------------------- /build/conan/test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from conan import ConanFile, tools 4 | from conan.tools.cmake import CMake, cmake_layout 5 | 6 | class UtkiTestConan(ConanFile): 7 | settings = "os", "compiler", "build_type", "arch" 8 | generators = "CMakeToolchain", "CMakeDeps" 9 | 10 | def requirements(self): 11 | self.requires(self.tested_reference_str) 12 | 13 | def build(self): 14 | cmake = CMake(self) 15 | cmake.configure() 16 | cmake.build() 17 | 18 | def layout(self): 19 | cmake_layout(self) 20 | 21 | def test(self): 22 | self.run(".%sexample" % os.sep, env="conanrun") # env sets LD_LIBRARY_PATH etc. to find dependency libs 23 | -------------------------------------------------------------------------------- /build/conan/test_package/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << utki::make_string("formatted %s", "string") << '\n'; 5 | } 6 | -------------------------------------------------------------------------------- /build/debian/compat: -------------------------------------------------------------------------------- 1 | 14 2 | -------------------------------------------------------------------------------- /build/debian/control.in: -------------------------------------------------------------------------------- 1 | Source: libutki 2 | Section: libs 3 | Priority: extra 4 | Maintainer: Ivan Gagis 5 | Build-Depends: 6 | debhelper (>= 9), 7 | dpkg-dev (>=1.17.0), 8 | prorab, 9 | prorab-extra, 10 | myci, 11 | clang-tidy, 12 | clang-format 13 | Build-Depends-Indep: doxygen 14 | Standards-Version: 3.9.2 15 | 16 | Package: libutki$(soname) 17 | Section: libs 18 | Architecture: any 19 | Depends: 20 | ${shlibs:Depends}, 21 | ${misc:Depends} 22 | Description: cross-platform C++ file system library. 23 | Different C++ utility classes and functions. 24 | 25 | Package: libutki-dbg$(soname) 26 | Section: libs 27 | Architecture: any 28 | Depends: 29 | ${shlibs:Depends}, 30 | ${misc:Depends} 31 | Description: cross-platform C++ file system library. 32 | Debug version of libutki. 33 | 34 | Package: libutki$(soname)-dbgsrc 35 | Section: debug 36 | Architecture: all 37 | Depends: 38 | libutki$(soname)-dbgsym (= ${binary:Version}), 39 | libutki-dbg$(soname)-dbgsym (= ${binary:Version}), 40 | ${misc:Depends} 41 | Description: debugging sources for libutki$(soname) package. 42 | 43 | Package: libutki-dev 44 | Section: libdevel 45 | Architecture: any 46 | Depends: 47 | libutki$(soname) (= ${binary:Version}), 48 | libutki-dbg$(soname) (= ${binary:Version}), 49 | ${misc:Depends} 50 | Suggests: libutki-doc 51 | Description: cross-platform C++ utility library. 52 | Different C++ utility classes and functions. 53 | 54 | Package: libutki-doc 55 | Section: doc 56 | Architecture: all 57 | Depends: ${misc:Depends} 58 | Description: documentation for libutki - cross-platform C++ library. 59 | For more details see description to libutki-dev package. 60 | -------------------------------------------------------------------------------- /build/debian/libutki$(soname)-dbgsrc.install.in: -------------------------------------------------------------------------------- 1 | usr/src/* 2 | -------------------------------------------------------------------------------- /build/debian/libutki$(soname).install.in: -------------------------------------------------------------------------------- 1 | usr/lib/libutki.so.* 2 | -------------------------------------------------------------------------------- /build/debian/libutki-dbg$(soname).install.in: -------------------------------------------------------------------------------- 1 | usr/lib/libutki-dbg.so.* 2 | -------------------------------------------------------------------------------- /build/debian/libutki-dev.install: -------------------------------------------------------------------------------- 1 | usr/include 2 | usr/lib/pkgconfig 3 | usr/lib/libutki.so 4 | usr/lib/libutki.a 5 | usr/lib/libutki-dbg.so 6 | usr/lib/libutki-dbg.a 7 | -------------------------------------------------------------------------------- /build/debian/libutki-doc.install: -------------------------------------------------------------------------------- 1 | usr/share/doc 2 | -------------------------------------------------------------------------------- /build/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export PREFIX := /usr 4 | 5 | dbgsrc_pkg_name := $(filter %-dbgsrc, $(shell awk '/^Package: /{print $2}' debian/control)) 6 | debug_prefix_map_arg := -fdebug-prefix-map=$(shell pwd)/src=$(PREFIX)/src/$(patsubst %-dbgsrc,%,$(dbgsrc_pkg_name)) 7 | 8 | export PRORAB_INSTALL_DBGSRC := true 9 | 10 | export DEB_CFLAGS_MAINT_APPEND := $(debug_prefix_map_arg) 11 | export DEB_CXXFLAGS_MAINT_APPEND := $(debug_prefix_map_arg) 12 | export DEB_OBJCFLAGS_MAINT_APPEND := $(debug_prefix_map_arg) 13 | export DEB_OBJCXXFLAGS_MAINT_APPEND := $(debug_prefix_map_arg) 14 | 15 | %: 16 | dh $@ 17 | 18 | override_dh_auto_build: 19 | dh_auto_build --buildsystem=makefile -- --directory=.. 20 | @echo "" 21 | @echo "|========== build debug version of the library ==========|" 22 | @echo "" 23 | dh_auto_build --buildsystem=makefile -- --directory=../src config=dbg 24 | 25 | override_dh_auto_install: 26 | dh_auto_install --buildsystem=makefile -- --directory=../ 27 | dh_auto_install --buildsystem=makefile -- --directory=../src config=dbg 28 | 29 | override_dh_auto_clean: 30 | dh_auto_clean --buildsystem=makefile -- --directory=../ 31 | dh_auto_clean --buildsystem=makefile -- --directory=../src config=dbg 32 | -------------------------------------------------------------------------------- /build/debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /build/emscripten/conan.profile: -------------------------------------------------------------------------------- 1 | [settings] 2 | os=Emscripten 3 | arch=wasm 4 | compiler=clang 5 | compiler.version=15 6 | compiler.libcxx=libc++ 7 | build_type=Release 8 | 9 | [tool_requires] 10 | emsdk/[>=3.1.72] 11 | -------------------------------------------------------------------------------- /build/vcpkg/portfile.cmake.in: -------------------------------------------------------------------------------- 1 | vcpkg_check_linkage(ONLY_STATIC_LIBRARY) 2 | 3 | vcpkg_from_github( 4 | OUT_SOURCE_PATH SOURCE_PATH 5 | REPO cppfw/${PORT} 6 | REF $(git_ref) 7 | SHA512 $(archive_hash) 8 | HEAD_REF main 9 | ) 10 | 11 | vcpkg_cmake_configure( 12 | SOURCE_PATH "${SOURCE_PATH}/build/cmake" 13 | ) 14 | 15 | vcpkg_cmake_install() 16 | 17 | vcpkg_cmake_config_fixup() 18 | 19 | # Delete the include directory from the debug installation to prevent overlap. 20 | file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") 21 | 22 | # Install the LICENSE file to the package's share directory and rename it to copyright. 23 | file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) 24 | 25 | # Copy the usage instruction file to the package's share directory. 26 | configure_file("${CMAKE_CURRENT_LIST_DIR}/usage" "${CURRENT_PACKAGES_DIR}/share/${PORT}/usage" COPYONLY) 27 | -------------------------------------------------------------------------------- /build/vcpkg/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) 4 | 5 | project(test) 6 | 7 | find_package(utki CONFIG REQUIRED) 8 | 9 | add_executable(test main.cpp) 10 | 11 | target_link_libraries( 12 | test 13 | PRIVATE 14 | utki::utki 15 | ) 16 | -------------------------------------------------------------------------------- /build/vcpkg/test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, const char** argv){ 4 | auto s = utki::split("Hello world!"); 5 | 6 | std::cout << "num strings = " << s.size() << std::endl; 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /build/vcpkg/test/vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "5e5d0e1cd7785623065e77eff011afdeec1a3574", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "git", 10 | "repository": "https://github.com/cppfw/vcpkg-repo/", 11 | "baseline": "cae91955c2be3e86b2488f44f0bff1c7b3f1cce8", 12 | "reference": "main", 13 | "packages": [ "myci" ] 14 | } 15 | ], 16 | "overlay-ports": [ 17 | "../overlay" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /build/vcpkg/test/vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | "utki" 4 | ] 5 | } -------------------------------------------------------------------------------- /build/vcpkg/usage: -------------------------------------------------------------------------------- 1 | utki provides CMake targets: 2 | 3 | find_package(utki CONFIG REQUIRED) 4 | target_link_libraries(main PRIVATE utki::utki) 5 | -------------------------------------------------------------------------------- /build/vcpkg/vcpkg.json.in: -------------------------------------------------------------------------------- 1 | { 2 | "name": "utki", 3 | "version": "$(version)", 4 | "homepage": "https://github.com/cppfw/utki", 5 | "description": "C++ utility kit library", 6 | "license": "MIT", 7 | "dependencies": [ 8 | { 9 | "name" : "vcpkg-cmake", 10 | "host" : true 11 | }, 12 | { 13 | "name" : "vcpkg-cmake-config", 14 | "host" : true 15 | }, 16 | { 17 | "name": "myci", 18 | "host": true 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /cocoapods/utki.podspec.in: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "utki" 3 | s.version = "$(version)" 4 | s.summary = "C++ utility library. Stuff missing from std:: namespace." 5 | s.homepage = "https://github.com/cppfw/#{s.name}" 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.author = { "Ivan Gagis" => "igagis@gmail.com" } 8 | s.platform = :ios 9 | s.ios.deployment_target = '9.0' 10 | 11 | s.source = { :http => "http://gagis.hopto.org/repo/cppfw/cocoapods/#{s.name}-#{s.version}.zip" } 12 | 13 | s.source_files = "include/**/*.{hpp,h}" 14 | s.header_mappings_dir = "include" 15 | 16 | s.ios.vendored_framework = "lib/ios/#{s.name}.xcframework" 17 | end 18 | -------------------------------------------------------------------------------- /config/asan.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)rel.mk 2 | 3 | this_cxxflags += -fsanitize=address 4 | this_ldflags += -fsanitize=address 5 | -------------------------------------------------------------------------------- /config/base/base.mk: -------------------------------------------------------------------------------- 1 | this_cxxflags += -Wall # enable all warnings 2 | this_cxxflags += -Wno-comment # enable all warnings 3 | this_cxxflags += -Wnon-virtual-dtor # warn if base class has non-virtual destructor 4 | this_cxxflags += -Werror # treat warnings as errors 5 | # this_cxxflags += -Wfatal-errors # stop on first error encountered 6 | this_cxxflags += -fstrict-aliasing # in order to comply with the c++ standard more strictly 7 | this_cxxflags += -std=c++17 8 | this_cxxflags += -fPIC 9 | this_cxxflags += -g 10 | 11 | this_ldlibs += -lstdc++ 12 | -------------------------------------------------------------------------------- /config/cpp20.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)rel.mk 2 | 3 | this_cxxflags += -std=c++20 4 | -------------------------------------------------------------------------------- /config/dbg.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)base/base.mk 2 | 3 | this_cxxflags += -DDEBUG 4 | this_cxxflags += -O0 5 | 6 | this_dbg := -dbg 7 | 8 | # do not install headers 9 | this_install_hdrs := $(prorab_space) 10 | -------------------------------------------------------------------------------- /config/default.mk: -------------------------------------------------------------------------------- 1 | $(eval $(call prorab-config-default, rel)) 2 | -------------------------------------------------------------------------------- /config/dev.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)base/base.mk 2 | 3 | this_cxxflags += -DDEBUG 4 | this_cxxflags += -O0 5 | -------------------------------------------------------------------------------- /config/emsc.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)base/base.mk 2 | 3 | this_cxxflags += -O3 4 | 5 | this_cxx := em++ 6 | this_cc := emcc 7 | this_ar := emar 8 | 9 | this_static_lib_only := true 10 | 11 | # TODO: remove the warning suppression when the PR is merged 12 | # Suppress version-check warning due to https://github.com/conan-io/conan-center-index/pull/26247 13 | this_cxxflags += -Wno-version-check 14 | 15 | this_cxxflags += -fwasm-exceptions 16 | this_ldflags += -fwasm-exceptions 17 | 18 | this_cxxflags += -pthread 19 | this_ldflags += -pthread 20 | -------------------------------------------------------------------------------- /config/gcov.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)base/base.mk 2 | 3 | # no optimization to avoid mismamtch of actual code to source lines, 4 | # otherwise coverage report will not be accurate 5 | this_cxxflags += -O0 6 | 7 | this_cxxflags += -ftest-coverage 8 | this_cxxflags += -fprofile-arcs 9 | this_cxxflags += --coverage 10 | 11 | this_ldflags += --coverage 12 | -------------------------------------------------------------------------------- /config/gprof.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)rel.mk 2 | 3 | this_cxxflags += -pg 4 | this_ldflags += -pg 5 | -------------------------------------------------------------------------------- /config/rel.mk: -------------------------------------------------------------------------------- 1 | include $(config_dir)base/base.mk 2 | 3 | this_cxxflags += -O3 4 | 5 | this_lint_cmd = $(prorab_lint_cmd_clang_tidy) 6 | 7 | # WORKAROUND: on ubuntu jammy dpkg-buildpackage passes -ffat-lto-objects compilation flag 8 | # which is not supported by clang and clang-tidy complains about it: 9 | # error: optimization flag '-ffat-lto-objects' is not supported [clang-diagnostic-ignored-optimization-argument] 10 | # Thus, suppress this warning. 11 | this_cxxflags += -Wno-ignored-optimization-argument 12 | 13 | ifeq ($(os),macosx) 14 | # WORKAROUND: 15 | # clang-tidy on macos doesn't use /usr/local/include as default place to 16 | # search for header files, so we add it explicitly 17 | this_cxxflags += -isystem /usr/local/include 18 | endif 19 | -------------------------------------------------------------------------------- /doc/makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | include prorab-doxygen.mk 3 | 4 | this_name := utki 5 | this_out_dir := out 6 | this_version := $(shell myci-deb-version.sh) 7 | 8 | $(eval $(prorab-build-doxygen)) 9 | -------------------------------------------------------------------------------- /homebrew/libutki.rb.in: -------------------------------------------------------------------------------- 1 | class Libutki < Formula 2 | desc "C++ utility functions library. Stuff missing from std:: namespace." 3 | homepage "https://github.com/cppfw/utki" 4 | url "https://github.com/cppfw/utki/archive/$(version).tar.gz" 5 | sha256 "$(sha256)" 6 | 7 | depends_on "prorab" => :build 8 | depends_on "prorab-extra" => :build 9 | 10 | # use gmake here because otherwise homebrew uses default Mac's make which is of too old version 3.81 11 | def install 12 | ENV['PATH'] += ":#{ENV['HOMEBREW_PREFIX']}/bin" 13 | # install from src directory because tests require submodules 14 | system "#{ENV['HOMEBREW_PREFIX']}/opt/make/libexec/gnubin/make", "--include-dir=#{ENV['HOMEBREW_PREFIX']}/include", "--directory=src", "install", "PREFIX=#{prefix}", "lint=off" 15 | end 16 | 17 | test do 18 | # tests require submodules, so skip those in homebrew recepie 19 | # system "#{ENV['HOMEBREW_PREFIX']}/opt/make/libexec/gnubin/make", "--include-dir=#{ENV['HOMEBREW_PREFIX']}/include", "test" 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | 3 | $(eval $(prorab-include-subdirs)) 4 | -------------------------------------------------------------------------------- /msvs_solution/clargs/clargs.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /msvs_solution/clargs/clargs.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /msvs_solution/libutki/libutki.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /msvs_solution/tst/tst.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /msvs_solution/unit_tests/unit_tests.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Source Files 62 | 63 | 64 | Source Files 65 | 66 | 67 | Source Files 68 | 69 | 70 | Source Files 71 | 72 | 73 | Source Files 74 | 75 | 76 | Source Files 77 | 78 | 79 | Source Files 80 | 81 | 82 | Source Files 83 | 84 | 85 | Source Files 86 | 87 | 88 | Source Files 89 | 90 | 91 | Source Files 92 | 93 | 94 | Source Files 95 | 96 | 97 | Source Files 98 | 99 | 100 | Source Files 101 | 102 | 103 | Source Files 104 | 105 | 106 | Source Files 107 | 108 | 109 | Source Files 110 | 111 | 112 | Source Files 113 | 114 | 115 | Source Files 116 | 117 | 118 | Source Files 119 | 120 | 121 | Source Files 122 | 123 | 124 | Source Files 125 | 126 | 127 | Source Files 128 | 129 | 130 | Source Files 131 | 132 | 133 | Source Files 134 | 135 | 136 | Source Files 137 | 138 | 139 | Source Files 140 | 141 | 142 | Source Files 143 | 144 | 145 | Source Files 146 | 147 | 148 | -------------------------------------------------------------------------------- /msvs_solution/unit_tests/unit_tests.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /msys2/PKGBUILD.in: -------------------------------------------------------------------------------- 1 | # Maintainer: Ivan Gagis 2 | 3 | if [ "$MSYSTEM" == "MSYS" ]; then 4 | pkgPrefix=lib 5 | dirPrefix=/usr 6 | arch=('x86_64' 'i686') 7 | elif [ "$MSYSTEM" == "MINGW32" ]; then 8 | pkgPrefix=mingw-w64-i686- 9 | dirPrefix=/mingw32 10 | arch=('any') 11 | elif [ "$MSYSTEM" == "MINGW64" ]; then 12 | pkgPrefix=mingw-w64-x86_64- 13 | dirPrefix=/mingw64 14 | arch=('any') 15 | else 16 | echo "ERROR: unknown MSYS shell: $MSYSTEM" 17 | exit 1 18 | fi 19 | 20 | pkgname="${pkgPrefix}utki" 21 | pkgver=$(version) 22 | pkgrel=1 23 | epoch= 24 | pkgdesc="C++ utilities" 25 | #arch=('any') # defined above 26 | url="http://github.com/cppfw/utki" 27 | license=('MIT') 28 | groups=() 29 | 30 | depends=() 31 | 32 | makedepends=( 33 | 'myci' 34 | 'prorab' 35 | 'prorab-extra' 36 | 'doxygen' 37 | "${pkgPrefix}clang" # for clang-format 38 | "${pkgPrefix}clang-tools-extra" # for clang-tidy 39 | ) 40 | checkdepends=() 41 | optdepends=() 42 | provides=() 43 | conflicts=() 44 | replaces=() 45 | backup=() 46 | options=() 47 | install= 48 | changelog= 49 | source=() # Do not download any sources 50 | noextract=() 51 | md5sums=() 52 | validpgpkeys=() 53 | 54 | rootDir=$(pwd)/.. # project root directory 55 | 56 | prepare() { 57 | cd "$rootDir" 58 | } 59 | 60 | build() { 61 | cd "$rootDir" 62 | make lint=off 63 | } 64 | 65 | check() { 66 | cd "$rootDir" 67 | make test 68 | } 69 | 70 | package() { 71 | cd "$rootDir" 72 | make DESTDIR="$pkgdir" PREFIX="$dirPrefix" install 73 | } 74 | -------------------------------------------------------------------------------- /nuget/build_nuget.ps1: -------------------------------------------------------------------------------- 1 | Push-Location 2 | $scriptdir = Split-Path $MyInvocation.MyCommand.Path 3 | cd $scriptdir 4 | 5 | #extract version from debian/changelog 6 | $ver = (Get-Content ..\build\debian\changelog -Head 1) -replace ".*\((\d*\.\d*\.\d*)(\-\d+){0,1}\).*",'$1' 7 | #Write-Host $ver 8 | 9 | #insert version into all *.in files 10 | Get-ChildItem "." -Filter *.in | Foreach-Object{ 11 | $content = Get-Content $_.FullName 12 | 13 | #filter and save content to the original file 14 | #$content | Where-Object {$_ -match 'step[49]'} | Set-Content $_.FullName 15 | 16 | #filter and save content to a new file 17 | ($content -replace '\$\(version\)',"$ver") | Set-Content ($_.BaseName) 18 | } 19 | 20 | "%VS143COMNTOOLS%\VsMSBuildCmd.bat" 21 | 22 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v141_Debug /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 23 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v141_Release /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 24 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v141_Debug /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 25 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v141_Release /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 26 | 27 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Debug_MD /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 28 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Release_MD /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 29 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Debug_MD /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 30 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Release_MD /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 31 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Debug_MT /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 32 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Release_MT /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 33 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Debug_MT /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 34 | # msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v142_Release_MT /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 35 | 36 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Debug_MD /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 37 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Release_MD /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 38 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Debug_MD /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 39 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Release_MD /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 40 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Debug_MT /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 41 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Release_MT /p:Platform=x86 /v:minimal /nologo; If(!$?){exit 1} 42 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Debug_MT /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 43 | msbuild /m ../msvs_solution/msvs_solution.sln /t:Rebuild /p:Configuration=v143_Release_MT /p:Platform=x64 /v:minimal /nologo; If(!$?){exit 1} 44 | 45 | Write-Host "running tests..." 46 | # run release tests first 47 | # ../msvs_solution/v142_Release_MD/unit_tests.exe --jobs=2 --junit-out=junit_x86_v142_release_md.xml; If(!$?){exit 1} 48 | # ../msvs_solution/v142_Release_MT/unit_tests.exe --jobs=2 --junit-out=junit_x86_v142_release_mt.xml; If(!$?){exit 1} 49 | # ../msvs_solution/x64/v142_Release_MD/unit_tests.exe --jobs=2 --junit-out=junit_x64_v142_release_md.xml; If(!$?){exit 1} 50 | # ../msvs_solution/x64/v142_Release_MT/unit_tests.exe --jobs=2 --junit-out=junit_x64_v142_release_mt.xml; If(!$?){exit 1} 51 | 52 | # ../msvs_solution/v142_Debug_MD/unit_tests.exe --jobs=2 --junit-out=junit_x86_v142_debug_md.xml; If(!$?){exit 1} 53 | # ../msvs_solution/v142_Debug_MT/unit_tests.exe --jobs=2 --junit-out=junit_x86_v142_debug_mt.xml; If(!$?){exit 1} 54 | # ../msvs_solution/x64/v142_Debug_MD/unit_tests.exe --jobs=2 --junit-out=junit_x64_v142_debug_md.xml; If(!$?){exit 1} 55 | # ../msvs_solution/x64/v142_Debug_MT/unit_tests.exe --jobs=2 --junit-out=junit_x64_v142_debug_mt.xml; If(!$?){exit 1} 56 | 57 | # run release tests first 58 | ../msvs_solution/v143_Release_MD/unit_tests.exe --jobs=2 --junit-out=junit_x86_v143_release_md.xml; If(!$?){exit 1} 59 | ../msvs_solution/v143_Release_MT/unit_tests.exe --jobs=2 --junit-out=junit_x86_v143_release_mt.xml; If(!$?){exit 1} 60 | ../msvs_solution/x64/v143_Release_MD/unit_tests.exe --jobs=2 --junit-out=junit_x64_v143_release_md.xml; If(!$?){exit 1} 61 | ../msvs_solution/x64/v143_Release_MT/unit_tests.exe --jobs=2 --junit-out=junit_x64_v143_release_mt.xml; If(!$?){exit 1} 62 | 63 | # for some reason debug tests freeze on CI 64 | # ../msvs_solution/v143_Debug_MD/unit_tests.exe --jobs=2 --junit-out=junit_x86_v143_debug_md.xml; If(!$?){exit 1} 65 | # ../msvs_solution/v143_Debug_MT/unit_tests.exe --jobs=2 --junit-out=junit_x86_v143_debug_mt.xml; If(!$?){exit 1} 66 | # ../msvs_solution/x64/v143_Debug_MD/unit_tests.exe --jobs=2 --junit-out=junit_x64_v143_debug_md.xml; If(!$?){exit 1} 67 | # ../msvs_solution/x64/v143_Debug_MT/unit_tests.exe --jobs=2 --junit-out=junit_x64_v143_debug_mt.xml; If(!$?){exit 1} 68 | 69 | Write-NuGetPackage nuget.autopkg 70 | If(!$?){exit 1} 71 | Pop-Location 72 | -------------------------------------------------------------------------------- /nuget/nuget.autopkg.in: -------------------------------------------------------------------------------- 1 | configurations { 2 | UserPlatformToolset { 3 | // Needed because autopackage lacks VS2015+ support 4 | key = "PlatformToolset"; 5 | choices: "v140,v141,v142,v143"; 6 | }; 7 | 8 | RuntimeLibrary { 9 | key = "RuntimeLibrary"; // This is the key you can find in .vcxproj file 10 | choices: "MultiThreaded,MultiThreadedDebug,MultiThreadedDLL,MultiThreadedDebugDLL"; // these choices must be valid values for .vcxproj file 11 | }; 12 | } 13 | 14 | nuget{ 15 | nuspec{ 16 | id = libutki; 17 | version : $(version); 18 | title: C++ utility library; 19 | authors: {Ivan Gagis}; 20 | owners: {Ivan Gagis}; 21 | licenseUrl: "https://raw.githubusercontent.com/cppfw/utki/main/LICENSE"; 22 | projectUrl: "https://github.com/cppfw/utki"; 23 | iconUrl: "https://github.com/cppfw/utki/blob/main/logo.svg"; 24 | requireLicenseAcceptance:false; 25 | summary: C++ utility library. Stuff missing from std:: namespace; 26 | 27 | description: @"C++ utility library. Stuff missing from std:: namespace"; 28 | releaseNotes: "initial release"; 29 | copyright: Copyright 2022 Ivan Gagis; 30 | tags: {native}; 31 | } 32 | dependencies { 33 | packages : { 34 | }; 35 | } 36 | files { 37 | //this is needed to put headers in the base folder 38 | nestedInclude: { 39 | #destination = ${d_include}utki; 40 | "..\src\utki\**\*.hpp" 41 | }; 42 | 43 | //==== v140 tools ===== 44 | /* 45 | [x86,v140,release] { 46 | lib: ..\msvs_solution\v140_Release\libutki.lib; 47 | } 48 | [x86,v140,debug] { 49 | lib: ..\msvs_solution\v140_Debug\libutki.lib; 50 | } 51 | [x64,v140,release] { 52 | lib: ..\msvs_solution\x64\v140_Release\libutki.lib; 53 | } 54 | [x64,v140,debug] { 55 | lib: ..\msvs_solution\x64\v140_Debug\libutki.lib; 56 | } 57 | */ 58 | //==== v141 tools ==== 59 | /* 60 | [x86,v141,release] { 61 | lib: ..\msvs_solution\v141_Release\libutki.lib; 62 | } 63 | [x86,v141,debug] { 64 | lib: ..\msvs_solution\v141_Debug\libutki.lib; 65 | } 66 | [x64,v141,release] { 67 | lib: ..\msvs_solution\x64\v141_Release\libutki.lib; 68 | } 69 | [x64,v141,debug] { 70 | lib: ..\msvs_solution\x64\v141_Debug\libutki.lib; 71 | } 72 | */ 73 | //==== v142 tools ==== 74 | /* 75 | [x86,v142,release,MultiThreaded] { 76 | lib: ..\msvs_solution\v142_Release_MT\libutki.lib; 77 | } 78 | [x86,v142,debug,MultiThreadedDebug] { 79 | lib: ..\msvs_solution\v142_Debug_MT\libutki.lib; 80 | } 81 | [x64,v142,release,MultiThreaded] { 82 | lib: ..\msvs_solution\x64\v142_Release_MT\libutki.lib; 83 | } 84 | [x64,v142,debug,MultiThreadedDebug] { 85 | lib: ..\msvs_solution\x64\v142_Debug_MT\libutki.lib; 86 | } 87 | [x86,v142,release,MultiThreadedDLL] { 88 | lib: ..\msvs_solution\v142_Release_MD\libutki.lib; 89 | } 90 | [x86,v142,debug,MultiThreadedDebugDLL] { 91 | lib: ..\msvs_solution\v142_Debug_MD\libutki.lib; 92 | } 93 | [x64,v142,release,MultiThreadedDLL] { 94 | lib: ..\msvs_solution\x64\v142_Release_MD\libutki.lib; 95 | } 96 | [x64,v142,debug,MultiThreadedDebugDLL] { 97 | lib: ..\msvs_solution\x64\v142_Debug_MD\libutki.lib; 98 | } 99 | */ 100 | //==== v143 tools ==== 101 | 102 | [x86,v143,release,MultiThreaded] { 103 | lib: ..\msvs_solution\v143_Release_MT\libutki.lib; 104 | } 105 | [x86,v143,debug,MultiThreadedDebug] { 106 | lib: ..\msvs_solution\v143_Debug_MT\libutki.lib; 107 | } 108 | [x64,v143,release,MultiThreaded] { 109 | lib: ..\msvs_solution\x64\v143_Release_MT\libutki.lib; 110 | } 111 | [x64,v143,debug,MultiThreadedDebug] { 112 | lib: ..\msvs_solution\x64\v143_Debug_MT\libutki.lib; 113 | } 114 | [x86,v143,release,MultiThreadedDLL] { 115 | lib: ..\msvs_solution\v143_Release_MD\libutki.lib; 116 | } 117 | [x86,v143,debug,MultiThreadedDebugDLL] { 118 | lib: ..\msvs_solution\v143_Debug_MD\libutki.lib; 119 | } 120 | [x64,v143,release,MultiThreadedDLL] { 121 | lib: ..\msvs_solution\x64\v143_Release_MD\libutki.lib; 122 | } 123 | [x64,v143,debug,MultiThreadedDebugDLL] { 124 | lib: ..\msvs_solution\x64\v143_Debug_MD\libutki.lib; 125 | } 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /pkg-config/makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | include prorab-pkg-config.mk 3 | 4 | this_version := $(shell myci-deb-version.sh) 5 | 6 | $(eval $(prorab-pkg-config)) 7 | -------------------------------------------------------------------------------- /pkg-config/utki.pc.in: -------------------------------------------------------------------------------- 1 | Name: utki # human-readable name 2 | Description: C++ utility library # human-readable description 3 | Version: $(version) 4 | URL: https://github.com/cppfw/utki 5 | Requires: 6 | Conflicts: 7 | Libs: -lutki 8 | Libs.private: 9 | Cflags: 10 | -------------------------------------------------------------------------------- /src/makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | include prorab-license.mk 3 | include prorab-install-dbgsrc.mk 4 | include prorab-clang-format.mk 5 | 6 | $(eval $(call prorab-config, ../config)) 7 | 8 | this__src_dir := utki 9 | 10 | this_name := $(this__src_dir)$(this_dbg) 11 | 12 | this_soname := $(shell cat $(d)soname.txt) 13 | 14 | this_srcs := $(call prorab-src-dir, $(this__src_dir)) 15 | 16 | this_cxxflags += -isystem ../src_deps 17 | 18 | $(eval $(prorab-build-lib)) 19 | 20 | this_src_dir := $(this__src_dir) 21 | this_license_file := ../LICENSE 22 | $(eval $(prorab-license)) 23 | 24 | $(eval $(prorab-clang-format)) 25 | 26 | $(eval $(prorab-install-dbgsrc)) 27 | -------------------------------------------------------------------------------- /src/soname.txt: -------------------------------------------------------------------------------- 1 | 3 -------------------------------------------------------------------------------- /src/utki/base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #include "base64.hpp" 30 | 31 | #include 32 | #include 33 | 34 | #include "debug.hpp" 35 | #include "type_traits.hpp" 36 | 37 | using namespace utki; 38 | 39 | namespace { 40 | constexpr const std::array::max()) + 1> decode_table{ 41 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43 | 0, 0, 0, 0, 0, 0x3E, 0, 0, 0, 0x3F, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 44 | 0x3D, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 45 | 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0, 0, 0, 0, 46 | 0, 0, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 47 | 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54 | 0, 0, 0, 0, 0, 0, 0, 0, 0 55 | }; 56 | 57 | std::array base64_decode_quad(char a, char b, char c, char d) 58 | { 59 | constexpr auto bits_per_char = 6; 60 | uint32_t quad = // 61 | (decode_table[a] << bits_per_char * 3) | // 62 | (decode_table[b] << bits_per_char * 2) | // 63 | (decode_table[c] << bits_per_char) | // 64 | decode_table[d]; 65 | 66 | return { 67 | uint8_t((quad >> utki::byte_bits * 2) & utki::byte_mask), // 68 | uint8_t((quad >> utki::byte_bits) & utki::byte_mask), 69 | uint8_t(quad & utki::byte_mask) 70 | }; 71 | } 72 | } // namespace 73 | 74 | std::vector utki::base64_decode(std::string_view str) 75 | { 76 | if (str.empty()) { 77 | return {}; 78 | } 79 | 80 | size_t num_quads = str.size() / 4; 81 | 82 | // strip out padding 83 | if (str.back() == '=') { 84 | if (num_quads == 0) { 85 | throw std::invalid_argument("base64_decode(): malformed base64 string: padding is not complete"); 86 | } 87 | --num_quads; 88 | 89 | str = str.substr(0, str.size() - 1); 90 | 91 | if (str.back() == '=') { 92 | str = str.substr(0, str.size() - 1); 93 | } 94 | } 95 | 96 | std::vector ret; 97 | 98 | auto i = str.begin(); 99 | for (size_t counter = 0; counter != num_quads; ++counter) { 100 | ASSERT(i < str.end()) 101 | ASSERT(std::next(i, 4) <= str.end()) 102 | auto a = *i; 103 | ++i; 104 | auto b = *i; 105 | ++i; 106 | auto c = *i; 107 | ++i; 108 | auto d = *i; 109 | ++i; 110 | auto bytes = base64_decode_quad(a, b, c, d); 111 | 112 | ret.insert(ret.end(), bytes.begin(), bytes.end()); 113 | } 114 | 115 | ASSERT(i <= str.end()) 116 | 117 | if (i == str.end()) { 118 | return ret; 119 | } 120 | 121 | auto a = *(i++); 122 | ASSERT(i != str.end()) 123 | auto b = *(i++); 124 | 125 | if (i == str.end()) { 126 | auto bytes = base64_decode_quad(a, b, 'A', 'A'); 127 | ret.push_back(bytes[0]); 128 | return ret; 129 | } 130 | 131 | auto c = *(i++); 132 | auto bytes = base64_decode_quad(a, b, c, 'A'); 133 | ret.push_back(bytes[0]); 134 | ret.push_back(bytes[1]); 135 | 136 | return ret; 137 | } 138 | -------------------------------------------------------------------------------- /src/utki/base64.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | namespace utki { 36 | 37 | std::vector base64_decode(std::string_view str); 38 | 39 | } // namespace utki 40 | -------------------------------------------------------------------------------- /src/utki/debug.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #include "debug.hpp" 30 | 31 | #include "config.hpp" 32 | #include "string.hpp" 33 | #include "utility.hpp" 34 | 35 | #if CFG_OS_NAME == CFG_OS_NAME_ANDROID 36 | # include 37 | #else 38 | # include 39 | #endif 40 | 41 | #if CFG_COMPILER == CFG_COMPILER_GCC || CFG_COMPILER == CFG_COMPILER_CLANG 42 | # include // for demangling type names 43 | #endif 44 | 45 | // undefine possibly defined assert macro to avoid macro's name collision 46 | #ifdef assert 47 | # undef assert 48 | #endif 49 | 50 | using namespace utki; 51 | 52 | namespace { 53 | const std::string_view colored_error_string = "\033[1;31merror\033[0m"; 54 | const std::string_view uncolored_error_string = "error"; 55 | } // namespace 56 | 57 | void utki::assert( 58 | bool condition, 59 | const std::function& print, 60 | const utki::source_location& source_location 61 | ) 62 | { 63 | if (condition) { 64 | return; 65 | } 66 | 67 | std::stringstream ss; 68 | ss << source_location.file_name() << ":" << source_location.line() << ": "; 69 | 70 | if (is_terminal_cerr()) { 71 | #if CFG_COMPILER == CFG_COMPILER_MSVC && CFG_COMPILER_MSVC_TOOLS_V <= 141 72 | ss << std::string(colored_error_string); 73 | } else { 74 | ss << std::string(uncolored_error_string); 75 | #else 76 | ss << colored_error_string; 77 | } else { 78 | ss << uncolored_error_string; 79 | #endif 80 | } 81 | 82 | ss << ": assertion failed"; 83 | 84 | if (print) { 85 | ss << ":" << std::endl << " "; 86 | print(ss); 87 | } 88 | 89 | #if CFG_OS_NAME == CFG_OS_NAME_ANDROID 90 | // TODO: remove commented code after __android_log_assert() is tested to be working 91 | // __android_log_write(ANDROID_LOG_INFO, "utki", ss.str().c_str()); 92 | 93 | __android_log_assert(ss.str().c_str(), "utki", nullptr); 94 | #else 95 | std::cerr << ss.str() << std::endl; 96 | std::cerr.flush(); 97 | #endif 98 | 99 | abort(); 100 | } 101 | 102 | void utki::log(const std::function& print) 103 | { 104 | if (!print) { 105 | return; 106 | } 107 | 108 | #if CFG_OS_NAME == CFG_OS_NAME_ANDROID 109 | std::stringstream ss; 110 | print(ss); 111 | __android_log_write(ANDROID_LOG_INFO, "utki", ss.str().c_str()); 112 | #else 113 | print(std::cout); 114 | std::cout.flush(); 115 | #endif 116 | } 117 | 118 | std::string utki::demangle(const std::type_info& type_info) 119 | { 120 | #if CFG_COMPILER == CFG_COMPILER_GCC || CFG_COMPILER == CFG_COMPILER_CLANG 121 | // NOLINTNEXTLINE(cppcoreguidelines-init-variables) 122 | int status; 123 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 124 | auto demangled_name = abi::__cxa_demangle( 125 | type_info.name(), 126 | nullptr, // let __cxa_demangle() allocate memory buffer for us 127 | nullptr, // not interested in allocated memory buffer size 128 | &status 129 | ); 130 | 131 | switch (status) { 132 | case 0: // demangling succeeded 133 | { 134 | utki::scope_exit scope_exit([demangled_name] { 135 | // NOLINTNEXTLINE(cppcoreguidelines-no-malloc, cppcoreguidelines-owning-memory) 136 | free(demangled_name); // abi::__cxa_demangle requires freeing allocated memory 137 | }); 138 | return {demangled_name}; 139 | } 140 | break; 141 | // NOLINTNEXTLINE(bugprone-branch-clone) 142 | case -1: // memory allocation failed 143 | [[fallthrough]]; 144 | case -2: // given mangled name is not a valid name under the C++ ABI mangling rules 145 | [[fallthrough]]; 146 | case -3: // one of the arguments is invalid 147 | [[fallthrough]]; 148 | default: 149 | return {type_info.name()}; 150 | } 151 | #elif CFG_COMPILER == CFG_COMPILER_MSVC 152 | std::string name(type_info.name()); 153 | constexpr const std::string_view prefix("class "); 154 | if (utki::starts_with(name, prefix)) { 155 | return name.substr(prefix.size()); 156 | } 157 | return name; 158 | #else 159 | return {type_info.name()}; 160 | #endif 161 | } 162 | -------------------------------------------------------------------------------- /src/utki/deserializer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #include "deserializer.hpp" 30 | 31 | #include "string.hpp" 32 | #include "utility.hpp" 33 | 34 | using namespace utki; 35 | 36 | std::string_view deserializer::read_string(size_t length) 37 | { 38 | if (this->size() < length) { 39 | throw std::invalid_argument( 40 | utki::cat("deserializer::read_string(", length, "): buffer size is only ", this->size()) 41 | ); 42 | } 43 | 44 | auto ret = utki::make_string_view(this->data.subspan(0, length)); 45 | this->data = this->data.subspan(length); 46 | return ret; 47 | } 48 | 49 | utki::span deserializer::read_span(size_t length) 50 | { 51 | if (this->size() < length) { 52 | throw std::invalid_argument( 53 | utki::cat("deserializer::read_span(", length, "): buffer size is only ", this->size()) 54 | ); 55 | } 56 | 57 | auto ret = this->data.subspan(0, length); 58 | this->data = this->data.subspan(length); 59 | return ret; 60 | } 61 | 62 | uint8_t deserializer::read_uint8() 63 | { 64 | if (this->size() < sizeof(uint8_t)) { 65 | throw std::invalid_argument("deserializer::read_uint8(): buffer has less bytes then needed"); 66 | } 67 | 68 | auto ret = this->data.front(); 69 | this->data = this->data.subspan(sizeof(uint8_t)); 70 | return ret; 71 | } 72 | 73 | uint16_t deserializer::read_uint16_le() 74 | { 75 | if (this->size() < sizeof(uint16_t)) { 76 | throw std::invalid_argument("deserializer::read_uint16_le(): buffer has less bytes then needed"); 77 | } 78 | 79 | auto ret = utki::deserialize16le(this->data.data()); 80 | this->data = this->data.subspan(sizeof(uint16_t)); 81 | return ret; 82 | } 83 | 84 | uint32_t deserializer::read_uint32_le() 85 | { 86 | if (this->size() < sizeof(uint32_t)) { 87 | throw std::invalid_argument("deserializer::read_uint32_le(): buffer has less bytes then needed"); 88 | } 89 | 90 | auto ret = utki::deserialize32le(this->data.data()); 91 | this->data = this->data.subspan(sizeof(uint32_t)); 92 | return ret; 93 | } 94 | 95 | uint64_t deserializer::read_uint64_le() 96 | { 97 | if (this->size() < sizeof(uint64_t)) { 98 | throw std::invalid_argument("deserializer::read_uint64_le(): buffer has less bytes then needed"); 99 | } 100 | 101 | auto ret = utki::deserialize64le(this->data.data()); 102 | this->data = this->data.subspan(sizeof(uint64_t)); 103 | return ret; 104 | } 105 | 106 | float deserializer::read_float_le() 107 | { 108 | if (this->size() < sizeof(float)) { 109 | throw std::invalid_argument("deserializer::read_float_le(): buffer has less bytes then needed"); 110 | } 111 | 112 | auto ret = utki::deserialize_float_le(this->data.data()); 113 | this->data = this->data.subspan(sizeof(float)); 114 | return ret; 115 | } 116 | 117 | uint16_t deserializer::read_uint16_be() 118 | { 119 | if (this->size() < sizeof(uint16_t)) { 120 | throw std::invalid_argument("deserializer::read_uint16_be(): buffer has less bytes then needed"); 121 | } 122 | 123 | auto ret = utki::deserialize16be(this->data.data()); 124 | this->data = this->data.subspan(sizeof(uint16_t)); 125 | return ret; 126 | } 127 | 128 | uint32_t deserializer::read_uint32_be() 129 | { 130 | if (this->size() < sizeof(uint32_t)) { 131 | throw std::invalid_argument("deserializer::read_uint32_be(): buffer has less bytes then needed"); 132 | } 133 | 134 | auto ret = utki::deserialize32be(this->data.data()); 135 | this->data = this->data.subspan(sizeof(uint32_t)); 136 | return ret; 137 | } 138 | 139 | uint64_t deserializer::read_uint64_be() 140 | { 141 | if (this->size() < sizeof(uint64_t)) { 142 | throw std::invalid_argument("deserializer::read_uint64_be(): buffer has less bytes then needed"); 143 | } 144 | 145 | auto ret = utki::deserialize64be(this->data.data()); 146 | this->data = this->data.subspan(sizeof(uint64_t)); 147 | return ret; 148 | } 149 | 150 | float deserializer::read_float_be() 151 | { 152 | if (this->size() < sizeof(float)) { 153 | throw std::invalid_argument("deserializer::read_float_be(): buffer has less bytes then needed"); 154 | } 155 | 156 | auto ret = utki::deserialize_float_be(this->data.data()); 157 | this->data = this->data.subspan(sizeof(float)); 158 | return ret; 159 | } 160 | 161 | void deserializer::skip(size_t length) 162 | { 163 | if (this->size() < length) { 164 | throw std::invalid_argument(utki::cat("deserializer::skip(", length, "): buffer size is only ", this->size())); 165 | } 166 | 167 | this->data = this->data.subspan(length); 168 | } 169 | -------------------------------------------------------------------------------- /src/utki/deserializer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include "span.hpp" 32 | 33 | namespace utki { 34 | 35 | class deserializer 36 | { 37 | utki::span data; 38 | 39 | public: 40 | deserializer(utki::span data) : 41 | data(data) 42 | {} 43 | 44 | bool empty() const noexcept 45 | { 46 | return this->data.empty(); 47 | } 48 | 49 | size_t size() const noexcept 50 | { 51 | return this->data.size(); 52 | } 53 | 54 | std::string_view read_string(size_t length); 55 | 56 | utki::span read_span(size_t length); 57 | 58 | uint8_t read_uint8(); 59 | 60 | uint16_t read_uint16_le(); 61 | uint32_t read_uint32_le(); 62 | uint64_t read_uint64_le(); 63 | float read_float_le(); 64 | 65 | uint16_t read_uint16_be(); 66 | uint32_t read_uint32_be(); 67 | uint64_t read_uint64_be(); 68 | float read_float_be(); 69 | 70 | void skip(size_t length); 71 | }; 72 | 73 | } // namespace utki 74 | -------------------------------------------------------------------------------- /src/utki/destructable.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | namespace utki { 32 | 33 | /** 34 | * @brief Destructable class. 35 | * This class is to be used as a base class for the cases when 'void*' is needed, but the ownership of the passed in 36 | * pointer is transferred, so that the object pointed to by the pointer could be destroyed correctly using 37 | * virtual destructor. So, in this case instead of 'void*' use 'destructable*'. 38 | */ 39 | class destructable 40 | { 41 | public: 42 | destructable() = default; 43 | 44 | destructable(const destructable&) = default; 45 | destructable& operator=(const destructable&) = default; 46 | 47 | destructable(destructable&&) = default; 48 | destructable& operator=(destructable&&) = default; 49 | 50 | virtual ~destructable() noexcept = default; 51 | }; 52 | 53 | } // namespace utki 54 | -------------------------------------------------------------------------------- /src/utki/enum_array.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | #include "enum_iterable.hpp" 34 | #include "type_traits.hpp" 35 | #include "views.hpp" 36 | 37 | namespace utki { 38 | 39 | /** 40 | * @brief Array indexed by enumeration. 41 | * @tparam element_type - element type of the array. 42 | * @tparam enum_type - enumeration type which will index the array. 43 | * Must be scoped enum. 44 | * Must define 'enum_size' item as its last item. 45 | */ 46 | // TODO: doxygen all 47 | template 48 | class enum_array : public std::array 49 | { 50 | static_assert(utki::is_scoped_enum_v, "enum_type must be a scoped enumeration type"); 51 | 52 | // check that enum has enum_size item. Note, that this is the only way to do it for MSVC compiler at the moment. 53 | static_assert( 54 | enum_type::enum_size == enum_type::enum_size, 55 | "enum_type must have enum_type::enum_size as its last item" 56 | ); 57 | 58 | public: 59 | using base_type = std::array; 60 | 61 | auto zip_with_enum() 62 | { 63 | return utki::views::zip( 64 | *this, // 65 | utki::enum_iterable_v 66 | ); 67 | } 68 | 69 | auto zip_with_enum() const 70 | { 71 | return utki::views::zip( 72 | *this, // 73 | utki::enum_iterable_v 74 | ); 75 | } 76 | 77 | using base_type::operator[]; 78 | 79 | element_type& operator[](enum_type i) noexcept 80 | { 81 | ASSERT(size_t(i) < this->size()) 82 | return this->operator[](size_t(i)); 83 | } 84 | 85 | const element_type& operator[](enum_type i) const noexcept 86 | { 87 | ASSERT(size_t(i) < this->size()) 88 | return this->operator[](size_t(i)); 89 | } 90 | }; 91 | 92 | }; // namespace utki 93 | -------------------------------------------------------------------------------- /src/utki/enum_iterable.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | #include "type_traits.hpp" 34 | 35 | namespace utki { 36 | 37 | // TODO: doxygen all 38 | template 39 | class enum_iterable 40 | { 41 | static_assert(utki::is_scoped_enum_v, "enum_type must be a scoped enumeration type"); 42 | 43 | // check that enum has enum_size item. Note, that this is the only way to do it for MSVC compiler at the moment. 44 | static_assert( 45 | enum_type::enum_size == enum_type::enum_size, 46 | "enum_type must have enum_type::enum_size as its last item" 47 | ); 48 | 49 | public: 50 | class iterator 51 | { 52 | friend class enum_iterable; 53 | 54 | enum_type e; 55 | 56 | constexpr iterator(enum_type begin) : 57 | e(begin) 58 | {} 59 | 60 | public: 61 | using iterator_category = std::input_iterator_tag; 62 | using difference_type = std::make_signed_t>; 63 | using value_type = enum_type; 64 | using reference = value_type; 65 | using pointer = void; 66 | 67 | constexpr bool operator!=(const iterator& i) const noexcept 68 | { 69 | return this->e != i.e; 70 | } 71 | 72 | constexpr value_type operator*() const noexcept 73 | { 74 | return this->e; 75 | } 76 | 77 | constexpr iterator& operator++() noexcept 78 | { 79 | this->e = enum_type(std::underlying_type_t(this->e) + 1); 80 | return *this; 81 | } 82 | }; 83 | 84 | using const_iterator = iterator; 85 | 86 | using reverse_iterator = std::reverse_iterator; 87 | 88 | constexpr size_t size() const noexcept 89 | { 90 | return size_t(enum_type::enum_size); 91 | } 92 | 93 | constexpr iterator begin() const noexcept 94 | { 95 | return iterator(enum_type(0)); 96 | } 97 | 98 | constexpr iterator end() const noexcept 99 | { 100 | return iterator(enum_type::enum_size); 101 | } 102 | 103 | constexpr iterator cbegin() const noexcept 104 | { 105 | return this->begin(); 106 | } 107 | 108 | constexpr iterator cend() const noexcept 109 | { 110 | return this->end(); 111 | } 112 | 113 | constexpr reverse_iterator crbegin() const 114 | { 115 | return const_reverse_iterator(this->cend()); 116 | } 117 | 118 | constexpr reverse_iterator crend() const 119 | { 120 | return const_reverse_iterator(this->cbegin()); 121 | } 122 | 123 | constexpr reverse_iterator rbegin() const 124 | { 125 | return reverse_iterator(this->end()); 126 | } 127 | 128 | constexpr reverse_iterator rend() const 129 | { 130 | return reverse_iterator(this->begin()); 131 | } 132 | }; 133 | 134 | template 135 | constexpr enum_iterable enum_iterable_v{}; 136 | 137 | } // namespace utki 138 | -------------------------------------------------------------------------------- /src/utki/exception.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #include "exception.hpp" 30 | 31 | #include 32 | 33 | #include "config.hpp" 34 | 35 | #if CFG_COMPILER == CFG_COMPILER_GCC || CFG_COMPILER == CFG_COMPILER_CLANG 36 | # include // for accessing current exception type name 37 | #endif 38 | 39 | #include "debug.hpp" 40 | 41 | using namespace std::string_literals; 42 | 43 | using namespace utki; 44 | 45 | std::string utki::current_exception_to_string(std::string_view indentation) 46 | { 47 | if (!std::current_exception()) { 48 | throw std::logic_error("no current exception"); 49 | } 50 | 51 | std::stringstream ss; 52 | ss << indentation << 53 | #if CFG_COMPILER == CFG_COMPILER_GCC || CFG_COMPILER == CFG_COMPILER_CLANG 54 | demangle(*abi::__cxa_current_exception_type()); 55 | #else 56 | // TODO: For MSVC compiler possible implementation can be found in 57 | // http://www.virjacode.com/papers/RTTI_current_exception_latest.pdf 58 | "unknown exception"s; 59 | #endif 60 | 61 | try { 62 | throw; 63 | } catch (std::nested_exception& nested) { 64 | try { 65 | nested.rethrow_nested(); 66 | } catch (std::exception& e) { 67 | ss << "\n" << to_string(e, indentation); 68 | } catch (...) { 69 | ss << "\n" << current_exception_to_string(indentation); 70 | } 71 | // NOLINTNEXTLINE(bugprone-empty-catch) 72 | } catch (...) { 73 | // do nothing, the exception is already printed 74 | } 75 | 76 | return ss.str(); 77 | } 78 | 79 | std::string utki::to_string(const std::exception& e, std::string_view indentation) 80 | { 81 | std::stringstream ss; 82 | ss << indentation << demangle(typeid(e)) << ": " << e.what(); 83 | 84 | try { 85 | std::rethrow_if_nested(e); 86 | } catch (std::exception& nested) { 87 | ss << "\n" << to_string(nested, indentation); 88 | } catch (...) { 89 | ss << "\n" << current_exception_to_string(indentation); 90 | } 91 | return ss.str(); 92 | } 93 | -------------------------------------------------------------------------------- /src/utki/exception.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "debug.hpp" 36 | #include "string.hpp" 37 | 38 | namespace utki { 39 | 40 | /** 41 | * @brief Convert exception object to human readable string. 42 | * The resulting string can be multiline. 43 | * @param e - exception object to convert to string. 44 | * @param indentation - indentation to use for resulting string. 45 | * @return String describing the exception object. 46 | */ 47 | std::string to_string(const std::exception& e, std::string_view indentation = std::string_view()); 48 | 49 | /** 50 | * @brief Convert currently handled exception object to human readable string. 51 | * The resulting string can be multiline. 52 | * @param indentation - indentation to use for resulting string. 53 | * @return String describing the exception object. 54 | */ 55 | std::string current_exception_to_string(std::string_view indentation = std::string_view()); 56 | 57 | /** 58 | * @brief Generic exception. 59 | */ 60 | class exception : public std::exception 61 | { 62 | public: 63 | /** 64 | * @brief Convert the exception object to human readable string. 65 | * The string can be multiline. 66 | * @param indentation - indentation to use for conversion. 67 | * @return Human readable string describing the exception object. 68 | */ 69 | virtual std::string to_string(std::string_view indentation = std::string_view()) const 70 | { 71 | return utki::to_string(static_cast(*this)); 72 | } 73 | }; 74 | 75 | // forward declaration 76 | template 77 | void throw_with_nested(exception_type exception); 78 | 79 | template 80 | class stacked_exception : public exception 81 | { 82 | static_assert( 83 | std::is_base_of_v, 84 | "nesting exception must be derived from std::exception" 85 | ); 86 | 87 | std::nested_exception nested; 88 | exception_type nesting; 89 | 90 | mutable std::string description; 91 | 92 | stacked_exception(exception_type nesting) : 93 | nesting(std::move(nesting)) 94 | {} 95 | 96 | template 97 | friend void utki::throw_with_nested(same_exception_type exception); 98 | 99 | public: 100 | const char* what() const noexcept override 101 | { 102 | try { 103 | this->description = 104 | utki::cat(std::string_view("exception stack:\n"), this->to_string(std::string_view("- "))); 105 | return this->description.c_str(); 106 | } catch (std::bad_alloc&) { 107 | return "std::bad_alloc"; 108 | } catch (...) { 109 | return "error"; 110 | } 111 | } 112 | 113 | std::string to_string(std::string_view indentation = std::string_view()) const override 114 | { 115 | std::stringstream ss; 116 | ss << indentation << utki::to_string(static_cast(this->nesting)); 117 | 118 | try { 119 | this->nested.rethrow_nested(); 120 | } catch (exception& e) { 121 | ss << "\n" << e.to_string(indentation); 122 | } catch (std::exception& e) { 123 | ss << "\n" << utki::to_string(e, indentation); 124 | } catch (...) { 125 | ss << "\n" << utki::current_exception_to_string(indentation); 126 | } 127 | return ss.str(); 128 | } 129 | }; 130 | 131 | /** 132 | * @brief Throw exception with nested current exception. 133 | * This is a replacement of std::throw_with_nested() which 134 | * prints all nested exceptions information via it's what() member function. 135 | * @param exception - exception to throw. 136 | */ 137 | template 138 | void throw_with_nested(exception_type exception) 139 | { 140 | static_assert( 141 | std::is_base_of_v, 142 | "nesting exception must be derived from std::exception" 143 | ); 144 | 145 | ASSERT( // 146 | std::current_exception() != nullptr, 147 | [](auto& o) { 148 | o << "called not from within 'catch' block"; 149 | } 150 | ) 151 | throw stacked_exception(std::move(exception)); 152 | } 153 | 154 | } // namespace utki 155 | -------------------------------------------------------------------------------- /src/utki/macros.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | /** 32 | * @brief Utility macro for defining macro overloads. 33 | * Allows defining macro overloads for different number of macro arguments. 34 | */ 35 | // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 36 | #define UTKI_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, NAME, ...) NAME 37 | -------------------------------------------------------------------------------- /src/utki/math.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | #include "utility.hpp" 34 | 35 | namespace utki { 36 | 37 | /** 38 | * @brief Get sign of a number. 39 | * Template function which returns the sign of a number. 40 | * General implementation of this template is as easy as: 41 | * @code 42 | * template inline number_type sign(number_type n){ 43 | * return n > 0 ? (1) : (-1); 44 | * } 45 | * @endcode 46 | * @param n - number to get sign of. 47 | * @return -1 if the argument is negative. 48 | * @return 1 if the number is positive. 49 | */ 50 | template 51 | constexpr number_type sign(number_type n) noexcept 52 | { 53 | return n < 0 ? number_type(-1) : number_type(1); 54 | } 55 | 56 | /** 57 | * @brief The pi number constant. 58 | */ 59 | constexpr auto pi = 3.14159265358979323846264338327950288; 60 | 61 | /** 62 | * @brief Two pi constant. 63 | */ 64 | constexpr auto two_pi = 2 * pi; 65 | 66 | /** 67 | * @brief Number of angle degrees in pi radians. 68 | */ 69 | constexpr auto pi_degrees = 180; 70 | 71 | /** 72 | * @brief Convert angle degrees to radians. 73 | * @param deg - angle in degrees. 74 | * @return Angle in radians. 75 | */ 76 | template 77 | constexpr number_type deg_to_rad(number_type deg) noexcept 78 | { 79 | return number_type(pi) * deg / number_type(pi_degrees); 80 | } 81 | 82 | /** 83 | * @brief Convert radians to angle degrees. 84 | * @param rad - angle in radians. 85 | * @return Angle in degrees. 86 | */ 87 | template 88 | constexpr number_type rad_to_deg(number_type rad) noexcept 89 | { 90 | return pi_degrees * rad / number_type(pi); 91 | } 92 | 93 | /** 94 | * @brief Natural logarithm of 2 constant, i.e. ln(2). 95 | */ 96 | constexpr auto log_2 = 0.693147180559945309417232121458; 97 | 98 | /** 99 | * @brief Calculate x^2. 100 | * @param x - value. 101 | * @return x * x. 102 | */ 103 | template 104 | constexpr number_type pow2(number_type x) noexcept 105 | { 106 | return x * x; 107 | } 108 | 109 | /** 110 | * @brief Calculate x^3. 111 | */ 112 | template 113 | constexpr number_type pow3(number_type x) noexcept 114 | { 115 | return pow2(x) * x; 116 | } 117 | 118 | /** 119 | * @brief Calculate x^4. 120 | */ 121 | template 122 | constexpr number_type pow4(number_type x) noexcept 123 | { 124 | return pow2(pow2(x)); 125 | } 126 | 127 | /** 128 | * @brief Calculate x^5. 129 | */ 130 | template 131 | constexpr number_type pow5(number_type x) noexcept 132 | { 133 | return pow4(x) * x; 134 | } 135 | 136 | /** 137 | * @brief Calculate x^6. 138 | */ 139 | template 140 | constexpr number_type pow6(number_type x) noexcept 141 | { 142 | return pow2(pow3(x)); 143 | } 144 | 145 | } // namespace utki 146 | -------------------------------------------------------------------------------- /src/utki/shared.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | #include "config.hpp" 34 | #include "debug.hpp" 35 | #include "shared_ref.hpp" 36 | 37 | namespace utki { 38 | 39 | /** 40 | * @brief Base class for objects managed by std::shared_ptr. 41 | * This class is to be used as virtual base class for cases when shared_from_this() 42 | * is needed in case of multiple inheritance. 43 | */ 44 | class shared : public std::enable_shared_from_this 45 | { 46 | static void* operator new(size_t size) 47 | { 48 | return ::operator new(size); 49 | } 50 | 51 | protected: 52 | shared() = default; 53 | 54 | public: 55 | shared(const shared&) = default; 56 | shared& operator=(const shared&) = default; 57 | 58 | shared(shared&&) = default; 59 | shared& operator=(shared&&) = default; 60 | 61 | virtual ~shared() noexcept = default; 62 | }; 63 | 64 | /** 65 | * @brief Helper function to make weak_ptr out of shared_ptr. 66 | * @param ptr - shared_ptr out of which to make weak_ptr. 67 | * @return std::weak_ptr created from given std::shared_ptr. 68 | */ 69 | template 70 | std::weak_ptr make_weak(const std::shared_ptr& ptr) 71 | { 72 | return std::weak_ptr(ptr); 73 | } 74 | 75 | /** 76 | * @brief Helper function to make weak_ptr out of shared_ref. 77 | * @param ref - shared_ref out of which to make weak_ptr. 78 | * @return std::weak_ptr created from given utki::shared_ref. 79 | */ 80 | template 81 | std::weak_ptr make_weak(const utki::shared_ref& ref) 82 | { 83 | return std::weak_ptr(ref.to_shared_ptr()); 84 | } 85 | 86 | /** 87 | * @brief Helper function to make shared pointer from shared object. 88 | * This function only works for types which are derived from std::enable_shared_from_this 89 | * or from utki::shared. 90 | * This helper function essentially just calls shared_from_this() and then does a dynamic cast 91 | * to the template type if needed. Thus, one does not have to do the dynamic cast manually. 92 | * @return shared reference to the given shared object. 93 | */ 94 | template 95 | utki::shared_ref make_shared_from(object_type& o) 96 | { 97 | static_assert( 98 | std::is_base_of_v || 99 | std::is_base_of_v, object_type>, 100 | "make_shared_from(): argument must be derived from utki::shared or std::enable_shared_from_this" 101 | ); 102 | 103 | if constexpr (std::is_base_of_v) { 104 | return utki::shared_ref(std::dynamic_pointer_cast(o.shared_from_this())); 105 | } else if constexpr (std::is_base_of_v, object_type>) { 106 | return utki::shared_ref(o.shared_from_this()); 107 | } 108 | } 109 | 110 | /** 111 | * @brief Helper function to make weak pointer from shared object. 112 | * This function only works for types which are derived from std::enable_shared_from_this 113 | * or from utki::shared. 114 | * This helper function essentially just calls shared_from_this() and then does a dynamic cast 115 | * to the template type if needed and then converts to weak pointer. 116 | * @return weak pointer to the given shared object. 117 | */ 118 | template 119 | std::weak_ptr make_weak_from(object_type& o) 120 | { 121 | static_assert( 122 | std::is_base_of_v || 123 | std::is_base_of_v, object_type>, 124 | "make_weak_from(): argument must be derived from utki::shared or std::enable_shared_from_this" 125 | ); 126 | 127 | if constexpr (std::is_base_of_v) { 128 | return utki::make_weak(utki::make_shared_from(o)); 129 | } else if constexpr (std::is_base_of_v, object_type>) { 130 | return o.weak_from_this(); 131 | } 132 | } 133 | 134 | } // namespace utki 135 | -------------------------------------------------------------------------------- /src/utki/signal.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | #include 33 | 34 | namespace utki { 35 | 36 | /** 37 | * @brief Signal-slot like callback calling utility. 38 | * Allows connecting many callbacks and call all the connected callbacks with given arguments. 39 | * @tparam args_type - callback argument types pack. 40 | */ 41 | template 42 | class signal 43 | { 44 | public: 45 | /** 46 | * @brief The callback type name. 47 | */ 48 | using callback_type = std::function; 49 | 50 | private: 51 | std::list callbacks; 52 | 53 | public: 54 | /** 55 | * @brief Connection ID. 56 | * The type name of the ID identifying the connection of a callback to the signal. 57 | * The ID can be used to later disconnect the callback from the signal. 58 | */ 59 | using connection = typename decltype(callbacks)::iterator; 60 | 61 | /** 62 | * @brief Connect a callback to the signal. 63 | * @param callback - callback to connect. 64 | * @return Connection ID. 65 | */ 66 | connection connect(callback_type callback) 67 | { 68 | this->callbacks.push_back(std::move(callback)); 69 | return std::prev(this->callbacks.end()); 70 | } 71 | 72 | /** 73 | * @brief Disconnect a callback from the signal. 74 | * @param conn - connection id of the callback to disconnect. 75 | */ 76 | void disconnect(connection conn) noexcept 77 | { 78 | this->callbacks.erase(conn); 79 | } 80 | 81 | /** 82 | * @brief Emit the signal. 83 | * Calls all the connected callbacks with given arguments. 84 | * @param a - arguments pack to pass to each of the connected callbacks. 85 | */ 86 | void emit(args_type... a) 87 | { 88 | for (const auto& c : this->callbacks) { 89 | c(std::forward(a)...); 90 | } 91 | } 92 | 93 | /** 94 | * @brief Get number of callbacks connected. 95 | * @return Number of callbacks connected. 96 | */ 97 | size_t size() const noexcept 98 | { 99 | return this->callbacks.size(); 100 | } 101 | 102 | /** 103 | * @brief Check if no callbacks are connected to the signal. 104 | * @return true if no callbacks are connected to the signal. 105 | * @return false otherwise. 106 | */ 107 | bool empty() const noexcept 108 | { 109 | return this->size() == 0; 110 | } 111 | }; 112 | 113 | } // namespace utki 114 | -------------------------------------------------------------------------------- /src/utki/sort.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | /** 30 | * NOTE: this implementation is taken from this blog post: 31 | * https://tristanbrindle.com/posts/a-more-useful-compile-time-quicksort 32 | */ 33 | 34 | #pragma once 35 | 36 | #include 37 | #include 38 | 39 | #include "config.hpp" 40 | 41 | namespace utki { 42 | 43 | #if CFG_CPP < 20 44 | namespace sort_internal { 45 | 46 | template 47 | constexpr random_iter_type next( 48 | random_iter_type it, 49 | typename std::iterator_traits::difference_type n = 1 50 | ) 51 | { 52 | return it + n; 53 | } 54 | 55 | template 56 | constexpr auto distance(random_iter_type first, random_iter_type last) 57 | { 58 | return last - first; 59 | } 60 | 61 | template 62 | constexpr void iter_swap(forward_iter_1_type a, forward_iter_2_type b) 63 | { 64 | auto temp = std::move(*a); 65 | *a = std::move(*b); 66 | *b = std::move(temp); 67 | } 68 | 69 | template 70 | constexpr input_iter_type find_if_not(input_iter_type first, input_iter_type last, unary_predicate_type q) 71 | { 72 | for (; first != last; ++first) { 73 | if (!q(*first)) { 74 | return first; 75 | } 76 | } 77 | return last; 78 | } 79 | 80 | template 81 | constexpr forward_iter_type partition(forward_iter_type first, forward_iter_type last, unary_predicate_type p) 82 | { 83 | first = utki::sort_internal::find_if_not(first, last, p); 84 | if (first == last) { 85 | return first; 86 | } 87 | 88 | for (forward_iter_type i = utki::sort_internal::next(first); i != last; ++i) { 89 | if (p(*i)) { 90 | utki::sort_internal::iter_swap(i, first); 91 | ++first; 92 | } 93 | } 94 | return first; 95 | } 96 | 97 | template > 98 | constexpr void quick_sort(random_iter_type first, random_iter_type last, comparator_type cmp = comparator_type{}) 99 | { 100 | auto const n = utki::sort_internal::distance(first, last); 101 | if (n <= 1) { 102 | return; 103 | } 104 | auto const pivot = *utki::sort_internal::next(first, n / 2); 105 | auto const middle1 = utki::sort_internal::partition(first, last, [=](auto const& elem) { 106 | return cmp(elem, pivot); 107 | }); 108 | auto const middle2 = utki::sort_internal::partition(middle1, last, [=](auto const& elem) { 109 | return !cmp(pivot, elem); 110 | }); 111 | quick_sort(first, middle1, cmp); // assert(std::is_sorted(first, middle1, cmp)); 112 | quick_sort(middle2, last, cmp); // assert(std::is_sorted(middle2, last, cmp)); 113 | } 114 | 115 | } // namespace sort_internal 116 | #endif // CFG_CPP < 20 117 | 118 | /** 119 | * @brief sort algorithm. 120 | * This is a drop-in replacement for std::sort() to support sorting in compile time for C++17. 121 | * In C++20 the std::sort() is marked constexpr, so this function is not needed for C++20 and later. 122 | * This function is to be used only for sorting in compile time with C++17. 123 | * In all other cases, one should use std::sort(). 124 | * @param first - range begin iterator. 125 | * @param last - range end iterator. 126 | * @param comp - comparator functor. 127 | */ 128 | template > 129 | constexpr void sort(random_iter_type first, random_iter_type last, comparator_type comp = comparator_type{}) 130 | { 131 | #if CFG_CPP >= 20 132 | std::sort(first, last, comp); 133 | #else 134 | utki::sort_internal::quick_sort(first, last, comp); 135 | #endif 136 | } 137 | 138 | } // namespace utki 139 | -------------------------------------------------------------------------------- /src/utki/spin_lock.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | namespace utki { 34 | 35 | /** 36 | * @brief Spin lock. 37 | */ 38 | class spin_lock 39 | { 40 | std::atomic_flag flag = ATOMIC_FLAG_INIT; 41 | 42 | public: 43 | /** 44 | * @brief Acquire spin lock. 45 | */ 46 | void lock() 47 | { 48 | while (this->flag.test_and_set(std::memory_order_acquire)) { 49 | } 50 | } 51 | 52 | /** 53 | * @brief Release spin lock. 54 | */ 55 | void unlock() 56 | { 57 | this->flag.clear(std::memory_order_release); 58 | } 59 | }; 60 | 61 | } // namespace utki 62 | -------------------------------------------------------------------------------- /src/utki/time.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #include "time.hpp" 30 | 31 | #include 32 | 33 | using namespace utki; 34 | 35 | uint32_t utki::get_ticks_ms() 36 | { 37 | return uint32_t( // 38 | std::chrono::duration_cast( // 39 | std::chrono::high_resolution_clock::now().time_since_epoch() 40 | ) 41 | .count() 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/utki/time.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | 33 | namespace utki { 34 | 35 | [[deprecated("use std::milli::den")]] constexpr auto num_millisec_in_sec = 1000; 36 | 37 | /** 38 | * @brief Get system ticks in milliseconds. 39 | * @return System clock ticks in milliseconds. 40 | */ 41 | uint32_t get_ticks_ms(); 42 | 43 | } // namespace utki 44 | -------------------------------------------------------------------------------- /src/utki/types.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | // TODO: this file is DEPRECATED, use instead 32 | 33 | #include "type_traits.hpp" -------------------------------------------------------------------------------- /src/utki/unicode.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "span.hpp" 36 | 37 | namespace utki { 38 | 39 | /** 40 | * @brief Iterator to iterate through utf-8 encoded unicode characters. 41 | */ 42 | class utf8_iterator 43 | { 44 | char32_t c = 0; 45 | const uint8_t* p = nullptr; 46 | const uint8_t* end = nullptr; 47 | 48 | public: 49 | /** 50 | * @brief Create undefined iterator. 51 | */ 52 | utf8_iterator() = default; 53 | 54 | utf8_iterator(utki::span str); 55 | 56 | /** 57 | * @brief Create iterator pointing to the begin of the given utf-8 encoded string. 58 | * @param str - pointer to the null-terminated utf-8 encoded string. 59 | */ 60 | utf8_iterator(const char* str); 61 | 62 | /** 63 | * @brief Get current unicode character. 64 | * @return unicode value of the character this interator is currently pointing to. 65 | */ 66 | char32_t character() const noexcept 67 | { 68 | return this->c; 69 | } 70 | 71 | // no operator*() because it usually returns reference, don't want to break this contract. 72 | 73 | /** 74 | * @brief Prefix increment. 75 | * Move iterator to the next character in the string. 76 | * If iterator points to the end of the string before this operation then the result of this operation is undefined. 77 | * @return reference to this iterator object. 78 | */ 79 | utf8_iterator& operator++() noexcept; 80 | 81 | // no postfix ++ operator, there is no need in it. 82 | 83 | /** 84 | * @brief Check if iterator points to the end of the string. 85 | * @return true if iterator points to the end of the string. 86 | * @return false otherwise. 87 | */ 88 | bool is_end() const noexcept 89 | { 90 | return this->c == 0; 91 | } 92 | }; 93 | 94 | /** 95 | * @brief Convert UTF-8 to UTF-32. 96 | * @param str - string to convert. 97 | * @return UTF-32 string. 98 | */ 99 | std::u32string to_utf32(utf8_iterator str); 100 | 101 | /** 102 | * @brief Convert UTF-8 to UTF-32. 103 | * @param str - string to convert. 104 | * @return UTF-32 string. 105 | */ 106 | inline std::u32string to_utf32(const char* str) 107 | { 108 | return to_utf32(utf8_iterator(str)); 109 | } 110 | 111 | /** 112 | * @brief Convert UTF-8 to UTF-32. 113 | * @param str - string to convert. 114 | * @return UTF-32 string. 115 | */ 116 | inline std::u32string to_utf32(const std::string& str) 117 | { 118 | return to_utf32(str.c_str()); 119 | } 120 | 121 | /** 122 | * @brief Convert UTF-8 to UTF-32. 123 | * @param str - string to convert. 124 | * @return UTF-32 string. 125 | */ 126 | std::u32string to_utf32(utki::span str); 127 | 128 | /** 129 | * @brief Convert UTF-8 to UTF-32. 130 | * @param str - string to convert. 131 | * @return UTF-32 string. 132 | */ 133 | inline std::u32string to_utf32(utki::span str) 134 | { 135 | static_assert(sizeof(char) == sizeof(uint8_t), "unexpected char size"); 136 | return to_utf32(to_uint8_t(str)); 137 | } 138 | 139 | /** 140 | * @brief Convert UTF-8 to UTF-32. 141 | * @param str - string to convert. 142 | * @return UTF-32 string. 143 | */ 144 | inline std::u32string to_utf32(std::string_view str) 145 | { 146 | return to_utf32(utki::make_span(str)); 147 | } 148 | 149 | constexpr unsigned max_size_of_utf8_encoded_character = 6; 150 | 151 | /** 152 | * @brief Convert UTF-32 character to UTF-8. 153 | * @param c - character to convert. 154 | * @return Zero-terminated array of bytes representing a UTF-8 character. 155 | */ 156 | std::array to_utf8(char32_t c); 157 | 158 | /** 159 | * @brief Convert UTF-32 string to UTF-8 string. 160 | * @param str - UTF-32 string to convert. 161 | * @return UTF-8 string. 162 | */ 163 | std::string to_utf8(utki::span str); 164 | 165 | /** 166 | * @brief Convert UTF-32 string to UTF-8 string. 167 | * @param str - UTF-32 string to convert. 168 | * @return UTF-8 string. 169 | */ 170 | inline std::string to_utf8(const std::u32string& str) 171 | { 172 | return to_utf8(utki::make_span(str)); 173 | } 174 | 175 | } // namespace utki 176 | -------------------------------------------------------------------------------- /src/utki/util.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | // TODO: this file is DEPRECATED, use instead 32 | 33 | #include "utility.hpp" 34 | -------------------------------------------------------------------------------- /src/utki/utility.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #include "utility.hpp" 30 | 31 | #include "config.hpp" 32 | 33 | #if CFG_OS == CFG_OS_WINDOWS 34 | # include 35 | #else 36 | # include 37 | #endif 38 | 39 | using namespace utki; 40 | 41 | bool utki::is_terminal_cerr() 42 | { 43 | #if CFG_OS == CFG_OS_WINDOWS 44 | // need to compare result with 0 to avoid MSVC compiler warning 45 | return _isatty(_fileno(stderr)) != 0; 46 | #else 47 | return isatty(fileno(stderr)); 48 | #endif 49 | } 50 | 51 | bool utki::is_terminal_cout() 52 | { 53 | #if CFG_OS == CFG_OS_WINDOWS 54 | // need to compare result with 0 to avoid MSVC compiler warning 55 | return _isatty(_fileno(stdout)) != 0; 56 | #else 57 | return isatty(fileno(stdout)); 58 | #endif 59 | } 60 | 61 | bool utki::is_terminal_cin() 62 | { 63 | #if CFG_OS == CFG_OS_WINDOWS 64 | // need to compare result with 0 to avoid MSVC compiler warning 65 | return _isatty(_fileno(stdin)) != 0; 66 | #else 67 | return isatty(fileno(stdin)); 68 | #endif 69 | } 70 | -------------------------------------------------------------------------------- /src/utki/variant.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include 32 | #include 33 | 34 | #include "config.hpp" 35 | 36 | namespace utki { 37 | 38 | template 39 | struct internal_helper_tag {}; 40 | 41 | /** 42 | * @brief Get variant's alternative index by its type in compile time. 43 | * @tparam indexed_type - type to get index of. 44 | * @tparam variant_type - std::variant type to get index from. 45 | */ 46 | template 47 | struct index_of; 48 | 49 | // MSVC compiler prior to tools v142 doesn't compile this 50 | #if CFG_COMPILER != CFG_COMPILER_MSVC || CFG_COMPILER_MSVC_TOOLS_V >= 142 51 | template 52 | struct index_of> : 53 | std::integral_constant< 54 | size_t, // 55 | std::variant...>(internal_helper_tag()).index() // 56 | > // 57 | {}; 58 | #endif 59 | 60 | /** 61 | * @brief Value alias for index_of. 62 | */ 63 | template 64 | constexpr size_t index_of_v = index_of::value; 65 | 66 | /** 67 | * @brief Functor overloads holder. 68 | * This is a helper template to be used along with std::visit. 69 | * Example: 70 | * @code 71 | * std::variant var; 72 | * 73 | * // ... 74 | * 75 | * std::visit( 76 | * overloaded{ 77 | * [](auto arg) { 78 | * // template argument overload 79 | * }, 80 | * [](double arg) { 81 | * std::cout << "variant stored value is double: " << arg; 82 | * }, 83 | * [](const std::string& arg) { 84 | * std::cout << "variant stored value is std::string: " arg; 85 | * } 86 | * }, 87 | * var 88 | * ); 89 | * @endcode 90 | */ 91 | template 92 | struct overloaded : functor_type... { 93 | using functor_type::operator()...; 94 | }; 95 | 96 | #if CFG_CPP < 20 97 | // explicit deduction guide (not needed as of C++20) 98 | template 99 | overloaded(functor_type...) -> overloaded; 100 | #endif 101 | 102 | } // namespace utki 103 | -------------------------------------------------------------------------------- /src/utki/views.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include "utility.hpp" 32 | 33 | namespace utki { 34 | /** 35 | * @brief Not to be used directly. 36 | * Use skip_front() function. 37 | */ 38 | template 39 | struct skip_front_collection_wrapper { 40 | collection_type& collection; 41 | 42 | skip_front_collection_wrapper(collection_type& collection) : 43 | collection(collection) 44 | {} 45 | 46 | auto begin() 47 | { 48 | ASSERT(this->collection.size() >= num_to_skip) 49 | using std::begin; 50 | return utki::next(begin(this->collection), num_to_skip); 51 | } 52 | 53 | auto end() 54 | { 55 | using std::end; 56 | return end(this->collection); 57 | } 58 | }; 59 | 60 | /** 61 | * @brief Create collection wrapper which effectively skips first elements. 62 | * @tparam num_to_skip - number of front elements to skip. 63 | * @tparam collection_type - collection type. 64 | * @param collection - collection to skip first elements of. 65 | * @return A special wrapper class which provides begin() and end() methods 66 | * returning collection iterators. 67 | */ 68 | // TODO: rename to utki::views::drop as in std:: 69 | template 70 | auto skip_front(collection_type& collection) 71 | { 72 | return skip_front_collection_wrapper(collection); 73 | } 74 | } // namespace utki 75 | 76 | namespace utki::views { 77 | 78 | /** 79 | * @brief Not to be used directly. 80 | * Use utki::views::reverse() function. 81 | */ 82 | template 83 | struct reverse_view { 84 | collection_type& collection; 85 | 86 | reverse_view(collection_type& collection) : 87 | collection(collection) 88 | {} 89 | 90 | auto begin() 91 | { 92 | using std::rbegin; 93 | return rbegin(this->collection); 94 | } 95 | 96 | auto end() 97 | { 98 | using std::rend; 99 | return rend(this->collection); 100 | } 101 | }; 102 | 103 | /** 104 | * @brief Drop-in replacement for std::views::reverse. 105 | * The reverse_view returns reverse iterators for begin() and end(). 106 | * @tparam collection_type - collection type. 107 | * @param collection - collection with reversed elements order. 108 | * @return A special wrapper class which provides begin() and end() methods 109 | * returning collection iterators. 110 | */ 111 | template 112 | auto reverse(collection_type& collection) 113 | { 114 | return reverse_view(collection); 115 | } 116 | 117 | /** 118 | * @brief Drop-in replacement for std::ranges::zip_view from C++23. 119 | * C++17 compatible. 120 | */ 121 | template 122 | struct zip_view { 123 | std::tuple collection; 124 | 125 | size_t min_size; 126 | 127 | zip_view(collection_type&... collection) : 128 | collection(std::tuple(collection...)), 129 | min_size(std::min({collection.size()...})) 130 | {} 131 | 132 | class iterator 133 | { 134 | std::tuple< // 135 | std::conditional_t< 136 | std::is_const_v, 137 | typename collection_type::const_iterator, 138 | typename collection_type::iterator>...> 139 | iters; 140 | 141 | public: 142 | iterator(decltype(iters) iters) : 143 | iters(iters) 144 | {} 145 | 146 | bool operator!=(const iterator& i) const noexcept 147 | { 148 | return std::get<0>(iters) != std::get<0>(i.iters); 149 | } 150 | 151 | iterator& operator++() noexcept 152 | { 153 | this->iters = std::apply( 154 | [](auto&... i) { 155 | return std::make_tuple(std::next(i)...); 156 | }, 157 | this->iters 158 | ); 159 | 160 | return *this; 161 | } 162 | 163 | auto operator*() noexcept 164 | { 165 | return std::apply( 166 | [](auto&... i) { 167 | return std::tuple((*i)...); 168 | }, 169 | this->iters 170 | ); 171 | } 172 | }; 173 | 174 | auto begin() 175 | { 176 | return iterator( 177 | std::apply( 178 | [](auto&... c) { 179 | return std::make_tuple([&]() { 180 | if constexpr (std::is_const_v>) { 181 | return c.cbegin(); 182 | } else { 183 | return c.begin(); 184 | } 185 | }()...); 186 | }, 187 | this->collection 188 | ) 189 | ); 190 | } 191 | 192 | auto end() 193 | { 194 | return iterator( 195 | std::apply( 196 | [this](auto&... c) { 197 | return std::make_tuple(utki::next(c.begin(), this->min_size)...); 198 | }, 199 | this->collection 200 | ) 201 | ); 202 | } 203 | }; 204 | 205 | /** 206 | * @brief Drop-in replacement for std::views::zip from C++23. 207 | * C++17 compatible. 208 | */ 209 | template 210 | zip_view zip(collection_type&... collection) 211 | { 212 | return zip_view(collection...); 213 | } 214 | 215 | } // namespace utki::views 216 | -------------------------------------------------------------------------------- /src/utki/windows.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | utki - Utility Kit for C++. 5 | 6 | Copyright (c) 2015-2025 Ivan Gagis 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | /* ================ LICENSE END ================ */ 28 | 29 | #pragma once 30 | 31 | #include "config.hpp" 32 | 33 | #if CFG_OS == CFG_OS_WINDOWS 34 | 35 | // if _WINSOCKAPI_ macro is not defined then it means that the winsock header file 36 | // has not been included. Here we temporarily define the macro in order to prevent 37 | // inclusion of winsock.h from within the windows.h. Because it may later conflict with 38 | // winsock2.h if it is included later. 39 | # ifndef _WINSOCKAPI_ 40 | # define _WINSOCKAPI_ 1234567890 41 | # endif 42 | 43 | # ifndef _WINSOCK_H 44 | # define _WINSOCK_H 1234567890 45 | # endif 46 | 47 | # include 48 | 49 | # if (_WINSOCKAPI_ + 0) // if defined and is not empty 50 | # if _WINSOCKAPI_ == 1234567890 51 | # undef _WINSOCKAPI_ 52 | # endif 53 | # endif 54 | 55 | # if (_WINSOCK_H + 0) // if defined and is not empty 56 | # if _WINSOCK_H == 1234567890 57 | # undef _WINSOCK_H 58 | # endif 59 | # endif 60 | 61 | # ifdef max 62 | # undef max 63 | # endif 64 | 65 | # ifdef min 66 | # undef min 67 | # endif 68 | 69 | # ifdef DELETE 70 | # undef DELETE 71 | # endif 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src_deps/fast_float/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 The fast_float authors 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /src_deps/fast_float/README.adoc: -------------------------------------------------------------------------------- 1 | fast_float library version 6.4.1 2 | 3 | https://github.com/fastfloat/fast_float 4 | -------------------------------------------------------------------------------- /tests/.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: | 2 | -cppcoreguidelines-avoid-magic-numbers, 3 | -clang-analyzer-cplusplus.Move, 4 | -modernize-use-designated-initializers 5 | InheritParentConfig: true 6 | -------------------------------------------------------------------------------- /tests/harness/makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | 3 | $(eval $(call prorab-include, ../../src/makefile)) 4 | 5 | clargs_cfg := rel_no_install 6 | tst_cfg := no_par_no_install 7 | 8 | # load config in order to resolve 'c' and 'config' in case of default config 9 | $(eval $(call prorab-config, ../../config)) 10 | 11 | $(eval $(call prorab-depend, \ 12 | clargs/src/out/$(clargs_cfg)/libclargs$(dot_so), \ 13 | ../../src/out/$(c)/libutki$(this_dbg)$(dot_so) \ 14 | )) 15 | $(eval $(call prorab-depend, \ 16 | tst/src/out/$(tst_cfg)/libtst$(dot_so), \ 17 | clargs/src/out/$(clargs_cfg)/libclargs$(dot_so) \ 18 | ../../src/out/$(c)/libutki$(this_dbg)$(dot_so) \ 19 | )) 20 | 21 | harness_old_cxxflags := $(CXXFLAGS) 22 | harness_old_ldflags := $(LDFLAGS) 23 | CXXFLAGS += -I$(d)../../src -I$(d)clargs/src 24 | LDFLAGS += -L$(d)../../src/out/$(c) -L$(d)clargs/src/out/$(clargs_cfg) 25 | $(eval $(call prorab-include, clargs/src/makefile, $(clargs_cfg))) 26 | $(eval $(call prorab-include, tst/src/makefile, $(tst_cfg))) 27 | CXXFLAGS := $(harness_old_cxxflags) 28 | LDFLAGS := $(harness_old_ldflags) 29 | -------------------------------------------------------------------------------- /tests/makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | 3 | $(eval $(prorab-include-subdirs)) 4 | -------------------------------------------------------------------------------- /tests/singleton_over_shared_library/makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | include prorab-test.mk 3 | 4 | this_name := testso 5 | this_soname := 0 6 | 7 | $(eval $(call prorab-config, ../../config)) 8 | 9 | this_srcs += testso.cpp 10 | 11 | this_no_install := true 12 | 13 | this_ldflags += -L $(d)../../src/out/$(c) 14 | this_ldlibs += -l utki$(this_dbg) 15 | 16 | $(eval $(prorab-build-lib)) 17 | 18 | lib_target_name := $(prorab_this_name) 19 | 20 | $(prorab_this_name): $(abspath $(d)../../src/out/$(c)/libutki$(this_dbg)$(dot_so)) 21 | 22 | #================================ 23 | $(eval $(prorab-clear-this-vars)) 24 | #================================ 25 | 26 | this_name := tests 27 | 28 | $(eval $(call prorab-config, ../../config)) 29 | 30 | this_srcs += singleton_test.cpp 31 | 32 | this_ldlibs += $(lib_target_name) 33 | 34 | this_no_install := true 35 | 36 | this_ldflags += -L $(d)../../src/out/$(c) 37 | this_ldlibs += -l utki$(this_dbg) 38 | 39 | $(eval $(prorab-build-app)) 40 | 41 | $(prorab_this_name): $(lib_target_name) 42 | 43 | $(eval $(call prorab-depend, $(prorab_this_name), ../../src/out/$(c)/libutki$(this_dbg)$(dot_so))) 44 | 45 | # no test under windows, it fails there 46 | ifneq ($(os),windows) 47 | this_test_cmd := $(prorab_this_name) 48 | this_test_deps := $(prorab_this_name) 49 | this_test_ld_path := out/$(c) ../../src/out/$(c) 50 | $(eval $(prorab-test)) 51 | endif 52 | 53 | $(eval $(call prorab-include, ../../src/makefile)) 54 | -------------------------------------------------------------------------------- /tests/singleton_over_shared_library/singleton_test.cpp: -------------------------------------------------------------------------------- 1 | #include "test_singleton.hpp_" 2 | #include "testso.hpp_" 3 | 4 | #include "../../src/utki/debug.hpp" 5 | 6 | // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) 7 | 8 | // NOLINTNEXTLINE(bugprone-exception-escape): global exceptions are not caught 9 | int main(int argc, char *argv[]){ 10 | test_singleton ts; 11 | 12 | utki::assert(test_singleton::inst().a == 32, SL); 13 | 14 | ++test_singleton::inst().a; 15 | 16 | utki::assert(test_singleton::inst().a == 33, SL); 17 | 18 | inc_a(); 19 | 20 | utki::assert(test_singleton::inst().a == 34, SL); 21 | utki::assert(ts.a == 34, SL); 22 | 23 | test_singleton::inst().a = 101; 24 | 25 | utki::assert(get_a() == 101, SL); 26 | 27 | get_a() = 145; 28 | 29 | utki::assert(ts.a == 145, SL); 30 | utki::assert(test_singleton::inst().a == 145, SL); 31 | 32 | return 0; 33 | } 34 | 35 | // NOLINTEND(cppcoreguidelines-avoid-magic-numbers) 36 | -------------------------------------------------------------------------------- /tests/singleton_over_shared_library/test_singleton.hpp_: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../src/utki/singleton.hpp" 4 | 5 | class test_singleton : public utki::singleton{ 6 | public: 7 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) 8 | int a{32}; 9 | 10 | test_singleton() = default; 11 | ~test_singleton()noexcept override = default; 12 | 13 | test_singleton(const test_singleton&) = delete; 14 | test_singleton& operator=(const test_singleton&) = delete; 15 | 16 | test_singleton(test_singleton&&) = delete; 17 | test_singleton& operator=(test_singleton&&) = delete; 18 | }; 19 | -------------------------------------------------------------------------------- /tests/singleton_over_shared_library/testso.cpp: -------------------------------------------------------------------------------- 1 | #include "testso.hpp_" 2 | #include "test_singleton.hpp_" 3 | 4 | #include "../../src/utki/debug.hpp" 5 | 6 | int& get_a(){ 7 | utki::assert(test_singleton::is_created(), SL); 8 | return test_singleton::inst().a; 9 | } 10 | 11 | void inc_a(){ 12 | utki::assert(test_singleton::is_created(), SL); 13 | ++(test_singleton::inst().a); 14 | } 15 | 16 | void print_a(){ 17 | utki::assert(test_singleton::is_created(), SL); 18 | utki::log([](auto&o){o << "PrintA(): a = " << test_singleton::inst().a << std::endl;}); 19 | } 20 | -------------------------------------------------------------------------------- /tests/singleton_over_shared_library/testso.hpp_: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | int& get_a(); 5 | void inc_a(); 6 | void print_a(); 7 | -------------------------------------------------------------------------------- /tests/unit/makefile: -------------------------------------------------------------------------------- 1 | include prorab.mk 2 | include prorab-test.mk 3 | include prorab-clang-format.mk 4 | 5 | $(eval $(call prorab-include, ../../src/makefile)) 6 | $(eval $(call prorab-include, ../harness/makefile)) 7 | 8 | $(eval $(call prorab-config, ../../config)) 9 | 10 | this_no_install := true 11 | 12 | this_name := tests 13 | 14 | this_srcs := $(call prorab-src-dir, src) 15 | 16 | this_libutki := ../../src/out/$(c)/libutki$(this_dbg)$(dot_so) 17 | 18 | this_harness_ld_paths := $(abspath $(d)../harness/tst/src/out/$(tst_cfg) $(d)../harness/clargs/src/out/$(clargs_cfg)) 19 | 20 | this_cxxflags += -isystem ../harness/tst/src -isystem ../../src 21 | 22 | this_ldflags += $(addprefix -L,$(this_harness_ld_paths)) 23 | 24 | this_ldlibs += $(this_libutki) 25 | this_ldlibs += -l tst 26 | this_ldlibs += -l clargs 27 | this_ldlibs += -l m 28 | 29 | $(eval $(prorab-build-app)) 30 | 31 | $(eval $(call prorab-depend, $(prorab_this_name), $(this_libutki) ../harness/tst/src/out/$(tst_cfg)/libtst$(dot_so))) 32 | 33 | this_test_cmd := $(prorab_this_name) --junit-out=$(dir $(prorab_this_name))/junit.xml 34 | this_test_deps := $(prorab_this_name) 35 | this_test_ld_path := $(dir $(this_libutki)) $(this_harness_ld_paths) 36 | $(eval $(prorab-test)) 37 | 38 | this_src_dir := src 39 | $(eval $(prorab-clang-format)) 40 | -------------------------------------------------------------------------------- /tests/unit/src/base64.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::string_view_literals; 6 | 7 | namespace { 8 | const tst::set set("base64", [](tst::suite& suite) { 9 | suite.add>>( // 10 | "samples", 11 | { 12 | { ""sv, {}}, 13 | { "SA=="sv, {'H'}}, 14 | { "SA"sv, {'H'}}, 15 | { "SGU="sv, {'H', 'e'}}, 16 | { "SGU"sv, {'H', 'e'}}, 17 | { "SGVs"sv, {'H', 'e', 'l'}}, 18 | { "SGVsbA=="sv, {'H', 'e', 'l', 'l'}}, 19 | { "SGVsbA"sv, {'H', 'e', 'l', 'l'}}, 20 | { "SGVsbG8="sv, {'H', 'e', 'l', 'l', 'o'}}, 21 | { "SGVsbG8"sv, {'H', 'e', 'l', 'l', 'o'}}, 22 | { "SGVsbG8g"sv, {'H', 'e', 'l', 'l', 'o', ' '}}, 23 | { "SGVsbG8gdw=="sv, {'H', 'e', 'l', 'l', 'o', ' ', 'w'}}, 24 | { "SGVsbG8gdw"sv, {'H', 'e', 'l', 'l', 'o', ' ', 'w'}}, 25 | { "SGVsbG8gd28="sv, {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o'}}, 26 | { "SGVsbG8gd28"sv, {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o'}}, 27 | { "SGVsbG8gd29y"sv, {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r'}}, 28 | {"SGVsbG8gd29ybGQh"sv, {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'}} 29 | }, 30 | [](const auto& p) { 31 | auto res = utki::base64_decode(p.first); 32 | tst::check(res == p.second, SL); 33 | } 34 | ); 35 | }); 36 | } // namespace 37 | -------------------------------------------------------------------------------- /tests/unit/src/config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | const tst::set set("config", [](tst::suite& suite) { 7 | suite.add("endianness", []() { 8 | unsigned integer = 1; 9 | static_assert(sizeof(integer) >= 2, "1 byte integer detected"); 10 | // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 11 | auto p = reinterpret_cast(&integer); 12 | 13 | bool is_little_endian = (*p == 1); 14 | 15 | if (is_little_endian) { 16 | tst::check(CFG_ENDIANNESS == CFG_ENDIANNESS_LITTLE, SL) << "CFG_ENDIANNESS = " << CFG_ENDIANNESS; 17 | tst::check(CFG_ENDIANNESS != CFG_ENDIANNESS_BIG, SL) << "CFG_ENDIANNESS = " << CFG_ENDIANNESS; 18 | } else { 19 | tst::check(CFG_ENDIANNESS == CFG_ENDIANNESS_BIG, SL) << "CFG_ENDIANNESS = " << CFG_ENDIANNESS; 20 | tst::check(CFG_ENDIANNESS != CFG_ENDIANNESS_LITTLE, SL) << "CFG_ENDIANNESS = " << CFG_ENDIANNESS; 21 | } 22 | }); 23 | }); 24 | } // namespace 25 | -------------------------------------------------------------------------------- /tests/unit/src/debug.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // undefine possibly defined macro 6 | #ifdef assert 7 | # undef assert 8 | #endif 9 | 10 | using namespace std::string_literals; 11 | 12 | class test_class 13 | {}; 14 | 15 | namespace { 16 | const tst::set set("debug", [](tst::suite& suite) { 17 | suite.add("assert_macro", []() { 18 | int a = 13; 19 | ASSERT(a == 13) 20 | ASSERT(sizeof(int) == 4) 21 | ASSERT(sizeof(a) == 4 && sizeof(a) == sizeof(int)) 22 | a += 4; 23 | utki::assert(a == 17, SL); 24 | }); 25 | 26 | suite.add("assert_macro_with_message", []() { 27 | int a = 13; 28 | ASSERT(a == 13, [&](auto& o) { 29 | o << "a is not 13, a is " << a; 30 | }) 31 | a += 4; 32 | utki::assert(a == 17, SL); 33 | }); 34 | 35 | suite.add("assert", []() { 36 | int b = 13; 37 | utki::assert(b == 13, SL); 38 | #if CFG_CPP >= 20 39 | utki::assert(b == 13); 40 | #endif 41 | }); 42 | 43 | suite.add("assert_with_message", []() { 44 | int b = 13; 45 | utki::assert( 46 | b == 13, 47 | [&](auto& o) { 48 | o << "b = " << b; 49 | }, 50 | SL 51 | ); 52 | #if CFG_CPP >= 20 53 | utki::assert(b == 13, [&](auto& o) { 54 | o << "b = " << b; 55 | }); 56 | #endif 57 | }); 58 | 59 | suite.add("demangle__class", []() { 60 | test_class a; 61 | 62 | auto name = utki::demangle(typeid(a)); 63 | 64 | auto expected = "test_class"s; 65 | 66 | tst::check_eq(name, expected, SL); 67 | }); 68 | 69 | suite.add("demangle__unsigned", []() { 70 | unsigned a = 0; 71 | 72 | auto name = utki::demangle(typeid(a)); 73 | 74 | auto expected = "unsigned int"s; 75 | 76 | tst::check_eq(name, expected, SL); 77 | }); 78 | }); 79 | } // namespace 80 | -------------------------------------------------------------------------------- /tests/unit/src/destructable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | const tst::set set("destructable", [](tst::suite& suite) { 7 | suite.add("virtual_destructor", []() { 8 | class test : public utki::destructable 9 | { 10 | public: 11 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) 12 | bool& destructor_called; 13 | 14 | test(bool& destructor_called) : 15 | destructor_called(destructor_called) 16 | {} 17 | 18 | test(const test&) = delete; 19 | test& operator=(const test&) = delete; 20 | 21 | test(test&&) = delete; 22 | test& operator=(test&&) = delete; 23 | 24 | ~test() override 25 | { 26 | this->destructor_called = true; 27 | } 28 | }; 29 | 30 | bool destructor_called = false; 31 | std::unique_ptr p = std::make_unique(destructor_called); 32 | 33 | tst::check(!destructor_called, SL); 34 | 35 | p.reset(); 36 | 37 | tst::check(destructor_called, SL); 38 | }); 39 | }); 40 | } // namespace 41 | -------------------------------------------------------------------------------- /tests/unit/src/enum_array.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::string_literals; 6 | 7 | namespace { 8 | const tst::set set("enum_array", [](tst::suite& suite) { 9 | suite.add("zip_with_enum", []() { 10 | enum class en { 11 | a, 12 | b, 13 | c, 14 | d, 15 | 16 | enum_size 17 | }; 18 | 19 | utki::enum_array arr = {"a", "b", "c", "d"}; 20 | 21 | tst::check_eq(arr.size(), size_t(en::enum_size), SL); 22 | 23 | std::vector> actual; 24 | 25 | const std::vector> expected = { 26 | {"a", en::a}, 27 | {"b", en::b}, 28 | {"c", en::c}, 29 | {"d", en::d}, 30 | }; 31 | 32 | for (auto [v, e] : arr.zip_with_enum()) { 33 | actual.emplace_back(v, e); 34 | 35 | // modify original array contents 36 | v.append("!"); 37 | } 38 | 39 | tst::check(actual == expected, SL); 40 | 41 | for (const auto& i : arr) { 42 | tst::check_eq(i.size(), size_t(2), SL); 43 | tst::check_eq(i.back(), '!', SL); 44 | } 45 | }); 46 | 47 | suite.add("zip_with_enum__const", []() { 48 | enum class en { 49 | a, 50 | b, 51 | c, 52 | d, 53 | 54 | enum_size 55 | }; 56 | 57 | const utki::enum_array arr = {"a", "b", "c", "d"}; 58 | 59 | tst::check_eq(arr.size(), size_t(en::enum_size), SL); 60 | 61 | std::vector> actual; 62 | 63 | const std::vector> expected = { 64 | {"a", en::a}, 65 | {"b", en::b}, 66 | {"c", en::c}, 67 | {"d", en::d}, 68 | }; 69 | 70 | for (auto [v, e] : arr.zip_with_enum()) { 71 | actual.emplace_back(v, e); 72 | } 73 | 74 | tst::check(actual == expected, SL); 75 | }); 76 | 77 | suite.add("operator_subscript", []() { 78 | enum class en { 79 | a, 80 | b, 81 | c, 82 | d, 83 | 84 | enum_size 85 | }; 86 | 87 | utki::enum_array arr = {"a", "b", "c", "d"}; 88 | 89 | tst::check_eq(arr[0], "a"s, SL); 90 | tst::check_eq(arr[1], "b"s, SL); 91 | tst::check_eq(arr[2], "c"s, SL); 92 | tst::check_eq(arr[3], "d"s, SL); 93 | 94 | tst::check_eq(arr[en::a], "a"s, SL); 95 | tst::check_eq(arr[en::b], "b"s, SL); 96 | tst::check_eq(arr[en::c], "c"s, SL); 97 | tst::check_eq(arr[en::d], "d"s, SL); 98 | 99 | arr[en::a].append("!"); 100 | 101 | tst::check_eq(arr[en::a], "a!"s, SL); 102 | }); 103 | 104 | suite.add("operator_subscript__const", []() { 105 | enum class en { 106 | a, 107 | b, 108 | c, 109 | d, 110 | 111 | enum_size 112 | }; 113 | 114 | const utki::enum_array arr = {"a", "b", "c", "d"}; 115 | 116 | tst::check_eq(arr[0], "a"s, SL); 117 | tst::check_eq(arr[1], "b"s, SL); 118 | tst::check_eq(arr[2], "c"s, SL); 119 | tst::check_eq(arr[3], "d"s, SL); 120 | 121 | tst::check_eq(arr[en::a], "a"s, SL); 122 | tst::check_eq(arr[en::b], "b"s, SL); 123 | tst::check_eq(arr[en::c], "c"s, SL); 124 | tst::check_eq(arr[en::d], "d"s, SL); 125 | }); 126 | }); 127 | } // namespace 128 | -------------------------------------------------------------------------------- /tests/unit/src/enum_iterable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | const tst::set set("enum_iterable", [](tst::suite& suite) { 7 | enum class test_enum { 8 | item1, 9 | item2, 10 | item3, 11 | 12 | enum_size 13 | }; 14 | 15 | suite.add("enum_iterable_v", []() { 16 | unsigned count = 0; 17 | 18 | for (auto i : utki::enum_iterable_v) { 19 | tst::check_eq(unsigned(i), count, SL); 20 | ++count; 21 | } 22 | 23 | tst::check_eq(count, unsigned(3), SL); 24 | }); 25 | }); 26 | } // namespace 27 | -------------------------------------------------------------------------------- /tests/unit/src/exception.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::string_literals; 7 | using namespace std::string_view_literals; 8 | 9 | namespace { 10 | class some_unknown_error 11 | {}; 12 | } // namespace 13 | 14 | namespace { 15 | const tst::set set("exception", [](tst::suite& suite) { 16 | suite.add("to_string__std_exception", []() { 17 | std::string s; 18 | try { 19 | throw std::logic_error("some logic error"); 20 | } catch (std::exception& e) { 21 | s = utki::to_string(e); 22 | } 23 | 24 | tst::check_eq(s, "std::logic_error: some logic error"s, SL); 25 | }); 26 | 27 | suite.add("to_string__std_nested_exception", []() { 28 | std::string s; 29 | try { 30 | try { 31 | try { 32 | try { 33 | throw std::logic_error("some logic error"); 34 | } catch (...) { 35 | std::throw_with_nested(std::runtime_error("some_runtime_error")); 36 | } 37 | } catch (...) { 38 | std::throw_with_nested(some_unknown_error()); 39 | } 40 | } catch (...) { 41 | std::throw_with_nested(std::invalid_argument("some argument is invalid")); 42 | } 43 | } catch (std::exception& e) { 44 | s = utki::to_string(e, " "sv); 45 | } 46 | 47 | auto actual = utki::split(s, '\n'); 48 | 49 | constexpr size_t expected_size = 4; 50 | 51 | tst::check_eq(actual.size(), expected_size, SL); 52 | 53 | #if CFG_COMPILER == CFG_COMPILER_MSVC 54 | #else 55 | auto expected_prefix = " std::"sv; 56 | 57 | std::vector expected = { 58 | ": some argument is invalid"s, 59 | "<(anonymous namespace)::some_unknown_error>"s, 60 | ": some_runtime_error"s, 61 | " std::logic_error: some logic error"s 62 | }; 63 | 64 | for (auto ei = expected.begin(), ai = actual.begin(); ei != expected.end(); ++ei, ++ai) { 65 | tst::check(utki::starts_with(*ai, expected_prefix), SL); 66 | tst::check(utki::ends_with(*ai, *ei), SL); 67 | } 68 | #endif 69 | }); 70 | 71 | suite.add("current_exception_to_string", []() { 72 | std::string s; 73 | 74 | try { 75 | throw some_unknown_error(); 76 | } catch (...) { 77 | s = utki::current_exception_to_string(" "sv); 78 | } 79 | 80 | #if CFG_COMPILER == CFG_COMPILER_MSVC 81 | tst::check_eq(s, " unknown exception"s, SL); 82 | #else 83 | tst::check_eq(s, " (anonymous namespace)::some_unknown_error"s, SL); 84 | #endif 85 | }); 86 | 87 | suite.add( 88 | "stacked_exception_to_string", 89 | { 90 | #if CFG_CPU_BITS == 32 && CFG_OS == CFG_OS_WINDOWS && CFG_COMPILER == CFG_COMPILER_GCC 91 | tst::flag::disabled 92 | #endif 93 | }, 94 | []() { 95 | std::string s; 96 | try { 97 | try { 98 | try { 99 | try { 100 | throw some_unknown_error(); 101 | } catch (...) { 102 | utki::throw_with_nested(std::logic_error("some logic error")); 103 | } 104 | } catch (...) { 105 | utki::throw_with_nested(std::runtime_error("some_runtime_error")); 106 | } 107 | } catch (...) { 108 | utki::throw_with_nested(std::invalid_argument("some argument is invalid")); 109 | } 110 | } catch (utki::exception& e) { 111 | s = e.to_string(" "sv); 112 | } 113 | 114 | auto actual = utki::split(s, '\n'); 115 | 116 | #if CFG_COMPILER == CFG_COMPILER_MSVC 117 | std::vector expected = { 118 | " std::invalid_argument: some argument is invalid"s, 119 | " std::runtime_error: some_runtime_error"s, 120 | " std::logic_error: some logic error"s, 121 | " unknown exception"s 122 | }; 123 | #else 124 | std::vector expected = { 125 | " std::invalid_argument: some argument is invalid"s, 126 | " std::runtime_error: some_runtime_error"s, 127 | " std::logic_error: some logic error"s, 128 | " (anonymous namespace)::some_unknown_error"s 129 | }; 130 | #endif 131 | 132 | tst::check_eq(actual.size(), expected.size(), SL); 133 | 134 | for (auto ei = expected.begin(), ai = actual.begin(); ei != expected.end(); ++ei, ++ai) { 135 | tst::check_eq(*ai, *ei, SL); 136 | } 137 | } 138 | ); 139 | 140 | suite.add( 141 | "stacked_exception_what", 142 | { 143 | #if CFG_CPU_BITS == 32 && CFG_OS == CFG_OS_WINDOWS && CFG_COMPILER == CFG_COMPILER_GCC 144 | tst::flag::disabled 145 | #endif 146 | }, 147 | []() { 148 | std::string s; 149 | try { 150 | try { 151 | try { 152 | try { 153 | throw some_unknown_error(); 154 | } catch (...) { 155 | utki::throw_with_nested(std::logic_error("some logic error")); 156 | } 157 | } catch (...) { 158 | utki::throw_with_nested(std::runtime_error("some_runtime_error")); 159 | } 160 | } catch (...) { 161 | utki::throw_with_nested(std::invalid_argument("some argument is invalid")); 162 | } 163 | } catch (std::exception& e) { 164 | s = e.what(); 165 | } 166 | 167 | auto actual = utki::split(s, '\n'); 168 | 169 | #if CFG_COMPILER == CFG_COMPILER_MSVC 170 | std::vector expected = { 171 | "exception stack:"s, 172 | "- std::invalid_argument: some argument is invalid"s, 173 | "- std::runtime_error: some_runtime_error"s, 174 | "- std::logic_error: some logic error"s, 175 | "- unknown exception"s 176 | }; 177 | #else 178 | std::vector expected = { 179 | "exception stack:"s, 180 | "- std::invalid_argument: some argument is invalid"s, 181 | "- std::runtime_error: some_runtime_error"s, 182 | "- std::logic_error: some logic error"s, 183 | "- (anonymous namespace)::some_unknown_error"s 184 | }; 185 | #endif 186 | 187 | tst::check_eq(actual.size(), expected.size(), SL); 188 | 189 | for (auto ei = expected.begin(), ai = actual.begin(); ei != expected.end(); ++ei, ++ai) { 190 | tst::check_eq(*ai, *ei, SL); 191 | } 192 | } 193 | ); 194 | }); 195 | } // namespace 196 | -------------------------------------------------------------------------------- /tests/unit/src/math.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace { 7 | const tst::set set("math", [](tst::suite& suite) { 8 | suite.add("basic", []() { 9 | constexpr auto epsilon = 0.00001; 10 | constexpr auto two_pi_degrees = 180.0; 11 | 12 | // clang-format off 13 | tst::check_eq(std::sin((long double)(0)), (long double)(0), SL); 14 | tst::check_lt(std::abs(std::sin((long double)(utki::pi) / 2) - 1), (long double)(epsilon), SL); 15 | tst::check_lt(std::abs(std::sin((long double)(utki::pi))), (long double)(epsilon), SL); 16 | tst::check_lt(std::abs(std::sin((long double)(utki::pi) * 3 / 2) + 1), (long double)(epsilon), SL); 17 | 18 | tst::check_eq(std::cos((long double)(0)), (long double)(1), SL); 19 | tst::check_lt(std::abs(std::cos((long double)(utki::pi) / 2)), (long double)(epsilon), SL); 20 | tst::check_lt(std::abs(std::cos((long double)(utki::pi)) + 1), (long double)(epsilon), SL); 21 | tst::check_lt(std::abs(std::cos((long double)(utki::pi) * 3 / 2)), (long double)(epsilon), SL); 22 | 23 | tst::check_eq(std::exp((long double)(0)), (long double)(1), SL); 24 | tst::check_lt(std::abs(std::exp((long double)(utki::log_2)) - 2), (long double)(epsilon), SL); 25 | 26 | tst::check_eq(std::log((long double)(1)), (long double)(0), SL); 27 | tst::check_lt(std::abs( 28 | #if CFG_CPP >= 20 29 | std::numbers::ln2_v 30 | #else 31 | std::log((long double)(2)) 32 | #endif 33 | - (long double)(utki::log_2)), (long double)(epsilon), SL); 34 | 35 | tst::check_eq(utki::rad_to_deg(float(utki::pi)), float(two_pi_degrees), SL); 36 | tst::check_eq(utki::deg_to_rad(float(two_pi_degrees)), float(utki::pi), SL); 37 | // clang-format on 38 | }); 39 | }); 40 | } // namespace -------------------------------------------------------------------------------- /tests/unit/src/shared.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | class test_class : public utki::shared 7 | { 8 | public: 9 | int a = 4; 10 | 11 | test_class() = default; 12 | 13 | test_class(int i) : 14 | a(i) 15 | {} 16 | 17 | std::shared_ptr get_ptr() 18 | { 19 | return utki::make_shared_from(*this).to_shared_ptr(); 20 | } 21 | }; 22 | 23 | class test_class_enable_shared_from_this : public std::enable_shared_from_this 24 | { 25 | public: 26 | int a = 4; 27 | 28 | test_class_enable_shared_from_this() = default; 29 | 30 | test_class_enable_shared_from_this(int i) : 31 | a(i) 32 | {} 33 | }; 34 | } // namespace 35 | 36 | namespace { 37 | const tst::set set("shared", [](tst::suite& suite) { 38 | suite.add("basic", []() { 39 | std::shared_ptr p1 = std::make_shared(); 40 | 41 | std::shared_ptr p2 = std::make_shared(21); 42 | 43 | tst::check_eq(p1->a, 4, SL); 44 | tst::check_eq(p2->a, 21, SL); 45 | 46 | tst::check_eq(p2->get_ptr().operator->(), p2.operator->(), SL); 47 | }); 48 | 49 | suite.add("make_weak_from_shared_ptr", []() { 50 | auto o = std::make_shared(); 51 | tst::check(o, SL); 52 | 53 | auto w = utki::make_weak(o); 54 | 55 | auto p = w.lock(); 56 | 57 | tst::check(p, SL); 58 | tst::check_eq(p->a, 4, SL); 59 | }); 60 | 61 | suite.add("make_shared_from__shared", []() { 62 | auto o = std::make_shared(); 63 | tst::check(o, SL); 64 | 65 | auto sft = utki::make_shared_from(*o); 66 | tst::check_eq(sft.get().a, 4, SL); 67 | }); 68 | 69 | suite.add("make_shared_from__enable_shared_from_this", []() { 70 | auto o = std::make_shared(); 71 | tst::check(o, SL); 72 | 73 | auto sft = utki::make_shared_from(*o); 74 | tst::check_eq(sft.get().a, 4, SL); 75 | }); 76 | 77 | suite.add("make_weak_from__shared", []() { 78 | auto o = std::make_shared(); 79 | tst::check(o, SL); 80 | 81 | auto sft = utki::make_weak_from(*o); 82 | tst::check(sft.lock(), SL); 83 | tst::check_eq(sft.lock()->a, 4, SL); 84 | }); 85 | 86 | suite.add("make_weak_from__enable_shared_from_this", []() { 87 | auto o = std::make_shared(); 88 | tst::check(o, SL); 89 | 90 | auto sft = utki::make_weak_from(*o); 91 | tst::check(sft.lock(), SL); 92 | tst::check_eq(sft.lock()->a, 4, SL); 93 | }); 94 | }); 95 | } // namespace 96 | -------------------------------------------------------------------------------- /tests/unit/src/signal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | const tst::set set("signal", [](tst::suite& suite) { 7 | suite.add("basic__no_arguments", []() { 8 | utki::signal<> sig; 9 | 10 | tst::check(sig.empty(), SL); 11 | tst::check_eq(sig.size(), size_t(0), SL); 12 | 13 | sig.emit(); 14 | 15 | tst::check(sig.empty(), SL); 16 | tst::check_eq(sig.size(), size_t(0), SL); 17 | 18 | unsigned num = 0; 19 | 20 | auto id1 = sig.connect([&]() { 21 | ++num; 22 | }); 23 | 24 | tst::check(!sig.empty(), SL); 25 | tst::check_eq(sig.size(), size_t(1), SL); 26 | tst::check_eq(num, unsigned(0), SL); 27 | 28 | sig.emit(); 29 | 30 | tst::check(!sig.empty(), SL); 31 | tst::check_eq(sig.size(), size_t(1), SL); 32 | tst::check_eq(num, unsigned(1), SL); 33 | 34 | auto id2 = sig.connect([&]() { 35 | num += 3; 36 | }); 37 | 38 | tst::check(id1 != id2, SL); 39 | 40 | tst::check(!sig.empty(), SL); 41 | tst::check_eq(sig.size(), size_t(2), SL); 42 | tst::check_eq(num, unsigned(1), SL); 43 | 44 | sig.emit(); 45 | 46 | tst::check(!sig.empty(), SL); 47 | tst::check_eq(sig.size(), size_t(2), SL); 48 | tst::check_eq(num, unsigned(5), SL); 49 | 50 | sig.disconnect(id1); 51 | 52 | tst::check(!sig.empty(), SL); 53 | tst::check_eq(sig.size(), size_t(1), SL); 54 | tst::check_eq(num, unsigned(5), SL); 55 | }); 56 | }); 57 | } // namespace -------------------------------------------------------------------------------- /tests/unit/src/singleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #if CFG_COMPILER != CFG_COMPILER_MSVC 7 | 8 | namespace { 9 | class test_singleton : public utki::singleton 10 | { 11 | public: 12 | int a = 13; 13 | }; 14 | } // namespace 15 | 16 | namespace { 17 | const tst::set set("singleton", [](tst::suite& suite) { 18 | suite.add( // 19 | "only_one_singleton_instance_can_exist_at_a_time", 20 | tst::flag::no_parallel, 21 | [] { 22 | test_singleton sing1; 23 | 24 | bool thrown = false; 25 | try { 26 | test_singleton sing2; 27 | tst::check(false, SL) << "creating second singleton object should throw"; 28 | } catch (std::logic_error&) { 29 | thrown = true; 30 | } 31 | 32 | tst::check(thrown, SL); 33 | } 34 | ); 35 | }); 36 | } // namespace 37 | #endif // ~msvc compiler 38 | -------------------------------------------------------------------------------- /tests/unit/src/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std::string_view_literals; 9 | 10 | #if CFG_COMPILER != CFG_COMPILER_MSVC || CFG_COMPILER_MSVC_TOOLS_V >= 142 11 | namespace { 12 | namespace test_compile_time_sorting { 13 | struct str_num_pair { 14 | std::string_view first; 15 | int second; 16 | 17 | constexpr bool operator==(const str_num_pair& p) const 18 | { 19 | return this->first == p.first && this->second == p.second; 20 | } 21 | }; 22 | 23 | constexpr auto arr = []() constexpr { 24 | std::array arr = { 25 | {{"hello"sv, 13}, {"bye"sv, 15}, {"how"sv, 33}, {"are"sv, 4}, {"you"sv, 9}} 26 | }; 27 | 28 | utki::sort( // 29 | arr.begin(), 30 | arr.end(), 31 | [](const auto& a, const auto& b) { // 32 | return a.first < b.first; 33 | } 34 | ); 35 | return arr; 36 | }(); 37 | 38 | constexpr decltype(arr) expected = { 39 | {{"are", 4}, {"bye", 15}, {"hello", 13}, {"how", 33}, {"you", 9}} 40 | }; 41 | 42 | static_assert( 43 | // the std::array::operator==() is not constexpr in C++17 (it is in C++20), so we need to compare element by element 44 | [] { 45 | if (arr.size() != expected.size()) { 46 | return false; 47 | } 48 | for (auto i = arr.begin(), j = expected.begin(); i != arr.end(); ++i, ++j) { 49 | if (*i == *j) { 50 | continue; 51 | } 52 | return false; 53 | } 54 | return true; 55 | }(), 56 | "sorted array is not as expected" 57 | ); 58 | } // namespace test_compile_time_sorting 59 | } // namespace 60 | #endif 61 | 62 | namespace { 63 | const tst::set set("sort", [](tst::suite& suite) { 64 | suite.add("sort_strings_with_default_comparator", [] { 65 | std::array arr = { 66 | { 67 | "hello", "bye", 68 | "how", "are", 69 | "you", } 70 | }; 71 | 72 | utki::sort(arr.begin(), arr.end()); 73 | 74 | decltype(arr) expected = { 75 | { 76 | "are", "bye", 77 | "hello", "how", 78 | "you", } 79 | }; 80 | 81 | tst::check(arr == expected, SL); 82 | }); 83 | 84 | suite.add("sort_strings_with_custom_comparator", [] { 85 | std::array, 5> arr = { 86 | {{"hello", 13}, {"bye", 15}, {"how", 33}, {"are", 4}, {"you", 9}} 87 | }; 88 | 89 | utki::sort( // 90 | arr.begin(), 91 | arr.end(), 92 | [](const auto& a, const auto& b) { 93 | return a.first < b.first; 94 | } 95 | ); 96 | 97 | decltype(arr) expected = { 98 | {{"are", 4}, {"bye", 15}, {"hello", 13}, {"how", 33}, {"you", 9}} 99 | }; 100 | 101 | tst::check(arr == expected, SL); 102 | }); 103 | 104 | suite.add("sort_integers_with_custom_comparator", [] { 105 | std::array, 5> arr = { 106 | { 107 | {"hello", 13}, 108 | {"bye", 15}, 109 | {"how", 33}, 110 | {"are", 4}, 111 | {"you", 9}, 112 | } 113 | }; 114 | 115 | utki::sort( // 116 | arr.begin(), 117 | arr.end(), 118 | [](const auto& a, const auto& b) { // 119 | return a.second < b.second; 120 | } 121 | ); 122 | 123 | decltype(arr) expected = { 124 | { 125 | {"are", 4}, 126 | {"you", 9}, 127 | {"hello", 13}, 128 | {"bye", 15}, 129 | {"how", 33}, 130 | } 131 | }; 132 | 133 | tst::check(arr == expected, SL); 134 | }); 135 | }); 136 | } // namespace 137 | -------------------------------------------------------------------------------- /tests/unit/src/type_traits.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace { 6 | const tst::set set("type_traits", [](tst::suite& suite) { 7 | suite.add("is_scoped_enum", []() { 8 | enum unscoped { 9 | a, 10 | b, 11 | c 12 | }; 13 | 14 | enum class scoped { 15 | a, 16 | b, 17 | c 18 | }; 19 | 20 | static_assert(!utki::is_scoped_enum_v); 21 | static_assert(!utki::is_scoped_enum_v); 22 | static_assert(utki::is_scoped_enum_v); 23 | 24 | tst::check(!utki::is_scoped_enum_v, SL); 25 | tst::check(!utki::is_scoped_enum_v, SL); 26 | tst::check(utki::is_scoped_enum_v, SL); 27 | }); 28 | }); 29 | } // namespace -------------------------------------------------------------------------------- /tests/unit/src/variant.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace { 8 | const tst::set set("variant", [](tst::suite& suite) { 9 | #if CFG_COMPILER != CFG_COMPILER_MSVC || CFG_COMPILER_MSVC_TOOLS_V >= 142 10 | suite.add("index_of", []() { 11 | using variant_type = std::variant>; 12 | 13 | std::array> variants = { 14 | {std::string("hello world!"), int(123), "const char*", std::make_pair(true, 45)} 15 | }; 16 | 17 | tst::check_eq(variants.size(), size_t(4), SL); 18 | 19 | std::vector res; 20 | 21 | for (auto& v : variants) { 22 | switch (v.index()) { 23 | case utki::index_of::value: 24 | res.emplace_back("int"); 25 | break; 26 | case utki::index_of, decltype(variants)::value_type>::value: 27 | res.emplace_back("pair"); 28 | break; 29 | case utki::index_of_v: 30 | res.emplace_back("string"); 31 | break; 32 | case utki::index_of_v: 33 | res.emplace_back("const char*"); 34 | break; 35 | default: 36 | break; 37 | } 38 | } 39 | 40 | # ifdef DEBUG 41 | for (auto& r : res) { 42 | utki::log([&](auto& o) { 43 | o << "r = " << r << std::endl; 44 | }); 45 | } 46 | # endif 47 | 48 | std::vector expected = { 49 | {"string", "int", "const char*", "pair"} 50 | }; 51 | 52 | tst::check(res == expected, SL); 53 | }); 54 | #endif // ~ non-MSVC compiler or MSVC compiler tools >= v142 55 | 56 | suite.add("overloaded", []() { 57 | using variant_type = std::variant>; 58 | 59 | std::array> variants = { 60 | {std::string("hello world!"), int(123), 'a', "const char*", std::make_pair(true, 45)} 61 | }; 62 | 63 | tst::check_eq(variants.size(), size_t(5), SL); 64 | 65 | std::vector res; 66 | 67 | for (auto& v : variants) { 68 | std::visit( 69 | utki::overloaded{ 70 | [&](auto v) { 71 | res.emplace_back("auto"); 72 | }, 73 | [&](const std::string s) { 74 | res.emplace_back(std::string("std::string(): ").append(s)); 75 | }, 76 | [&](int v) { 77 | res.emplace_back(utki::cat("int = ", v)); 78 | } 79 | }, 80 | v 81 | ); 82 | } 83 | 84 | #ifdef DEBUG 85 | for (auto& r : res) { 86 | utki::log([&](auto& o) { 87 | o << "r = " << r << std::endl; 88 | }); 89 | } 90 | #endif 91 | 92 | std::vector expected = { 93 | {"std::string(): hello world!", "int = 123", "auto", "auto", "auto"} 94 | }; 95 | 96 | tst::check(res == expected, SL); 97 | }); 98 | }); 99 | } // namespace -------------------------------------------------------------------------------- /tests/unit/src/views.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::string_literals; 7 | 8 | namespace { 9 | const tst::set set("views", [](tst::suite& suite) { 10 | suite.add("reverse_view", []() { 11 | std::array arr{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 12 | 13 | std::vector result; 14 | 15 | for (const auto& i : utki::views::reverse(arr)) { 16 | result.push_back(i); 17 | } 18 | 19 | std::vector expected = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; 20 | 21 | tst::check(result == expected, SL); 22 | }); 23 | 24 | suite.add("zip_view", []() { 25 | std::vector vec = {0, 1, 2, 3, 4, 5, 6}; 26 | std::array arr = { 27 | {3.0f, 4.0f, 5.0f} 28 | }; 29 | std::vector vecstr = {"1"s, "2"s, "3"s, "4"s}; 30 | 31 | std::vector result; 32 | 33 | for (auto [a, b, c] : utki::views::zip(vec, arr, vecstr)) { 34 | result.push_back(utki::cat(a, b, c)); 35 | a += 1; 36 | } 37 | 38 | std::vector expected = {"031"s, "142"s, "253"s}; 39 | 40 | tst::check_eq(result.size(), expected.size(), SL); 41 | tst::check(result == expected, SL) 42 | << "[0] = " << result[0] << ", [1] = " << result[1] << ", [2] = " << result[2]; 43 | 44 | std::vector vec_expected = {1, 2, 3, 3, 4, 5, 6}; 45 | tst::check(vec == vec_expected, SL) << vec[0] << vec[1] << vec[2] << vec[3] << vec[4] << vec[5] << vec[6]; 46 | }); 47 | 48 | suite.add("zip_view__const", []() { 49 | const std::vector vec = {0, 1, 2, 3, 4, 5, 6}; 50 | const std::array arr = { 51 | {3.0f, 4.0f, 5.0f} 52 | }; 53 | const std::vector vecstr = {"1"s, "2"s, "3"s, "4"s}; 54 | 55 | std::vector result; 56 | 57 | for (auto [a, b, c] : utki::views::zip(vec, arr, vecstr)) { 58 | result.push_back(utki::cat(a, b, c)); 59 | } 60 | 61 | std::vector expected = {"031"s, "142"s, "253"s}; 62 | 63 | tst::check_eq(result.size(), expected.size(), SL); 64 | tst::check(result == expected, SL) 65 | << "[0] = " << result[0] << ", [1] = " << result[1] << ", [2] = " << result[2]; 66 | }); 67 | 68 | suite.add("zip_view__const_non_const_mix", []() { 69 | const std::vector vec = {0, 1, 2, 3, 4, 5, 6}; 70 | const std::array arr = { 71 | {3.0f, 4.0f, 5.0f} 72 | }; 73 | std::vector vecstr = {"1"s, "2"s, "3"s, "4"s}; 74 | 75 | std::vector result; 76 | 77 | for (const auto& [a, b, c] : utki::views::zip(vec, arr, vecstr)) { 78 | result.push_back(utki::cat(a, b, c)); 79 | } 80 | 81 | std::vector expected = {"031"s, "142"s, "253"s}; 82 | 83 | tst::check_eq(result.size(), expected.size(), SL); 84 | tst::check(result == expected, SL) 85 | << "[0] = " << result[0] << ", [1] = " << result[1] << ", [2] = " << result[2]; 86 | }); 87 | 88 | suite.add("zip_view__iterate_over_span", []() { 89 | std::vector vec1 = {1, 2, 3, 4}; 90 | std::vector vec2 = {"a", "b", "c", "d"}; 91 | 92 | auto vec1span = utki::make_span(vec1); 93 | 94 | std::vector result; 95 | 96 | for (auto [v2, v1span] : utki::views::zip(vec2, vec1span)) { 97 | result.push_back(utki::cat(v2, v1span)); 98 | } 99 | 100 | std::vector expected = {"a1", "b2", "c3", "d4"}; 101 | 102 | tst::check(result == expected, SL); 103 | }); 104 | }); 105 | } // namespace -------------------------------------------------------------------------------- /wiki/MainPage.adoc: -------------------------------------------------------------------------------- 1 | = Istallation 2 | :package_name: utki 3 | 4 | . Setup your OS-preferred package system repo following link:https://github.com/cppfw/wiki/blob/main/enable_repo/enable_repo.adoc[this manual] 5 | . Install package 6 | + 7 | - **vcpkg** (multi-OS): `{package_name}` 8 | - **conan** (multi-OS): `{package_name}` 9 | - **deb** (Linux): `lib{package_name}-dev` 10 | - **homebrew** (MacOS X): `lib{package_name}` 11 | - **Android**: `io.github.cppfw:{package_name}` 12 | - **cocoapods** (iOS): `{package_name}` 13 | - **Msys2** (Windows): `mingw-w64-i686-{package_name}`, `mingw-w64-x86_64-{package_name}` 14 | - **Nuget** (Windows, Visual Studio): `lib{package_name}` 15 | 16 | = Tutorials 17 | 18 | link:SingletonUsage.md[Singleton] 19 | -------------------------------------------------------------------------------- /wiki/SingletonUsage.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | It is often required to create singleton objects in the program, i.e. use the singleton pattern. 4 | 5 | **utki** provides a template for quick creation of singleton classes. 6 | 7 | 8 | 9 | ## Header file 10 | In order to use `utki::singleton` one needs to include the following header file: 11 | ```cpp 12 | #include 13 | ``` 14 | 15 | ## Usage 16 | The usage is simple: 17 | ```cpp 18 | class MySingletonClass : public utki::singleton{ 19 | public: 20 | int a; 21 | int b; 22 | }; 23 | 24 | int main(int argc, char** argv){ 25 | // create singleton object 26 | MySingletonClass mySingletonObject; 27 | 28 | // NOTE: since this is a singleton class, creating other 29 | // instances of this class will cause an exception to be thrown. 30 | 31 | // access the members of singleton object 32 | MySingletonClass::inst().a = 355; 33 | MySingletonClass::inst().b = 13; 34 | 35 | int k = MySingletonClass::inst().b; 36 | } 37 | ``` 38 | 39 | Note, that we create a singleton object inside of the main() function. Though it is not forbidden to create singleton objects in a global scope, it is not recommended and considered a bad practice. This is because when defining objects in global scope and in different files their order of construction is undetermined. Creating objects inside of the main() function allows you to strictly define the order of objects construction. 40 | -------------------------------------------------------------------------------- /xcode/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/cppfw/cocoapods-repo.git' 2 | 3 | project 'utki/utki.xcodeproj' 4 | 5 | platform :ios, '9.0' 6 | 7 | target "utki" do 8 | 9 | end 10 | -------------------------------------------------------------------------------- /xcode/utki/utki.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /xcode/utki/utki.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /xcode/utki/utki.xcodeproj/project.xcworkspace/xcuserdata/ivan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cppfw/utki/5153f58865714c6b139ea8fa3e2e0cf3b9fb0984/xcode/utki/utki.xcodeproj/project.xcworkspace/xcuserdata/ivan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /xcode/utki/utki.xcodeproj/xcuserdata/ivan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | utki.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------