Initial commit.
This commit is contained in:
		
							
								
								
									
										121
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| # ClangFormatConfigureSource: 'LLVM' | ||||
| --- | ||||
| Language:        Cpp | ||||
| # BasedOnStyle:  LLVM | ||||
| AccessModifierOffset: -4 | ||||
| AlignAfterOpenBracket: Align | ||||
| AlignConsecutiveAssignments: false | ||||
| AlignConsecutiveDeclarations: false | ||||
| AlignEscapedNewlines: Right | ||||
| AlignOperands:   true | ||||
| AlignTrailingComments: true | ||||
| AllowAllParametersOfDeclarationOnNextLine: true | ||||
| AllowShortBlocksOnASingleLine: false | ||||
| AllowShortCaseLabelsOnASingleLine: true | ||||
| AllowShortFunctionsOnASingleLine: All | ||||
| AllowShortIfStatementsOnASingleLine: Never | ||||
| AllowShortLoopsOnASingleLine: false | ||||
| AlwaysBreakAfterDefinitionReturnType: None | ||||
| AlwaysBreakAfterReturnType: None | ||||
| AlwaysBreakBeforeMultilineStrings: false | ||||
| AlwaysBreakTemplateDeclarations: MultiLine | ||||
| BinPackArguments: true | ||||
| BinPackParameters: true | ||||
| BraceWrapping: | ||||
|   AfterClass:      false | ||||
|   AfterControlStatement: false | ||||
|   AfterEnum:       false | ||||
|   AfterFunction:   false | ||||
|   AfterNamespace:  false | ||||
|   AfterObjCDeclaration: false | ||||
|   AfterStruct:     false | ||||
|   AfterUnion:      false | ||||
|   AfterExternBlock: false | ||||
|   BeforeCatch:     false | ||||
|   BeforeElse:      false | ||||
|   IndentBraces:    false | ||||
|   SplitEmptyFunction: true | ||||
|   SplitEmptyRecord: true | ||||
|   SplitEmptyNamespace: true | ||||
| BreakBeforeBinaryOperators: None | ||||
| BreakBeforeBraces: Attach | ||||
| BreakBeforeInheritanceComma: false | ||||
| BreakInheritanceList: BeforeColon | ||||
| BreakBeforeTernaryOperators: true | ||||
| BreakConstructorInitializersBeforeComma: false | ||||
| BreakConstructorInitializers: BeforeColon | ||||
| BreakAfterJavaFieldAnnotations: false | ||||
| BreakStringLiterals: true | ||||
| ColumnLimit:     120 | ||||
| CommentPragmas:  '^ IWYU pragma:' | ||||
| CompactNamespaces: false | ||||
| ConstructorInitializerAllOnOneLineOrOnePerLine: false | ||||
| ConstructorInitializerIndentWidth: 4 | ||||
| ContinuationIndentWidth: 4 | ||||
| Cpp11BracedListStyle: true | ||||
| DerivePointerAlignment: false | ||||
| DisableFormat:   false | ||||
| ExperimentalAutoDetectBinPacking: false | ||||
| FixNamespaceComments: false | ||||
| ForEachMacros: | ||||
|   - foreach | ||||
|   - Q_FOREACH | ||||
|   - BOOST_FOREACH | ||||
| IncludeBlocks:   Merge | ||||
| IncludeCategories: | ||||
|   - Regex:           '^"(llvm|llvm-c|clang|clang-c)/' | ||||
|     Priority:        2 | ||||
|   - Regex:           '^(<|"(gtest|gmock|isl|json)/)' | ||||
|     Priority:        1 | ||||
|   - Regex:           '.*' | ||||
|     Priority:        3 | ||||
| IncludeIsMainRegex: '(Test)?$' | ||||
| IndentCaseLabels: true | ||||
| IndentPPDirectives: None | ||||
| IndentWidth:     4 | ||||
| IndentWrappedFunctionNames: false | ||||
| JavaScriptQuotes: Leave | ||||
| JavaScriptWrapImports: true | ||||
| KeepEmptyLinesAtTheStartOfBlocks: true | ||||
| MacroBlockBegin: '' | ||||
| MacroBlockEnd:   '' | ||||
| MaxEmptyLinesToKeep: 1 | ||||
| NamespaceIndentation: All | ||||
| ObjCBinPackProtocolList: Auto | ||||
| ObjCBlockIndentWidth: 2 | ||||
| ObjCSpaceAfterProperty: false | ||||
| ObjCSpaceBeforeProtocolList: true | ||||
| PenaltyBreakAssignment: 2 | ||||
| PenaltyBreakBeforeFirstCallParameter: 19 | ||||
| PenaltyBreakComment: 300 | ||||
| PenaltyBreakFirstLessLess: 120 | ||||
| PenaltyBreakString: 1000 | ||||
| PenaltyBreakTemplateDeclaration: 10 | ||||
| PenaltyExcessCharacter: 1000000 | ||||
| PenaltyReturnTypeOnItsOwnLine: 60 | ||||
| PointerAlignment: Left | ||||
| ReflowComments:  true | ||||
| SortIncludes:    true | ||||
| SortUsingDeclarations: true | ||||
| SpaceAfterCStyleCast: false | ||||
| SpaceAfterTemplateKeyword: true | ||||
| SpaceBeforeAssignmentOperators: true | ||||
| SpaceBeforeCpp11BracedList: false | ||||
| SpaceBeforeCtorInitializerColon: true | ||||
| SpaceBeforeInheritanceColon: true | ||||
| SpaceBeforeParens: ControlStatements | ||||
| SpaceBeforeRangeBasedForLoopColon: true | ||||
| SpaceInEmptyParentheses: false | ||||
| SpacesBeforeTrailingComments: 1 | ||||
| SpacesInAngles:  false | ||||
| SpacesInContainerLiterals: true | ||||
| SpacesInCStyleCastParentheses: false | ||||
| SpacesInParentheses: false | ||||
| SpacesInSquareBrackets: false | ||||
| Standard:        Cpp11 | ||||
| StatementMacros: | ||||
|   - Q_UNUSED | ||||
|   - QT_REQUIRE_VERSION | ||||
| TabWidth:        8 | ||||
| UseTab:          Never | ||||
| ... | ||||
							
								
								
									
										53
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| kind: pipeline | ||||
| name: default | ||||
|  | ||||
| type: docker | ||||
| steps: | ||||
|   - name: test-debug-linux | ||||
|     image: deukhoofd/linux64builder | ||||
|     environment: | ||||
|       CC: /usr/bin/clang | ||||
|       CXX: /usr/bin/clang++ | ||||
|     commands: | ||||
|       - cmake -DCMAKE_BUILD_TYPE=Debug . -B build-debug | ||||
|       - cmake --build build-debug --target all -- -j 4 | ||||
|       - build-debug/ArbutilsTests -s --durations yes --use-colour yes | ||||
|   - name: test-release-linux | ||||
|     image: deukhoofd/linux64builder | ||||
|     environment: | ||||
|       CC: /usr/bin/clang | ||||
|       CXX: /usr/bin/clang++ | ||||
|     commands: | ||||
|       - cmake -DCMAKE_BUILD_TYPE=Release . -B build-release | ||||
|       - cmake --build build-release --target all -- -j 4 | ||||
|       - build-release/ArbutilsTests -s --durations yes --use-colour yes | ||||
|       - valgrind --tool=memcheck --gen-suppressions=all --leak-check=full --leak-resolution=med --track-origins=yes --vgdb=no --error-exitcode=1 build-release/ArbutilsTests | ||||
|   - name: test-release-windows | ||||
|     image: deukhoofd/windowsbuilder | ||||
|     commands: | ||||
|       - update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix | ||||
|       - update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix | ||||
|       - update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix | ||||
|       - update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix | ||||
|       - cmake -DCMAKE_BUILD_TYPE=Release . -B build-release-windows -D CMAKE_C_COMPILER=/usr/bin/x86_64-w64-mingw32-gcc -D CMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DWINDOWS=ON | ||||
|       - cmake --build build-release-windows --target all -- -j  4 | ||||
|       - export WINEARCH=win64 | ||||
|       - wine build-release-windows/ArbutilsTests.exe -s | ||||
|   - name: conan-deploy | ||||
|     image: deukhoofd/linux64builder | ||||
|     environment: | ||||
|       CONAN_LOGIN_USERNAME: | ||||
|         from_secret: conan_username | ||||
|       CONAN_PASSWORD: | ||||
|         from_secret: conan_password | ||||
|     commands: | ||||
|       - rm -rf build-debug | ||||
|       - rm -rf build-release-windows | ||||
|       - cmake -DCMAKE_BUILD_TYPE=Release . -B build-conan -D CMAKE_C_COMPILER=/usr/bin/clang -D CMAKE_CXX_COMPILER=clang++ -DWINDOWS=0 | ||||
|       - conan export-pkg . $DRONE_COMMIT@epsilon/$DRONE_BRANCH --build-folder build-conan -s compiler='clang' -s compiler.version=8 -s compiler.libcxx='libstdc++11' | ||||
|       - conan alias Arbutils/latest@epsilon/$DRONE_BRANCH Arbutils/$DRONE_COMMIT@epsilon/$DRONE_BRANCH | ||||
|       - conan remote add epsilon-public https://packages.p-epsilon.com/artifactory/api/conan/epsilon-public | ||||
|       - conan user -p -r=epsilon-public | ||||
|       - conan upload Arbutils/$DRONE_COMMIT@epsilon/$DRONE_BRANCH --all -r=epsilon-public --force | ||||
|       - conan upload Arbutils/latest@epsilon/$DRONE_BRANCH --all -r=epsilon-public --force | ||||
|       - conan user --clean | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| /cmake-build-debug/ | ||||
| /cmake-build-release/ | ||||
| /.idea/ | ||||
							
								
								
									
										23
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| cmake_minimum_required(VERSION 3.16) | ||||
| project(Arbutils) | ||||
|  | ||||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
|  | ||||
| file(GLOB_RECURSE SRC_FILES "src/*.cpp" "src/*.hpp") | ||||
| add_library(Arbutils SHARED ${SRC_FILES}) | ||||
|  | ||||
| file(GLOB_RECURSE TEST_FILES "tests/*.cpp" "tests/*.hpp") | ||||
| add_executable(ArbutilsTests ${TEST_FILES} extern/catch.hpp) | ||||
|  | ||||
| target_link_libraries(ArbutilsTests Arbutils) | ||||
|  | ||||
| if (WINDOWS) | ||||
|     MESSAGE(WARNING, "Using Windows Build.") | ||||
|     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 -Wa,-mbig-obj -Wl,-allow-multiple-definition") | ||||
|     # Statically link libraries we need in Windows. | ||||
|     target_link_libraries(Arbutils -static -static-libgcc -static-libstdc++) | ||||
|     target_link_libraries(ArbutilsTests -static -static-libgcc -static-libstdc++) | ||||
| endif (WINDOWS) | ||||
|  | ||||
| target_compile_definitions(ArbutilsTests PRIVATE TESTS_BUILD) | ||||
							
								
								
									
										35
									
								
								conanfile.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								conanfile.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| from conans import ConanFile, CMake | ||||
