Initial commit.
This commit is contained in:
commit
01f1d65739
|
@ -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
|
||||||
|
...
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
/cmake-build-debug/
|
||||||
|
/cmake-build-release/
|
||||||
|
/.idea/
|
|
@ -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)
|
|
@ -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"]
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
||||||
|
#include "ConstString.hpp"
|
|
@ -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
|
|
@ -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; \
|
||||||
|
} \
|
||||||
|
};
|
|
@ -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__)
|
|
@ -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())) {}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue