diff --git a/PKGBUILD b/PKGBUILD index 29a19a2..3d0f7bb 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Matthias Quintern pkgname=gz-cpp-util pkgver=1.0 -pkgrel=1 +pkgrel=2 pkgdesc="Utility library for c++" arch=('any') url="https://github.com/MatthiasQuintern/gz-cpp-util" diff --git a/README.md b/README.md index 4b8273c..2e20213 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # gz-cpp-util +cpp-20 utility library for my projects ## Features - Extensive logger using variadic templates to log almost anything @@ -23,7 +24,7 @@ - Include the wanted header: `#include ` ### Documentation The documentation for this library can be generated using **doxygen**. -Install doxygen and run `make docs`. +Install doxygen and run `make docs`, then open `docs/html/index.html`. ## Changelog diff --git a/generate_vec.py b/generate_vec.py new file mode 100644 index 0000000..7660689 --- /dev/null +++ b/generate_vec.py @@ -0,0 +1,374 @@ +""" +A python script to generate a c++ vector library for math +Should work with vectors with 2-9 components +2022 Matthias Quintern +""" + +from os import path +# +# SETTINGS +# +vectors = [2, 3, 4] # , 5, 6, 7, 8, 9] +letters_ = { + 2: [ "x", "y" ], + 3: [ "x", "y", "z" ], + 4: [ "x", "y", "z", "w"], +} +# \t or x-spaces +tab = "\t" +# float, double, long double +float_type = "float" +# string or None +namespace = "gz" +types = { + "float": "f", + "double": "d", + "int": "i", + "unsigned int": "u", +} +filename = "vec.hpp" +# for genstring +templateStr = "@" + +# +# HELPERS +# +def classname(n): + return "vec" + str(n) + +def letters(n, i): + if n in letters_: + return letters_[n][i] + else: + return f"x{i}" + + +def genstring(n: int, template: str, sep=", ", offset=0): + """ + Generate a string from a template for each vector component + eg genstring(3, "@ + ") -> x + y + z + """ + s = "" + for i in range(n): + s += template.replace(templateStr, letters(n, i + offset)) + sep + s.removesuffix(sep) + return s[0:len(s)-len(sep)] + +def transformString(s: str, depth: int): + """Add tabs after all but the last line break and before the first char""" + return depth * tab + s.replace("\n", "\n" + depth * tab, s.count("\n") - 1) + +def getPossiblities(i, a, depth=0): + """ + get all possibilites to get i by addition of numbers lower than i + eg i=3 -> 3, 2+1, 1+2, 1+1+1 + """ + if i == 0: + return + if i == 1: + a.append(str(1)) + return + for j in range(1, i): + b = [] + # print("\t" * depth + "gp: i="+str(i)+" j="+str(j)) + getPossiblities(i-j, b, depth+1) + + for poss in b: + # print("\t"*depth + f"{i}-{j} returned", b) + a.append(str(j) + poss) + a.append(str(i)) + + + +# +# GENERATORS +# +def genConstructors(n): + constructors = [] + s = "/// Default constructor\n" + s += classname(n) + "() : " + genstring(n, "@(0)") + " {};\n" + constructors.append(s) + + s = "/// Create vector from scalar, all components will have value n\n" + s += "template\n" + s += "(const N n) : " + genstring(n, "@(static_cast(n))") + " {};\n"; + + a = [] + getPossiblities(n, a) + for possiblity in a: + n_count = 0 + v_count = 0 + i = 0 + comment = "/// Create a " + classname(n) + " from " + templates = "template<" + args = classname(n) + "(" + init = " : " + + for nstr in possiblity: + c = int(nstr) + if c == 1: + comment += "n " + templates += f"typename N{n_count}, " + args += f"N{n_count} n{n_count}, " + init += letters(n, i) + f"(static_cast(n{n_count})), " + n_count += 1 + i += 1 + else: + comment += f"vec{c} " + args += "const " + classname(c) + f"& v{v_count}, " + templates += f"typename V{v_count}, " + for j in range(c): + init += letters(n, i) + "(static_cast(v" + str(v_count) + "." + letters(n, j) + ")), " + i += 1 + v_count += 1 + templates = templates.removesuffix(", ") + ">" + args = args.removesuffix(", ") + ")" + init = init.removesuffix(", ") + " {};" + + s = comment + "\n" + templates + "\n" + args + init + "\n" + constructors.append(s) + + return constructors + + +def genValues(n): + s = genstring(n, "T @;\n", "") + return s + +def genAssignment(n): + s = "/// component-wise assignment\n" + s += "template\n" + s += "void operator=(const " + classname(n) + "& other) {\n" + s += genstring(n, f"\t@ = static_cast(other.@);\n", "") + "};\n\n" + s += "template\n" + s += "void operator=(const N& other) {\n" + s += genstring(n, f"\t@ = static_cast(other);\n", "") + "};\n\n" + + return s + + + +def genArithmeticVectorial(n): + operators = [] + for op in ["+", "-", "*", "/", "%"]: + s = "/// component-wise " + op + "\n" + s += "template\n" + s += classname(n) + " operator" + op + "(const " + classname(n) + "& other) const { return " + s += classname(n) + "(" + genstring(n, f"@ {op} static_cast(other.@)") + "); };\n" + operators.append(s) + operators.append("\n") + + for op in ["+=", "-=", "*=", "/=", "%="]: + s = "/// component-wise assignment" + op + "\n" + s += "template\n" + s += "void operator" + op + "(const " + classname(n) + "& other) {\n" + s += genstring(n, f"\t@ {op} static_cast(other.@);\n", "") + "};\n" + operators.append(s) + operators.append("\n") + + return operators + +def genArithmeticScalar(n): + operators = [] + for op in ["+", "-", "*", "/", "%"]: + s = "/// component-wise " + op + "\n" + s += "template\n" + s += classname(n) + " operator" + op + "(const N& other) const { return " + s += classname(n) + "(" + genstring(n, f"@ {op} static_cast(other.@)") + "); };\n" + operators.append(s) + operators.append("\n") + for op in ["+=", "-=", "*=", "/=", "%="]: + s = "/// component-wise assignment" + op + "\n" + s += "template\n" + s += "void operator" + op + "(const N& other) {\n" + s += genstring(n, f"\t@ {op} static_cast(other.@);\n", "") + "};\n" + operators.append(s) + operators.append("\n") + return operators + +def genComparisonVectorial(n): + operators = [] + for op in ["==", "<", ">"]: + s = "/// component-wise comparison " + op + " (and)\n" + s += "template\n" + s += "bool operator" + op + "(const " + classname(n) + "& other) const { return " + s += genstring(n, f"@ {op} other.@", " and ") + "; };\n" + operators.append(s) + operators.append("\n") + for op, antiop in { "!=": "==", "<=": ">", ">=": "<" }.items(): + s = "/// component-wise comparison " + op + " (and)\n" + s += "template\n" + s += "bool operator" + op + "(const " + classname(n) + "& other) const { return " + s += genstring(n, f"@ {antiop} other.@", " and ") + "; };\n" + operators.append(s) + operators.append("\n") + return operators + +def genComparisonScalar(n): + operators = [] + for op in ["==", "<", ">"]: + s = "/// component-wise comparison " + op + " (and)\n" + s += "template\n" + s += "bool operator" + op + "(const N& other) const { return " + s += genstring(n, f"@ {op} other.@", " and ") + "; };\n" + operators.append(s) + operators.append("\n") + for op, antiop in { "!=": "==", "<=": ">", ">=": "<" }.items(): + s = "/// component-wise comparison " + op + " (and)\n" + s += "template\n" + s += "bool operator" + op + "(const N& other) const { return " + s += genstring(n, f"@ {antiop} other.@", " and ") + "; };\n" + operators.append(s) + return operators + +def genIterator(n): + s = """struct Iterator { + public: + using value_type = T; + Iterator() : ptr(nullptr) {}; + Iterator(T* ptr) : ptr(ptr) {}; + T& operator*() { return *ptr; }; + Iterator& operator=(const Iterator& other) { + ptr = other.ptr; + return *this; + }; + Iterator& operator++() { ptr += sizeof(T); return *this; }; + Iterator operator++(int) { auto copy = *this; ptr += sizeof(T); return copy; }; + friend int operator-(Iterator lhs, Iterator rhs) { + return lhs.ptr - rhs.ptr; + }; + bool operator==(const Iterator& other) const { return ptr == other.ptr; }; + // bool operator!=(const Iterator& other) const { return ptr != other.ptr; }; + private: + T* ptr; +}; +""" + s += "const Iterator cbegin() const { return Iterator(&" + letters(n, 0) + "); };\n" + s += "const Iterator cend() const { return Iterator(&" + letters(n, n-1) + "); };\n" + s += "const Iterator begin() const { return Iterator(&" + letters(n, 0) + "); };\n" + s += "const Iterator end() const { return Iterator(&" + letters(n, n-1) + "); };\n\n" + return s + + +def genFunctional(n): + s = "/// Returns the absolute value of the vector\n" + s += "inline " + float_type + " abs() const { return std::sqrt(" + genstring(n, f"static_cast<{float_type}>(@ * @)", " + ") + "); };" + + if n == 2: + s += "/// Returns x/y\n" + s += "inline " + float_type + " ratio() const { return static_cast<" + float_type + ">(x) / y; };" + s += "/// Returns y/x\n" + s += "inline " + float_type + " inverseRatio() const { return static_cast<" + float_type + ">(y) / x; };" + + s += "/// Returns the min of the components\n" + s += "inline T min() const { return std::min_element(cbegin(), cend()); };\n" + + s += "/// Returns the max of the components\n" + s += "inline T max() const { return std::max_element(cbegin(), cend()); };\n" + + s += "/// Scalar product\n" + s += "template\n" + s += "inline " + classname(n) + " dot(const " + classname(n) + "& other) { return " + classname(n) + "(" + s += genstring(n, "@ * static_cast(other.@)", " + ") + "); };\n" + + return s + +def genUtility(n): + s = "std::string to_string() const { return \"(\" + " + genstring(n, "std::to_string(@)", " + \", \" + ") + " + \")\"; };\n" + + return s + +def genUsing(n): + global types + s = "" + for t, c in types.items(): + s += f"using {classname(n)}{c} = {classname(n)}<{t}>;\n" + return s + +def generateFile(vectors): + depth = 0 + s = "#pragma once\n\n#include \n#include \n#include \n\n" + if namespace: + s += "namespace " + namespace + " {\n" + depth = 1 + for v in vectors: + s += generateVector(v, depth) + s += "\n" + for v in vectors: + s += transformString(genUsing(v), depth) + s += "\n" + if namespace: + depth -= 1 + s += transformString("} // namespace " + namespace + "\n", depth) + + for i in range(1, 5): + s = s.replace("\n" + i * tab + "\n", "\n\n") + + return s + + + +def generateVector(n, depth): + s = transformString("""/** + * @brief Class containing $ numbers + */ +template +class vec$ { + public: +""".replace("$", str(n)), depth) + + depth += 1 + + s += transformString("// Constructors\n", depth) + for c in genConstructors(n): + s += transformString(c, depth + 1) + + s += transformString("// Values\n", depth) + s += transformString(genValues(n), depth + 1) + + s += transformString("// Assignment\n", depth) + s += transformString(genAssignment(n), depth + 1) + + s += transformString("// Arithmetic\n", depth - 1) + s += transformString("// Vectorial\n", depth) + for o in genArithmeticVectorial(n): + s += transformString(o, depth + 1) + s += transformString("// Scalar\n", depth) + for o in genArithmeticScalar(n): + s += transformString(o, depth + 1) + + s += transformString("// Comparison\n", depth - 1) + s += transformString("// Vectorial\n", depth) + for o in genComparisonVectorial(n): + s += transformString(o, depth + 1) + s += transformString("// Scalar\n", depth) + for o in genComparisonScalar(n): + s += transformString(o, depth + 1) + + s += transformString("// Functional\n", depth - 1) + s += transformString(genFunctional(n), depth + 1) + + s += transformString("// Utility\n", depth - 1) + s += transformString(genUtility(n), depth + 1) + s += transformString(genIterator(n), depth + 1) + + s += transformString("}; // vec" + str(n) + "\n", depth - 1) + + return s + + +def write_file(s): + global filename + if path.exists(filename): + answer = input("File " + filename + "already exists. Overwrite? (y/n): ") + if answer not in "yY": + return + + with open(filename, "w") as file: + file.write(s) + +if __name__ == "__main__": + write_file(generateFile(vectors)) + + + diff --git a/src/Container/queue.hpp b/pkg/usr/include/gz-util/Container/queue.hpp similarity index 100% rename from src/Container/queue.hpp rename to pkg/usr/include/gz-util/Container/queue.hpp diff --git a/src/Container/ringbuffer.hpp b/pkg/usr/include/gz-util/Container/ringbuffer.hpp similarity index 100% rename from src/Container/ringbuffer.hpp rename to pkg/usr/include/gz-util/Container/ringbuffer.hpp diff --git a/src/Math/vec.hpp b/pkg/usr/include/gz-util/Math/vec.hpp similarity index 100% rename from src/Math/vec.hpp rename to pkg/usr/include/gz-util/Math/vec.hpp diff --git a/src/Util/gz_regex.hpp b/pkg/usr/include/gz-util/Util/gz_regex.hpp similarity index 100% rename from src/Util/gz_regex.hpp rename to pkg/usr/include/gz-util/Util/gz_regex.hpp diff --git a/src/Util/util.hpp b/pkg/usr/include/gz-util/Util/util.hpp similarity index 100% rename from src/Util/util.hpp rename to pkg/usr/include/gz-util/Util/util.hpp diff --git a/pkg/usr/include/gz-util/log.hpp b/pkg/usr/include/gz-util/log.hpp new file mode 100644 index 0000000..342ca19 --- /dev/null +++ b/pkg/usr/include/gz-util/log.hpp @@ -0,0 +1,345 @@ +#pragma once + +#define LOG_MULTITHREAD + +#include +#include +#include +#include +#include + +#ifdef LOG_MULTITHREAD +#include +#endif + +namespace gz { + inline const char* boolToString(bool b) { + return b ? "true" : "false"; + } + + const int logLength = 100; + + constexpr unsigned int TIMESTAMP_CHAR_COUNT = 22; + constexpr unsigned int POSTPREFIX_CHAR_COUNT = 2; + + // + // CONCEPTS + // + /// is std::string or convertible to std::string + template + concept Stringy = std::same_as || std::convertible_to; + + /// has .to_string() member + template + concept HasToString = !Stringy && requires(T t) { { t.to_string() }-> Stringy; }; + + /// works with std::to_string(), except bool + template + concept WorksToString = !std::same_as && !Stringy && !HasToString && requires(T t) { { std::to_string(t) } -> Stringy; }; + + /// string-like, has .to_string() member, works with std::to_string() or bool + template + concept PrintableNoPtr = Stringy || HasToString || WorksToString || std::same_as; + + template + concept Printable = PrintableNoPtr || requires(T t) { { *(t.get()) } -> PrintableNoPtr; }; + + /// Type having printable .x and .y members + template + concept Vector2Printable = !Printable && + requires(T t) { { t.x } -> Printable; { t.y } -> Printable; }; + + /// Pair having printable elements + template + concept PairPrintable = !Vector2Printable && !Printable && + requires(T p) { { p.first } -> Printable; } && (requires(T p){ { p.second } -> Printable; } || requires(T p){ { p.second } -> Vector2Printable; }); + + /// Container having printable elements + template + concept ContainerPrintable = !Printable && !Vector2Printable && !PairPrintable && + std::ranges::forward_range && (Printable> || Vector2Printable>); + + /// Container having printable pairs + template + concept MapPrintable = !Printable && !Vector2Printable && !ContainerPrintable && + std::ranges::forward_range && PairPrintable>; + + template + concept LogableNotPointer = Printable || Vector2Printable || PairPrintable || ContainerPrintable || MapPrintable; + + template + concept LogableSmartPointer = requires(T t) { { *(t.get()) } -> LogableNotPointer; }; + + + // + // COLORS + // + /// Colors to be used in Log::clog + enum Color { + RESET, BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, BBLACK, BRED, BGREEN, BYELLOW, BBLUE, BMAGENTA, BCYAN, BWHITE + }; + extern const char* COLORS[]; + + + // + // LOG + // + /** + * @brief Define types that can be logged with Log + * @details + * As of now you can log type T with instance t: + * -# Any string-like type + * -# Any type that works with std::to_string() + * -# Any type that has a to_string() member that returns a string + * -# Any type with t.x and t.y, provided t.x and t.y satisfy one of 1-3 + * -# Any type with t.first, t.second provided t.first satisfies one of 1-3 and t.second satisfies 1-4 + * -# Any type that has a forward_iterator which references any one of 1-5 + * + * 1-6 include for example: + * - int, float, bool... + * - std::vector, std::list + * - std::map> if A.to_string() returns a string + * - ... + */ + template + concept Logable = LogableNotPointer || LogableSmartPointer; + + template + class vec2; // defined in gz_math.hpp + +/** + * @brief Manages printing messages to stdout and to logfiles. + * @details + * @subsection log_threads Thread safety + * Log can use a static mutex for thread safety. To use this feature, you have to #define LOG_MULTITHREAD at the top of log.hpp. + * Note that log uses the default std::cout buffer, so you should make sure it is not being used while logging something. + */ +class Log { + public: + /** + * @brief Creates a log object, which can print messages to stdout and/or write them to a log file + * @details By creating multiple instances with different parameters, logs can be easily turned on/off for different usages. + * + * @param logfile: Name of the file in the logs folder + * @param showLog: Wether to print the messages to stdout + * @param storeLog: Wether to save the messages to the logfile + * @param prefix: A prefix that comes between the timestamp and the message. ": " is automatically appended to the prefix + * @param prefixColor: The color of the prefix + * + * @note Colors will only be shown when written to stdout, not in the logfile. + */ + Log(std::string logfile="log.log", bool showLog=true, bool storeLog=true, std::string&& prefix="", Color prefixColor=RESET); + + ~Log(); + + /** + * @brief Logs a message + * @details Depending on the settings of the log instance, the message will be printed to stdout and/or written to the logfile. + * The current date and time is placed before the message. + * The message will look like this: + *