|  | ||||
|  | ||||
| class ArbutilsConan(ConanFile): | ||||
|     name = "Arbutils" | ||||
|     license = "TODO" | ||||
|     url = "https://git.p-epsilon.com/Deukhoofd/Arbutils" | ||||
|     description = "A helper library for the Epsilon project." | ||||
|     settings = "os", "compiler" | ||||
|     options = {"shared": [True, False]} | ||||
|     default_options = {"shared": True} | ||||
|     generators = "cmake" | ||||
|     exports_sources = "*" | ||||
|     compiler = "clang" | ||||
|  | ||||
|     def build(self): | ||||
|         cmake = CMake(self) | ||||
|         self.output.info("Target OS: %s." % self.settings.os) | ||||
|         if self.settings.os == "Windows": | ||||
|             self.output.warn("Noticed Windows target, setting Cmake WINDOWS=On.") | ||||
|             cmake.definitions["WINDOWS"] = "On" | ||||
|         cmake.configure() | ||||
|         cmake.build() | ||||
|  | ||||
|         # Explicit way: | ||||
|         # self.run('cmake "%s/src" %s' % (self.source_folder, cmake.command_line)) | ||||
|         # self.run("cmake --build . %s" % cmake.build_config) | ||||
|  | ||||
|     def package(self): | ||||
|         self.copy("*.hpp", dst="include/Arbutils", src="src") | ||||
|         self.copy("*.dll", dst="bin", keep_path=False) | ||||
|         self.copy("*.so", dst="lib", keep_path=False) | ||||
|  | ||||
|     def package_info(self): | ||||
|         self.cpp_info.libs = ["Arbutils"] | ||||
							
								
								
									
										17597
									
								
								extern/catch.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17597
									
								
								extern/catch.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								src/ConstString.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/ConstString.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| #include "ConstString.hpp" | ||||
							
								
								
									
										33
									
								
								src/ConstString.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/ConstString.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| #ifndef ARBUTILS_CONSTSTRING_HPP | ||||
| #define ARBUTILS_CONSTSTRING_HPP | ||||
|  | ||||
| #include <cstddef> | ||||
| #include <cstdint> | ||||
| #include <cstring> | ||||
| #include <string> | ||||
| namespace Arbutils { | ||||
|     class ConstString { | ||||
|     private: | ||||
|         const char* _str; | ||||
|         size_t _length; | ||||
|         uint32_t _hash; | ||||
|  | ||||
|         inline static uint32_t constexpr Hash(char const* input) { | ||||
|             return (*input) ? static_cast<uint32_t>((*input)) + 33 * Hash(input + 1) : 5381; | ||||
|         } | ||||
|         inline static int constexpr Length(const char* str) { return *str ? 1 + Length(str + 1) : 0; } | ||||
|  | ||||
|     public: | ||||
|         constexpr explicit ConstString(const char* str) : _str(str), _length(Length(str)), _hash(Hash(str)){}; | ||||
|  | ||||
|         [[nodiscard]] constexpr const char* c_str() const noexcept { return _str; } | ||||
|         [[nodiscard]] std::string std_str() const { return std::string(_str, _length); } | ||||
|  | ||||
|         [[nodiscard]] constexpr size_t Length() const noexcept { return _length; } | ||||
|  | ||||
|         [[nodiscard]] constexpr uint32_t GetHash() const noexcept { return _hash; } | ||||
|     }; | ||||
|     constexpr ConstString operator"" _const(const char* c) { return ConstString(c); } | ||||
| } | ||||
|  | ||||
| #endif // ARBUTILS_CONSTSTRING_HPP | ||||
							
								
								
									
										79
									
								
								src/Enum.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/Enum.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| #include <algorithm> | ||||
| #include <sstream> | ||||
| #include <stdexcept> | ||||
| #include <vector> | ||||
| #include "MacroUtils.hpp" | ||||
|  | ||||
| #define ENUM_CASE(x, name)                                                                                             \ | ||||
|     case name::x:                                                                                                      \ | ||||
|         return #x; | ||||
| # | ||||
| #define ENUM_PARSE_CASE(x, name)                                                                                       \ | ||||
|     case ConstHash(#x):                                                                                                \ | ||||
|         return name::x; | ||||
| # | ||||
| #define ENUM_TRY_PARSE_CASE(x, name)                                                                                   \ | ||||
|     case ConstHash(#x):                                                                                                \ | ||||
|         out = name::x;                                                                                                 \ | ||||
|         return true; | ||||
| # | ||||
| #define ENUM_PARSE_CASE_INSENSITIVE(x, name)                                                                           \ | ||||
|     case ConstHashCI(#x):                                                                                              \ | ||||
|         return name::x; | ||||
| # | ||||
| #define ENUM_TRY_PARSE_CASE_INSENSITIVE(x, name)                                                                       \ | ||||
|     case ConstHashCI(#x):                                                                                              \ | ||||
|         out = name::x;                                                                                                 \ | ||||
|         return true; | ||||
| # | ||||
| #define ARRAY_NAME(x, name) name::x, | ||||
|  | ||||
| /*! | ||||
|   \def ENUM(name, type, values...) | ||||
|   Generates an enum class inheriting from specified type with specified name. Also generates a useful helper class with | ||||
|   static methods for use with the enum. | ||||
| */ | ||||
|  | ||||
| #define ENUM(name, type, values...)                                                                                    \ | ||||
|     enum class name : type { values };                                                                                 \ | ||||
|     class name##Helper {                                                                                               \ | ||||
|         inline static uint32_t constexpr ConstHash(char const* input) {                                                \ | ||||
|             return *input ? static_cast<uint32_t>(*input) + 33 * ConstHash(input + 1) : 5381;                          \ | ||||
|         }                                                                                                              \ | ||||
|         inline static constexpr char charToLower(const char c) {                                                       \ | ||||
|             return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;                                                       \ | ||||
|         }                                                                                                              \ | ||||
|         inline static uint32_t constexpr ConstHashCI(char const* input) {                                              \ | ||||
|             return charToLower(*input) ? static_cast<uint32_t>(charToLower(*input)) + 33 * ConstHashCI(input + 1)      \ | ||||
|                                        : 5381;                                                                         \ | ||||
|         }                                                                                                              \ | ||||
|                                                                                                                        \ | ||||
|     public:                                                                                                            \ | ||||
|         constexpr static const char* ToString(name value) {                                                            \ | ||||
|             switch (value) { FOR_EACH(ENUM_CASE, name, values) }                                                       \ | ||||
|             throw std::logic_error("Not reachable");                                                                   \ | ||||
|         }                                                                                                              \ | ||||
|         constexpr static name Parse(const char* input, bool caseInsensitive = false) {                                 \ | ||||
|             if (caseInsensitive)                                                                                       \ | ||||
|                 return ParseCaseInsensitive(input);                                                                    \ | ||||
|             switch (ConstHash(input)) { FOR_EACH(ENUM_PARSE_CASE, name, values) }                                      \ | ||||
|             throw std::runtime_error("Invalid " #name " string.");                                                          \ | ||||
|         }                                                                                                              \ | ||||
|         constexpr static bool TryParse(const char* input, name& out, bool caseInsensitive = false) {                   \ | ||||
|             if (caseInsensitive)                                                                                       \ | ||||
|                 return TryParseCaseInsensitive(input, out);                                                            \ | ||||
|             switch (ConstHash(input)) { FOR_EACH(ENUM_TRY_PARSE_CASE, name, values) }                                  \ | ||||
|             return false;                                                                                              \ | ||||
|         }                                                                                                              \ | ||||
|         static std::vector<name> GetValues() { return {FOR_EACH(ARRAY_NAME, name, values)}; }                          \ | ||||
|                                                                                                                        \ | ||||
|     private:                                                                                                           \ | ||||
|         static name ParseCaseInsensitive(const char* input) {                                                          \ | ||||
|             switch (ConstHashCI(input)) { FOR_EACH(ENUM_PARSE_CASE_INSENSITIVE, name, values) }                        \ | ||||
|             throw std::runtime_error("Invalid " #name " string.");                                                          \ | ||||
|         }                                                                                                              \ | ||||
|         static bool TryParseCaseInsensitive(const char* input, name& out) {                                            \ | ||||
|             switch (ConstHashCI(input)) { FOR_EACH(ENUM_TRY_PARSE_CASE_INSENSITIVE, name, values) }                    \ | ||||
|             return false;                                                                                              \ | ||||
|         }                                                                                                              \ | ||||
|     }; | ||||
							
								
								
									
										45
									
								
								src/MacroUtils.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/MacroUtils.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| // Defines a for each macro utility, with up to 32 elements. | ||||
|  | ||||
| #define FE_0(FUNC, arg) | ||||
| #define FE_1(FUNC, arg, X) FUNC(X, arg) | ||||
| #define FE_2(FUNC, arg, X, ...) FUNC(X, arg) FE_1(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_3(FUNC, arg, X, ...) FUNC(X, arg) FE_2(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_4(FUNC, arg, X, ...) FUNC(X, arg) FE_3(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_5(FUNC, arg, X, ...) FUNC(X, arg) FE_4(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_6(FUNC, arg, X, ...) FUNC(X, arg) FE_5(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_7(FUNC, arg, X, ...) FUNC(X, arg) FE_6(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_8(FUNC, arg, X, ...) FUNC(X, arg) FE_7(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_9(FUNC, arg, X, ...) FUNC(X, arg) FE_8(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_10(FUNC, arg, X, ...) FUNC(X, arg) FE_9(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_11(FUNC, arg, X, ...) FUNC(X, arg) FE_10(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_12(FUNC, arg, X, ...) FUNC(X, arg) FE_11(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_13(FUNC, arg, X, ...) FUNC(X, arg) FE_12(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_14(FUNC, arg, X, ...) FUNC(X, arg) FE_13(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_15(FUNC, arg, X, ...) FUNC(X, arg) FE_14(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_16(FUNC, arg, X, ...) FUNC(X, arg) FE_15(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_17(FUNC, arg, X, ...) FUNC(X, arg) FE_16(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_18(FUNC, arg, X, ...) FUNC(X, arg) FE_17(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_19(FUNC, arg, X, ...) FUNC(X, arg) FE_18(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_20(FUNC, arg, X, ...) FUNC(X, arg) FE_19(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_21(FUNC, arg, X, ...) FUNC(X, arg) FE_20(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_22(FUNC, arg, X, ...) FUNC(X, arg) FE_21(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_23(FUNC, arg, X, ...) FUNC(X, arg) FE_22(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_24(FUNC, arg, X, ...) FUNC(X, arg) FE_23(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_25(FUNC, arg, X, ...) FUNC(X, arg) FE_24(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_26(FUNC, arg, X, ...) FUNC(X, arg) FE_25(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_27(FUNC, arg, X, ...) FUNC(X, arg) FE_26(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_28(FUNC, arg, X, ...) FUNC(X, arg) FE_27(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_29(FUNC, arg, X, ...) FUNC(X, arg) FE_28(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_30(FUNC, arg, X, ...) FUNC(X, arg) FE_29(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_31(FUNC, arg, X, ...) FUNC(X, arg) FE_30(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_32(FUNC, arg, X, ...) FUNC(X, arg) FE_31(FUNC, arg, __VA_ARGS__) | ||||
| #define FE_33(FUNC, arg, X, ...) THE_FOREACH_MACRO_CURRENTLY_ONLY_SUPPORTS_UP_TO_32_VALUES FE_32(FUNC, arg, __VA_ARGS__) | ||||
|  | ||||
| #define GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,  \ | ||||
|                   _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, NAME, ...)                               \ | ||||
|     NAME | ||||
| #define FOR_EACH(action, arg, ...)                                                                                     \ | ||||
|     GET_MACRO(_0, __VA_ARGS__, FE_33, FE_32, FE_31, FE_30, FE_29, FE_28, FE_27, FE_26, FE_25, FE_24, FE_23, FE_22,     \ | ||||
|               FE_21, FE_20, FE_19, FE_18, FE_17, FE_16, FE_15, FE_14, FE_13, FE_12, FE_11, FE_10, FE_9, FE_8, FE_7,    \ | ||||
|               FE_6, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0)                                                                \ | ||||
|     (action, arg, __VA_ARGS__) | ||||
							
								
								
									
										9
									
								
								src/Random.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Random.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| #include "Random.hpp" | ||||
| #include <chrono> | ||||
|  | ||||
| // Seed parameterless constructor with current milliseconds since epoch. | ||||
| template <class RandomT> | ||||
| Arbutils::BaseRandom<RandomT>::BaseRandom() | ||||
|     : _rng(RandomT( | ||||
|           std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()) | ||||
|               .count())) {} | ||||
							
								
								
									
										71
									
								
								src/Random.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/Random.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| #ifndef ARBUTILS_RANDOM_HPP | ||||
| #define ARBUTILS_RANDOM_HPP | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <random> | ||||
|  | ||||
| namespace Arbutils { | ||||
|  | ||||
|     template <class RandomT> class BaseRandom { | ||||
|     private: | ||||
|         RandomT _rng; | ||||
|  | ||||
|     public: | ||||
|         BaseRandom(); | ||||
|         explicit BaseRandom(int32_t seed) : _rng(seed){}; | ||||
|  | ||||
|         /// Gets a random float between 0.0 and 1.0. | ||||
|         [[nodiscard]] inline float GetFloat() { | ||||
|             return static_cast<float>(_rng()) / (static_cast<float>(std::mt19937::max() - std::mt19937::min()) - 0.5f); | ||||
|         } | ||||
|  | ||||
|         /// Gets a random double between 0.0 and 1.0. | ||||
|         [[nodiscard]] inline double GetDouble() { | ||||
|             return static_cast<double>(_rng()) / | ||||
|                    ((static_cast<double>(std::mt19937::max()) - std::mt19937::min()) - 0.5); | ||||
|         } | ||||
|  | ||||
|         /// Gets a random 32 bit integer between 0 and max int. | ||||
|         [[nodiscard]] inline int32_t Get() { | ||||
|             return static_cast<int32_t>(GetDouble() * static_cast<float>(std::numeric_limits<int32_t>::max())); | ||||
|         } | ||||
|  | ||||
|         /// Gets a random 32 bit integer between 0, and given max parameter. | ||||
|         /// \param max The exclusive max value the random value should be. | ||||
|         [[nodiscard]] inline int32_t Get(int32_t max) { | ||||
|             return static_cast<int32_t>(GetDouble() * static_cast<float>(max)); | ||||
|         } | ||||
|  | ||||
|         /// Gets a random 32 bit integer between given min and max parameters. | ||||
|         /// \param min The inclusive min value the random value should be. | ||||
|         /// \param max The exclusive max value the random value should be. | ||||
|         [[nodiscard]] inline int32_t Get(int32_t min, int32_t max) { | ||||
|             return static_cast<int32_t>(GetDouble() * static_cast<float>(max - min) + min); | ||||
|         } | ||||
|  | ||||
|         /// Gets a random 32 bit unsigned integer between 0 and max unsigned int. | ||||
|         [[nodiscard]] inline uint32_t GetUnsigned() { | ||||
|             return static_cast<int32_t>(GetDouble() * static_cast<float>(std::numeric_limits<uint32_t>::max())); | ||||
|         } | ||||
|  | ||||
|         /// Gets a random 32 bit unsigned integer between 0, and given max parameter. | ||||
|         /// \param max The exclusive max value the random value should be. | ||||
|         [[nodiscard]] inline uint32_t GetUnsigned(uint32_t max) { | ||||
|             return static_cast<int32_t>(GetDouble() * static_cast<float>(max)); | ||||
|         } | ||||
|  | ||||
|         /// Gets a random 32 bit unsigned integer between given min and max parameters. | ||||
|         /// \param min The inclusive min value the random value should be. | ||||
|         /// \param max The exclusive max value the random value should be. | ||||
|         [[nodiscard]] inline uint32_t GetUnsigned(uint32_t min, uint32_t max) { | ||||
|             return static_cast<int32_t>(GetDouble() * static_cast<float>(max - min) + min); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     class Random : public BaseRandom<std::mt19937> { | ||||
|     public: | ||||
|         Random() : BaseRandom() {} | ||||
|         Random(int32_t seed) : BaseRandom(seed) {} | ||||
|     }; | ||||
| } | ||||
| #endif // ARBUTILS_RANDOM_HPP | ||||
							
								
								
									
										69
									
								
								tests/EnumTests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tests/EnumTests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| #ifdef TESTS_BUILD | ||||
| #include <cstring> | ||||
| #include "../extern/catch.hpp" | ||||
| #include "../src/Enum.hpp" | ||||
|  | ||||
| ENUM(TestEnum, uint8_t, Val1, Val2, Val3) | ||||
|  | ||||
| TEST_CASE("Parse Enum case sensitive", "[Utilities]") { | ||||
|     CHECK(TestEnumHelper::Parse("Val1") == TestEnum::Val1); | ||||
|     CHECK(TestEnumHelper::Parse("Val2") == TestEnum::Val2); | ||||
|     CHECK(TestEnumHelper::Parse("Val3") == TestEnum::Val3); | ||||
|     CHECK_THROWS(TestEnumHelper::Parse("Val4")); | ||||
|     CHECK_THROWS(TestEnumHelper::Parse("val1")); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Try Parse Enum case sensitive", "[Utilities]") { | ||||
|     TestEnum v = static_cast<TestEnum>(255); | ||||
|     REQUIRE(TestEnumHelper::TryParse("Val1", v)); | ||||
|     CHECK(v == TestEnum::Val1); | ||||
|     REQUIRE(TestEnumHelper::TryParse("Val2", v)); | ||||
|     CHECK(v == TestEnum::Val2); | ||||
|     REQUIRE(TestEnumHelper::TryParse("Val3", v)); | ||||
|     CHECK(v == TestEnum::Val3); | ||||
|     CHECK_FALSE(TestEnumHelper::TryParse("Val4", v)); | ||||
|     CHECK_FALSE(TestEnumHelper::TryParse("val1", v)); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Parse Enum case insensitive", "[Utilities]") { | ||||
|     CHECK(TestEnumHelper::Parse("Val1", true) == TestEnum::Val1); | ||||
|     CHECK(TestEnumHelper::Parse("Val2", true) == TestEnum::Val2); | ||||
|     CHECK(TestEnumHelper::Parse("Val3", true) == TestEnum::Val3); | ||||
|     CHECK(TestEnumHelper::Parse("val1", true) == TestEnum::Val1); | ||||
|     CHECK(TestEnumHelper::Parse("vAL2", true) == TestEnum::Val2); | ||||
|     CHECK(TestEnumHelper::Parse("VaL3", true) == TestEnum::Val3); | ||||
|     CHECK_THROWS(TestEnumHelper::Parse("Val4", true)); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Try Parse Enum case insensitive", "[Utilities]") { | ||||
|     TestEnum v = static_cast<TestEnum>(255); | ||||
|     REQUIRE(TestEnumHelper::TryParse("Val1", v, true)); | ||||
|     CHECK(v == TestEnum::Val1); | ||||
|     REQUIRE(TestEnumHelper::TryParse("Val2", v, true)); | ||||
|     CHECK(v == TestEnum::Val2); | ||||
|     REQUIRE(TestEnumHelper::TryParse("Val3", v, true)); | ||||
|     CHECK(v == TestEnum::Val3); | ||||
|     REQUIRE(TestEnumHelper::TryParse("val1", v, true)); | ||||
|     CHECK(v == TestEnum::Val1); | ||||
|     REQUIRE(TestEnumHelper::TryParse("vAL2", v, true)); | ||||
|     CHECK(v == TestEnum::Val2); | ||||
|     REQUIRE(TestEnumHelper::TryParse("VaL3", v, true)); | ||||
|     CHECK(v == TestEnum::Val3); | ||||
|     CHECK_FALSE(TestEnumHelper::TryParse("Val4", v, true)); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Enum To String", "[Utilities]") { | ||||
|     CHECK(strcmp(TestEnumHelper::ToString(TestEnum::Val1), "Val1") == 0); | ||||
|     CHECK(strcmp(TestEnumHelper::ToString(TestEnum::Val2), "Val2") == 0); | ||||
|     CHECK(strcmp(TestEnumHelper::ToString(TestEnum::Val3), "Val3") == 0); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Enum Get Values", "[Utilities]") { | ||||
|     auto vec = TestEnumHelper::GetValues(); | ||||
|     REQUIRE(vec.size() == 3); | ||||
|     CHECK(vec[0] == TestEnum::Val1); | ||||
|     CHECK(vec[1] == TestEnum::Val2); | ||||
|     CHECK(vec[2] == TestEnum::Val3); | ||||
| } | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										120
									
								
								tests/RandomTests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								tests/RandomTests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| #ifdef TESTS_BUILD | ||||
| #define CATCH_CONFIG_MAIN | ||||
|  | ||||
| #include "../extern/catch.hpp" | ||||
| #include "../src/Random.hpp" | ||||
|  | ||||
| TEST_CASE("Random ints", "[Utilities]") { | ||||
|     auto rand = Arbutils::Random(10); | ||||
|     CHECK(rand.Get() == 1656398469); | ||||
|     CHECK(rand.Get() == 641584702); | ||||
|     CHECK(rand.Get() == 44564466); | ||||
|     CHECK(rand.Get() == 1062123783); | ||||
|     CHECK(rand.Get() == 1360749216); | ||||
|     CHECK(rand.Get() == 951367352); | ||||
|     CHECK(rand.Get() == 1608044094); | ||||
|     CHECK(rand.Get() == 1786516046); | ||||
|     CHECK(rand.Get() == 1070535660); | ||||
|     CHECK(rand.Get() == 1252673902); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Random ints with limit", "[Utilities]") { | ||||
|     auto rand = Arbutils::Random(10); | ||||
|     CHECK(rand.Get(10) == 7); | ||||
|     CHECK(rand.Get(10) == 2); | ||||
|     CHECK(rand.Get(10) == 0); | ||||
|     CHECK(rand.Get(10) == 4); | ||||
|     CHECK(rand.Get(10) == 6); | ||||
|     CHECK(rand.Get(10) == 4); | ||||
|     CHECK(rand.Get(10) == 7); | ||||
|     CHECK(rand.Get(10) == 8); | ||||
|     CHECK(rand.Get(10) == 4); | ||||
|     CHECK(rand.Get(10) == 5); | ||||
|  | ||||
|     CHECK(rand.Get(2) == 0); | ||||
|     CHECK(rand.Get(2) == 0); | ||||
|     CHECK(rand.Get(2) == 0); | ||||
|     CHECK(rand.Get(2) == 1); | ||||
|     CHECK(rand.Get(2) == 1); | ||||
|     CHECK(rand.Get(2) == 0); | ||||
|     CHECK(rand.Get(2) == 0); | ||||
|     CHECK(rand.Get(2) == 0); | ||||
|     CHECK(rand.Get(2) == 0); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Random ints with upper and bottom", "[Utilities]") { | ||||
|     auto rand = Arbutils::Random(10); | ||||
|     CHECK(rand.Get(10, 30) == 25); | ||||
|     CHECK(rand.Get(10, 30) == 15); | ||||
|     CHECK(rand.Get(10, 30) == 10); | ||||
|     CHECK(rand.Get(10, 30) == 19); | ||||
|     CHECK(rand.Get(10, 30) == 22); | ||||
|     CHECK(rand.Get(10, 30) == 18); | ||||
|     CHECK(rand.Get(10, 30) == 24); | ||||
|     CHECK(rand.Get(10, 30) == 26); | ||||
|     CHECK(rand.Get(10, 30) == 19); | ||||
|     CHECK(rand.Get(10, 30) == 21); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Random distribution (max 0, min 1)", "[Utilities]") { | ||||
|     auto rand = Arbutils::Random(10); | ||||
|     const int size = 100000; | ||||
|     int arr[size]; | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         arr[i] = rand.Get(0, 1); | ||||
|     } | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         if (arr[i] != 0) | ||||
|             FAIL("We expected a value of 0 here, but got a " + std::to_string(arr[i])); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Random distribution (max 0, min 2)", "[Utilities]") { | ||||
|     auto rand = Arbutils::Random(10); | ||||
|     const int size = 100000; | ||||
|     int arr[size]; | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         arr[i] = rand.Get(0, 2); | ||||
|     } | ||||
|     auto numZeros = 0; | ||||
|     auto numOnes = 0; | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         if (arr[i] != 0 && arr[i] != 1) | ||||
|             FAIL("We expected a value of 0 or 1 here, but got a " + std::to_string(arr[i])); | ||||
|         if (arr[i] == 0) | ||||
|             numZeros++; | ||||
|         else | ||||
|             numOnes++; | ||||
|     } | ||||
|     auto div = static_cast<float>(numZeros) / static_cast<float>(numOnes); | ||||
|     INFO("Distribution: " << numZeros << "/" << numOnes); | ||||
|     CHECK(Approx(div).margin(0.01) == 1); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Random distribution (max 0, min 3)", "[Utilities]") { | ||||
|     auto rand = Arbutils::Random(10); | ||||
|     const int size = 100000; | ||||
|     int arr[size]; | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         arr[i] = rand.Get(0, 3); | ||||
|     } | ||||
|     auto numZeros = 0; | ||||
|     auto numOnes = 0; | ||||
|     auto numTwos = 0; | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         if (arr[i] != 0 && arr[i] != 1 && arr[i] != 2) | ||||
|             FAIL("We expected a value between 0 and 2 here, but got a " + std::to_string(arr[i])); | ||||
|         if (arr[i] == 0) | ||||
|             numZeros++; | ||||
|         else if (arr[i] == 1) | ||||
|             numOnes++; | ||||
|         else | ||||
|             numTwos++; | ||||
|     } | ||||
|     INFO("Distribution: " << numZeros << "/" << numOnes << "/" << numTwos); | ||||
|     CHECK(Approx(static_cast<float>(numZeros) / static_cast<float>(numOnes)).margin(0.01) == 1); | ||||
|     CHECK(Approx(static_cast<float>(numZeros) / static_cast<float>(numTwos)).margin(0.01) == 1); | ||||
|     CHECK(Approx(static_cast<float>(numOnes) / static_cast<float>(numTwos)).margin(0.01) == 1); | ||||
| } | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user