diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f60158..9473a5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,13 +48,12 @@ if(NB_BUILD_TESTS) include(FetchContent) FetchContent_Declare( gtest - URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip + URL https://github.com/google/googletest/archive/refs/heads/main.zip ) if (WIN32) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(gtest) endif() - include(GoogleTest) set(GTEST_COLOR ON) set(GLFW_BUILD_TESTS ON CACHE BOOL "" FORCE) endif() @@ -62,6 +61,7 @@ endif() if (NB_LOGGING) message(STATUS "Building with automatic logging") add_compile_definitions(_NB_AUTOLOG) + add_compile_definitions(_NB_CODE_ERROR_LOCATIONS) endif() if (NB_TARGET_WINDOWS) diff --git a/engine/NBCore/ANSITerm.hpp b/engine/NBCore/ANSITerm.hpp index b74ee7b..b729bac 100644 --- a/engine/NBCore/ANSITerm.hpp +++ b/engine/NBCore/ANSITerm.hpp @@ -2,16 +2,10 @@ #ifndef _NB_ANSI_TERM #define _NB_ANSI_TERM -#include -#include -#include #include #include -#include #include -#include "TypeTraits.hpp" - /* ----------- TECH DEBT ------------ Idk wtf to do here. This was originally to allow me to print diff --git a/engine/NBCore/CMakeLists.txt b/engine/NBCore/CMakeLists.txt index 6b105df..76e9249 100644 --- a/engine/NBCore/CMakeLists.txt +++ b/engine/NBCore/CMakeLists.txt @@ -1,20 +1,23 @@ -include_directories(./.) - toAbsolutePath(NB_CORE_SOURCE - ./src/Errors.cpp - ./src/Processes.cpp + ./src/ErrorsImpl.cpp ./src/Logger.cpp + ./src/Processes.cpp + ./src/StringUtils.cpp + ./src/Utils.cpp ) toAbsolutePath(NB_CORE_INCLUDE ./ANSITerm.hpp ./DataSink.hpp ./Errors.hpp + ./ErrorsImpl.hpp ./Logger.hpp ./Processes.hpp + ./StringUtils.hpp ./ThreadsafeQueue.hpp ./Types.hpp ./TypeTraits.hpp + ./Utils.hpp ) set(NB_CORE_SOURCE ${NB_CORE_SOURCE} PARENT_SCOPE) @@ -22,6 +25,8 @@ set(NB_CORE_INCLUDE ${NB_CORE_INCLUDE} PARENT_SCOPE) add_library(NBCore ${NB_CORE_SOURCE}) +target_include_directories(NBCore PUBLIC ./.) + if (NB_BUILD_TESTS) add_subdirectory(./tests) endif() \ No newline at end of file diff --git a/engine/NBCore/DataSink.hpp b/engine/NBCore/DataSink.hpp index 75cbad6..155c48a 100644 --- a/engine/NBCore/DataSink.hpp +++ b/engine/NBCore/DataSink.hpp @@ -2,6 +2,7 @@ #ifndef _NB_DATASINK #define _NB_DATASINK +#include #include #include "ThreadSafeQueue.hpp" @@ -34,9 +35,9 @@ class BufferedDataProcessor : public DataSink { public: using Base::Base; - bool stop() noexcept { return type_ptr->stop(); } - bool run() { return type_ptr->run(); } - bool in(const DataType& val) { return type_ptr->in(val); } + virtual bool stop() noexcept override { return type_ptr->stop(); } + virtual bool run() override { return type_ptr->run(); } + virtual bool in(const DataType& val) override { return type_ptr->in(val); } protected: unsigned int count() const { @@ -71,13 +72,13 @@ template class MultithreadedDataProcessor : public BufferedDataProcessor, ProcessorType> { using Base = BufferedDataProcessor, ProcessorType>; -public: + public: ~MultithreadedDataProcessor() { type_ptr->stop(); } bool isRunning() const noexcept override { - return this->_running && (_runningThread!=nullptr); + return this->_running; } - bool run() { + bool run() override { if (!type_ptr->isRunning()) { this->_running = true; _runningThread = std::make_shared([&]{ @@ -97,7 +98,7 @@ public: return !type_ptr->isRunning(); } -protected: + protected: using Base::Base; unsigned int count() const { diff --git a/engine/NBCore/Errors.hpp b/engine/NBCore/Errors.hpp index a893b9b..8a6db60 100644 --- a/engine/NBCore/Errors.hpp +++ b/engine/NBCore/Errors.hpp @@ -2,138 +2,42 @@ #ifndef _NB_ERROR #define _NB_ERROR -#include -#include -#include -#include -#include - -#ifndef THROW_WITH_INFO - #ifdef CODE_ERROR_LOCATIONS - #define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__) - #else - #define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__) - #endif // CODE_ERROR_LOCATIONS -#endif // THROW_WITH_INFO - -#ifndef THROW - #ifdef LOGGING - #define THROW_WTIH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__) - #else - #define THROW(...) THROW_WITH_INFO(__VA_ARGS__) - #endif // LOGGING -#endif // THROW +#include "ErrorsImpl.hpp" +#include "Logger.hpp" namespace nb { -typedef std::unordered_map ErrorCodeMap; - -template -class ErrorBase : public std::exception { -public: - ErrorBase( - const unsigned int code, - unsigned int line=0, - std::string filename="" - ) noexcept : ErrorBase( - code, - ErrorBase::lookup(code), - line, - filename - ) {} - ErrorBase( - const unsigned int code, - const std::exception& trace, - unsigned int line=0, - std::string filename="" - ) noexcept : ErrorBase( - code, - ErrorBase::lookup(code), - trace, - line, - filename - ) {} - - static std::string lookup(unsigned int); - unsigned int code() const noexcept { - return static_cast(this)->_code; - }; - virtual const char* what() const noexcept override final { return _msg.c_str(); }; - -protected: - ErrorBase() = default; - ErrorBase( - const unsigned int code, - std::string msg, - unsigned int line=0, - std::string filename="" - ) noexcept : _code{code}, _msg{""} { - static_assert(std::is_same, decltype(ErrorType::ErrorMessages)>::value, - "const std::unordered_map ErrorMessages must be " - "a class member." - ); - static_assert(std::is_enum_v, "enum ErrorCodes must be a class member."); - static_assert(std::is_same, unsigned int>::value, - "enum ErrorCodes must be of underlying type unsigned int." - ); - static_assert(std::is_same::value, - "const std::string type must be a class member." - ); - - _msg += std::string(ErrorType::type); - _msg += "[" + std::to_string(_code) + "]"; - if (line && filename.size()) { - _msg += " in \'" + filename + "\':" + std::to_string(line); - } - _msg += "\n " + msg; - } - ErrorBase( - const unsigned int code, - std::string msg, - const std::exception& trace, - unsigned int line=0, - std::string filename="" - ) noexcept : ErrorBase(code, msg, line, filename) { - std::string what_msg(trace.what()); - std::string::size_type newline_pos=-1; - while((newline_pos=what_msg.find("\n", newline_pos+1))!= std::string::npos) { - what_msg.replace(newline_pos, 1, "\n "); - } - _msg += "\n Trace - " + what_msg; - } - - unsigned int _code; - std::string _msg; -}; - -class Error : public ErrorBase { -public: - enum ErrorCodes : unsigned int { - GENERAL, UNDEFINED, BADERRORCODE - }; - - using ErrorBase::ErrorBase; - - friend ErrorBase; - -protected: - static const std::string type; - static const ErrorCodeMap ErrorMessages; - -}; - -template -std::string ErrorBase::lookup(unsigned int code) { - for (auto kv : ErrorType::ErrorMessages) { - if (kv.first==code) { - return std::string(kv.second); - } - } - - throw Error(Error::ErrorCodes::BADERRORCODE); -} +#ifdef _NB_AUTOLOG + #ifdef _NB_CODE_ERROR_LOCATIONS + #ifndef LOG + #define LOG(args...) nb::logger.log(args, __FILE__, __LINE__) + #endif // LOG + #ifndef WARN + #define WARN(args...) nb::logger.warn(args, __FILE__, __LINE__) + #endif // WARN + #ifndef ERROR + #define ERROR(args...) nb::logger.error(args, __FILE__, __LINE__) + #endif // ERROR + #else + #ifndef LOG + #define LOG(args...) nb::logger.log(args) + #endif // LOG + #ifndef WARN + #define WARN(args...) nb::logger.warn(args) + #endif // WARN + #ifndef ERROR + #define ERROR(args...) nb::logger.error(args) + #endif // ERROR + #endif // _NB_CODE_ERROR_LOCATIONS +#endif // _NB_AUTOLOG +#ifndef THROW + #ifdef _NB_AUTOLOG + #define THROW(args...) ERROR(args); nb::logger.stop(); throw args + #else + #define THROW(args...) throw args + #endif // _NB_CODE_ERROR_LOCATIONS +#endif // THROW } // namespace nb - #endif // _NB_ERROR \ No newline at end of file diff --git a/engine/NBCore/ErrorsImpl.hpp b/engine/NBCore/ErrorsImpl.hpp new file mode 100644 index 0000000..c5b8589 --- /dev/null +++ b/engine/NBCore/ErrorsImpl.hpp @@ -0,0 +1,175 @@ +#pragma once +#ifndef _NB_ERRORS_IMPL +#define _NB_ERRORS_IMPL + +#include +#include +#include +#include +#include + +#include "StringUtils.hpp" +#include "TypeTraits.hpp" + +namespace nb { + +typedef std::unordered_map ErrorCodeMap; + +template +class Error; + +class ErrorBase { + protected: + + public: + const unsigned int code; + const std::string msg; + const std::string type; + const std::shared_ptr trace; + + ErrorBase(const ErrorBase&) = default; + ErrorBase(const std::exception&) noexcept; + virtual std::string what() const noexcept { + std::string ret = msg; + if (trace) { + std::string trace_msg = msg; + ret += nb::NEWLINE + indent_strblock( + trace_msg, + nb::TABOVER, + nb::TABOVER+"Trace: " + ); + } + return ret; + } + + protected: + ErrorBase( + unsigned int code_, + std::string msg_, + std::string type_, + std::shared_ptr trace_ +) noexcept : + code(code_), + msg(msg_), + type(type_), + trace{trace_} +{} +}; + +template +class Error : public ErrorBase { + private: + void inline check_asserts() { + static_assert(std::is_same::value, + "const std::unordered_map ErrorMessages must be " + "a class member." + ); + static_assert(std::is_enum_v, "enum Codes must be a class member."); + static_assert(std::is_same, unsigned int>::value, + "enum Codes must be of underlying type unsigned int." + ); + static_assert(std::is_same::value, + "const std::string type must be a class member." + ); + } + + protected: + using ErrorBase::ErrorBase; + + public: + Error( + unsigned int code_, + const ErrorBase& trace_ + ) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_), + ErrorType::type, + std::make_shared(trace_) + ) { check_asserts(); } + + Error( + std::string msg_, + const ErrorBase& trace_ + ) noexcept : ErrorBase( + 0, + msg_, + ErrorType::type, + std::make_shared(trace_) + ) { check_asserts(); } + + Error(unsigned int code_) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_), + ErrorType::type, + nullptr + ) { check_asserts(); } + + Error(std::string msg_) noexcept : ErrorBase( + 0, + msg_, + ErrorType::type, + nullptr + ) { check_asserts(); } + + Error( + unsigned int code_, + const std::string& note_, + const ErrorBase& trace_ + ) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_) + " (" + note_ + ")", + ErrorType::type, + std::make_shared(trace_) + ) { check_asserts(); } + + Error(unsigned int code_, const std::string& note_) noexcept : ErrorBase( + code_, + ErrorType::ErrorMessages.at(code_) + " (" + note_ + ")", + ErrorType::type, + nullptr + ) { check_asserts(); } + + using ErrorBase::what; + using ErrorBase::code; + using ErrorBase::msg; + using ErrorBase::trace; + using ErrorBase::type; +}; + + +template<> +class Error : public Error> { + using Base = Error>; + + public: + using Base::Base; + + Error(unsigned int code_=1) noexcept : Base(code_) {} + + Error(const std::exception& err) : Base( + Codes::STANDARD, + std::string(err.what()), + "std::exception", + nullptr + ) {} + + // fix + Error(std::string msg_) noexcept : Base( + Codes::UNDEFINED, + msg_ + ) {} + + enum Codes : unsigned int { + STANDARD, UNDEFINED + }; + + using Base::what; + using Base::code; + using Base::msg; + using Base::trace; + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; +} // namespace nb + +#endif // _NB_ERRORS_IMPL \ No newline at end of file diff --git a/engine/NBCore/Logger.hpp b/engine/NBCore/Logger.hpp index e767eea..f4e79be 100644 --- a/engine/NBCore/Logger.hpp +++ b/engine/NBCore/Logger.hpp @@ -2,35 +2,27 @@ #ifndef _NB_LOGGER #define _NB_LOGGER -#include #include -#include #include #include -#include #include #include #include "DataSink.hpp" +#include "ErrorsImpl.hpp" #include "Processes.hpp" #include "ThreadSafeQueue.hpp" #include "TypeTraits.hpp" namespace nb { +class ErrorBase; + typedef std::chrono::time_point< std::chrono::system_clock, std::chrono::nanoseconds > LoggerTimePoint; -struct LogEvent{ - const LoggerTimePoint time; - const unsigned char lvl; - const std::string msg; - const std::thread::id tid; - const uint64_t pid; -}; - typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&); typedef std::unordered_map LogProcessFunctionMap; @@ -40,26 +32,36 @@ class LoggerBase using StreamType = ST; using LoggerType = Logger; using Base = MultithreadedDataProcessor; -public: - bool run() { - if (!type_ptr->isRunning()) { + + public: + bool run() override { + if (!static_cast(this)->isRunning()) { this->_running = true; this->_runningThread = std::make_shared([&]{ - while(type_ptr->isRunning()) { - type_ptr->flush(); + while(static_cast(this)->isRunning()) { + static_cast(this)->flush(); } - type_ptr->flush(); + static_cast(this)->flush(); }); } - return type_ptr->isRunning(); + return static_cast(this)->isRunning(); } -protected: + using Base::flush; + + protected: LoggerBase() = default; StreamType _ostream; +}; -private: - LoggerType* type_ptr = static_cast(this); +struct LogEvent{ + const LoggerTimePoint time; + const unsigned char lvl; + const std::string msg; + const std::thread::id tid; + const uint64_t pid; + const std::string file=""; + const unsigned int line=0; }; template @@ -72,39 +74,70 @@ public: template DebugLogger(ST&... streams) : _ostream(nb::RefPackToPtrVec(streams...).vec) {} - ~DebugLogger() { type_ptr->stop(); } + ~DebugLogger() { static_cast(this)->stop(); } - void log(const std::string& msg, const uint8_t& lvl=0xFF) { - type_ptr->push(LogEvent{ + template + void log( + U val, + std::string file="", + unsigned int line=0 + ) { static_cast(this)->write_message(val, 0x00, file, line); } + + template + void warn( + U val, + uint8_t lvl=0x01, + std::string file="", + unsigned int line=0 + ) { static_cast(this)->write_message(val, lvl, file, line); } + + void error( + const ErrorBase& val, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->write_message(val, 0xFF, file, line); + static_cast(this)->flush(); + } + + protected: + std::vector _ostream; + void write_message( + std::string msg, + uint8_t lvl=0x00, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->push(LogEvent{ std::chrono::system_clock::now(), lvl, msg, std::this_thread::get_id(), GetPID(), + file, + line }); } template - void log(char const(&msg) [N], const uint8_t& lvl=0xFF) { - type_ptr->log(std::string(msg), lvl); + void write_message( + char const(&msg) [N], + uint8_t lvl=0x00, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->write_message(std::string(msg), lvl, file, line); } - void log(const std::exception& err, const uint8_t& lvl=0xFF) { - type_ptr->log(err.what(), lvl); + void write_message( + const ErrorBase& err, + uint8_t lvl=0x00, + std::string file="", + unsigned int line=0 + ) { + static_cast(this)->write_message(err.what(), lvl, file, line); } - template - void warn(const U& val, const uint8_t& lvl=0x01) { type_ptr->log(val, lvl); } - - template - void error(const U& val) { type_ptr->log(val, 0xFF); } - -protected: - std::vector _ostream; - -private: - LoggerType* type_ptr = static_cast(this); - }; class DefaultDebugLogger : public DebugLogger { @@ -114,7 +147,7 @@ class DefaultDebugLogger : public DebugLogger { template struct LogRow; -public: + public: using Base::Base; ~DefaultDebugLogger() { stop(); } @@ -127,17 +160,17 @@ public: protected: using Base::_ostream; - bool process(const LogEvent& msg) { - for (const auto os : this->_ostream) { - *os << msg.lvl << "\t|\t" << msg.msg << "\n"; - } - return true; - } + bool process(const LogEvent& msg); }; +extern const bool LOGGER_RUNNING; + +#ifndef _NB_NO_LOGGER extern DefaultDebugLogger logger; +#endif // _NB_NO_LOGGER // Taking Charge of Adult ADHD by Russell Barkley } // namespace nb + #endif // _NB_LOGGER \ No newline at end of file diff --git a/engine/NBCore/Processes.hpp b/engine/NBCore/Processes.hpp index 87ee029..87c16ee 100644 --- a/engine/NBCore/Processes.hpp +++ b/engine/NBCore/Processes.hpp @@ -7,6 +7,8 @@ namespace nb { uint64_t GetPID(); +uint64_t getTID(); + } // namespace nb diff --git a/engine/NBCore/StringUtils.hpp b/engine/NBCore/StringUtils.hpp new file mode 100644 index 0000000..b8d9778 --- /dev/null +++ b/engine/NBCore/StringUtils.hpp @@ -0,0 +1,79 @@ +#pragma once +#ifndef _NB_STRING_UTILS +#define _NB_STRING_UTILS + +#include +#include +#include + +#include "TypeTraits.hpp" + +namespace nb { + +#ifdef _NB_TARGET_WINDOWS + const std::string NEWLINE = "\n"; + const std::wstring WNEWLINE = L"\n"; +#endif // _NB_TARGET_WINDOWS +#ifdef _NB_TARGET_LINUX + const std::string NEWLINE = "\n"; + const std::wstring WNEWLINE = L"\n"; +#endif // _NB_TARGET_LINUX + +const std::string TABOVER = " "; + +// std::wstring str_to_wstr(std::string in); + +// std::string wstr_to_str(std::wstring in); + +template +void stream(const Stream& s, Args&&... args); + +template +void stream(const Stream& s, Args&&... args) { + (s << ... << args); +} + +template +void term(Args&&... args) { stream(std::cout, args..., nb::NEWLINE); } + +template +void wterm(Args&&... args) { stream(std::wcout, args..., nb::WNEWLINE); } + +template +std::basic_string find_and_replace( + ExplicitType_t> original, + ExplicitType_t> find, + ExplicitType_t> replace +) { + using StringType = std::basic_string; + + StringType ret(original); + + std::size_t find_len = find.length(); + std::size_t replace_len = replace.length(); + std::size_t currpos = 0; + while(true) { + currpos = ret.find(find, currpos); + if (currpos == StringType::npos) { + break; + } + ret = ret.erase(currpos, find_len); + ret = ret.insert(currpos, replace); + currpos += replace_len; + } + return ret; +} + +std::string indent_strblock( + std::string block, + std::string prepend, + std::string topIndent +); + +std::string indent_strblock( + std::string block, + std::string prepend +); + +} // namespace nb +#endif // _NB_STRING_UTILS \ No newline at end of file diff --git a/engine/NBCore/TypeTraits.hpp b/engine/NBCore/TypeTraits.hpp index 5226e94..fb990fe 100644 --- a/engine/NBCore/TypeTraits.hpp +++ b/engine/NBCore/TypeTraits.hpp @@ -2,15 +2,96 @@ #ifndef _NB_TYPE_TRAITS #define _NB_TYPE_TRAITS +#include #include +#include namespace nb { -template +namespace detail { + template < + class Default, + class AlwaysVoid, + template class Op, + class... Args + > + struct detector { + using value = std::false_type; + using type = Default; + }; + + template < + class Default, + template class Op, + class... Args + > + struct detector>, Op, Args...> { + using value = std::true_type; + using type = Op; + }; +} // namespace detail + +struct NoneType{}; + +template < + template class Op, + class... Args +> +using is_detected = typename detail::detector::value; + +template +struct ValidConversion; + +template +struct ValidConversion { + typedef std::conjunction< + std::is_constructible, std::is_constructible, std::conjunction...> + > value_type; + static constexpr bool value = value_type::value; + typedef std::enable_if_t to; + +}; + +template +struct ValidConversion { + typedef std::is_constructible value_type; + static constexpr bool value = value_type::value; + typedef std::enable_if_t to; + typedef std::enable_if_t from; +}; + +template +using ValidConversion_v = typename ValidConversion::value; + +template +using ValidConversion_to = typename ValidConversion::to; + +template +using ValidConversion_from = typename ValidConversion::from; + +template +struct ExplicitType { using type=T; }; + +template +using ExplicitType_t = typename ExplicitType::type; + +template +T* NULLPTR = static_cast(nullptr); + +template +struct RunAndOutput { + using type = T; + T value; + RunAndOutput(Func func, T val) : value(val) { + func(); + } +}; + +template inline typename std::enable_if::type ForEach(std::tuple&, Func) {} -template +template inline typename std::enable_if::type ForEach(std::tuple& tup, Func f) { f(N, std::get(tup)); diff --git a/engine/NBCore/Types.hpp b/engine/NBCore/Types.hpp deleted file mode 100644 index 293bdb5..0000000 --- a/engine/NBCore/Types.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#ifndef _NB_CORE_TYPES -#define _NB_CORE_TYPES - -#include - -namespace nb { - -template -T swapEndian(const T& val) { - T ret; - const int size = sizeof(T); - auto retLoc = static_cast(&ret); - auto valLoc = static_cast(&val); - - for (int i = 0; i < size; ++i) { - memcpy(retLoc+i, valLoc+(size-i-1), 1); - } - return ret; -} - -using ByteVector = std::vector; - -} // namespace nb -#endif // _NB_CORE_TYPES \ No newline at end of file diff --git a/engine/NBCore/Utils.hpp b/engine/NBCore/Utils.hpp new file mode 100644 index 0000000..76ae1ed --- /dev/null +++ b/engine/NBCore/Utils.hpp @@ -0,0 +1,118 @@ +#pragma once +#ifndef _NB_CORE_TYPES +#define _NB_CORE_TYPES + +#include +#include + +#include "Errors.hpp" + +namespace nb { + +template +using SharedVector = std::vector>; + +template +using RValueVector = std::vector; + +using ByteVector = std::vector; + +class ObjectManagerError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, NO_MANAGER, MANAGER_MISMATCH, LOCK_OVERFLOW, BAD_THREAD + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +template +ByteVector vectorToBytes(const std::vector& vec) { + unsigned int num_bytes = vec.size() * sizeof(T); + ByteVector ret(num_bytes); + memcpy(ret.data(), vec.data(), num_bytes); + return ret; +} + +template +ByteVector concatVectorBytes(const std::vector& vec1, const std::vector& vec2) { + ByteVector vec1_raw = vectorToBytes(vec1); + ByteVector vec2_raw = vectorToBytes(vec2); + unsigned int vec1_raw_size = vec1_raw.size(); + unsigned int vec2_raw_size = vec2_raw.size(); + ByteVector ret(vec1_raw_size + vec2_raw_size); + memcpy(ret.data(), vec1_raw.data(), vec1_raw_size); + memcpy(ret.data() + vec1_raw_size, vec2_raw.data(), vec2_raw_size); + return ret; +} + +template +std::vector bytesToVector(const ByteVector& vec) { + if (vec.size() % sizeof(T) != 0) { + THROW(std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">.")); + } + unsigned int num_elmts = vec.size() / sizeof(T); + std::vector ret(num_elmts); + memcpy(ret.data(), vec.data(), vec.size()); + + return ret; +} + +template +class ThreadsafeObjectLock; + +template +class ThreadsafeObject { + using Codes = ObjectManagerError::Codes; + + public: + ThreadsafeObject(T&& obj) + : _obj(std::make_shared(obj)) {} + + ThreadsafeObjectLock lock() { + return ThreadsafeObjectLock(this); + } + + friend ThreadsafeObjectLock; + + protected: + std::shared_ptr _obj; + mutable std::recursive_mutex _mutex; + +}; + +template +class ThreadsafeObjectLock { + public: + ThreadsafeObjectLock(const ThreadsafeObjectLock&) = delete; + ThreadsafeObjectLock operator=(const ThreadsafeObjectLock&) = delete; + ~ThreadsafeObjectLock() { + _manager->_mutex.unlock(); + } + + T* operator->() { + return _manager->_obj.get(); + } + + friend ThreadsafeObject; + + protected: + ThreadsafeObjectLock( + ThreadsafeObject* const manager_ + ) : _manager(manager_) { + if (!_manager) { + using Codes = ObjectManagerError::Codes; + THROW(ObjectManagerError(Codes::NO_MANAGER)); + } + _manager->_mutex.lock(); + } + ThreadsafeObject* const _manager; +}; + +} // namespace nb +#endif // _NB_CORE_TYPES \ No newline at end of file diff --git a/engine/NBCore/src/Errors.cpp b/engine/NBCore/src/Errors.cpp deleted file mode 100644 index 544346a..0000000 --- a/engine/NBCore/src/Errors.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "Errors.hpp" - -namespace nb { - -const std::string Error::type = "Error"; -const ErrorCodeMap Error::ErrorMessages = { - {ErrorCodes::GENERAL, "General std::exception."}, - {ErrorCodes::UNDEFINED, "Undefined / general error."}, - {ErrorCodes::BADERRORCODE, "Unrecognized error code."} -}; - -} diff --git a/engine/NBCore/src/ErrorsImpl.cpp b/engine/NBCore/src/ErrorsImpl.cpp new file mode 100644 index 0000000..a36b110 --- /dev/null +++ b/engine/NBCore/src/ErrorsImpl.cpp @@ -0,0 +1,16 @@ +#include "ErrorsImpl.hpp" + +namespace nb { + +const std::string Error<>::type = "nb::Error"; +const ErrorCodeMap Error<>::ErrorMessages = { + {Error::Codes::STANDARD, "std::exception"}, + {Error::Codes::UNDEFINED, "Error"} +}; + +//ErrorBase:: + +ErrorBase::ErrorBase(const std::exception& exception_) noexcept +: ErrorBase(Error(exception_)) {} + +} diff --git a/engine/NBCore/src/Logger.cpp b/engine/NBCore/src/Logger.cpp index 1deed87..70c8ff8 100644 --- a/engine/NBCore/src/Logger.cpp +++ b/engine/NBCore/src/Logger.cpp @@ -1,15 +1,38 @@ #include #include "Logger.hpp" +#include "StringUtils.hpp" namespace nb { +#ifndef _NB_NO_LOGGER nb::DefaultDebugLogger logger(std::cout); +#endif // _NB_NO_LOGGER + +bool nb::DefaultDebugLogger::process(const LogEvent& msg) { + constexpr size_t level_field_width = 5; + constexpr size_t msg_prompt_length = level_field_width+7; + for (const auto os : this->_ostream) { + *os << "["; + os->width(level_field_width); + *os << std::to_string(msg.lvl) << "] :: "; + std::string fileloc=""; + if (!msg.file.empty()) { + fileloc += "(" + msg.file + ":" + std::to_string(msg.line) + ") - "; + } + *os << indent_strblock( + fileloc + msg.msg, + std::string(20 - msg_prompt_length, ' '), + "" + ) << nb::NEWLINE; + } + return true; +} static bool RUN_LOGGER(nb::DefaultDebugLogger& log) { return log.run(); } -static const bool LOGGER_RUNNING = RUN_LOGGER(logger); +const bool LOGGER_RUNNING = RUN_LOGGER(logger); } // namespace nb \ No newline at end of file diff --git a/engine/NBCore/src/Processes.cpp b/engine/NBCore/src/Processes.cpp index dabf222..f0aec20 100644 --- a/engine/NBCore/src/Processes.cpp +++ b/engine/NBCore/src/Processes.cpp @@ -7,7 +7,6 @@ #endif // _NB_TARGET_LINUX #include "Processes.hpp" -#include "Types.hpp" namespace nb { @@ -16,8 +15,15 @@ uint64_t GetPID() { return GetCurrentProcessId(); } #endif // _NB_TARGET_WINDOWS - #ifdef _NB_TARGET_LINUX #endif // _NB_TARGET_LINUX +#ifdef _NB_TARGET_WINDOWS +uint64_t GetTID() { + return GetCurrentThreadId(); +} +#endif // _NB_TARGET_WINDOWS +#ifdef _NB_TARGET_LINUX +#endif // _NB_TARGET_LINUX + } // namespace nb \ No newline at end of file diff --git a/engine/NBCore/src/StringUtils.cpp b/engine/NBCore/src/StringUtils.cpp new file mode 100644 index 0000000..1946c3c --- /dev/null +++ b/engine/NBCore/src/StringUtils.cpp @@ -0,0 +1,29 @@ +#include "StringUtils.hpp" + +/* std::string nb::wstr_to_str(std::wstring in) { + std::size_t wstrlen = in.length(); + char* c_str= new char[wstrlen]; + std::wcstombs(c_str, in.c_str(), wstrlen); + std::string ret(c_str, wstrlen); + delete[] c_str; + return ret; +} */ + +std::string nb::indent_strblock( + std::string block, + std::string prepend, + std::string topIndent +) { + return topIndent + nb::find_and_replace( + block, + nb::NEWLINE, + nb::NEWLINE + prepend + ); +} + +std::string nb::indent_strblock( + std::string block, + std::string prepend +) { + return nb::indent_strblock(block, prepend, prepend); +} diff --git a/engine/NBCore/src/Utils.cpp b/engine/NBCore/src/Utils.cpp new file mode 100644 index 0000000..39fc680 --- /dev/null +++ b/engine/NBCore/src/Utils.cpp @@ -0,0 +1,27 @@ +#include "Utils.hpp" + +namespace nb { + +using ObjectManagerCodes = ObjectManagerError::Codes; +const std::string ObjectManagerError::type = "nb::ObjectManagerError"; +const ErrorCodeMap ObjectManagerError::ErrorMessages({ + {ObjectManagerCodes::UNDEFINED, "Error"}, + { + ObjectManagerCodes::NO_MANAGER, + "Attempting to create object lock without lock manager" + }, + { + ObjectManagerCodes::MANAGER_MISMATCH, + "Attempting to delete object lock from mismatched lock manager" + }, + { + ObjectManagerCodes::LOCK_OVERFLOW, + "Too many object locks allocated" + }, + { + ObjectManagerCodes::BAD_THREAD, + "Attempting operation from a bad thread" + } +}); + +}; // namespace nb diff --git a/engine/NBCore/tests/CMakeLists.txt b/engine/NBCore/tests/CMakeLists.txt index d3ad479..34476ff 100644 --- a/engine/NBCore/tests/CMakeLists.txt +++ b/engine/NBCore/tests/CMakeLists.txt @@ -4,12 +4,21 @@ if (NB_BUILD_TESTS) enable_testing() include(GoogleTest) add_executable(TestCore - testErrors.cpp - testProcesses.cpp + ./testErrors.cpp + #./testProcesses.cpp + ./testUtils.cpp ) target_link_libraries(TestCore NBCore GTest::gtest_main ) + + add_executable(LoggerTest + ./testLogger.cpp + ) + target_link_libraries(LoggerTest + NBCore + ) + gtest_discover_tests(TestCore) endif() \ No newline at end of file diff --git a/engine/NBCore/tests/testErrors.cpp b/engine/NBCore/tests/testErrors.cpp index 7d79ee9..0c7be49 100644 --- a/engine/NBCore/tests/testErrors.cpp +++ b/engine/NBCore/tests/testErrors.cpp @@ -1,38 +1,39 @@ #define CODE_ERROR_LOCATIONS -#include "Errors.hpp" #include -#include -#include -#include "Logger.hpp" + +#include "Errors.hpp" +#include using namespace nb; -class TestError : public ErrorBase { +class TestError : public Error { + using Base = Error; public: - using ErrorBase::ErrorBase; + using Base::Base; + using Base::what; + using Base::code; + using Base::msg; + using Base::trace; - enum ErrorCodes : unsigned int { + enum Codes : unsigned int { A, B, C, D }; + static const std::string type; - static const ErrorCodeMap ErrorMessages; + static const nb::ErrorCodeMap ErrorMessages; }; const std::string TestError::type="TestError"; const ErrorCodeMap TestError::ErrorMessages{ - {TestError::ErrorCodes::A, "Hey!"}, - {TestError::ErrorCodes::B, "How"}, - {TestError::ErrorCodes::C, "You"}, - {TestError::ErrorCodes::D, "Doin"} + {TestError::Codes::A, "Hey!"}, + {TestError::Codes::B, "How"}, + {TestError::Codes::C, "You"}, + {TestError::Codes::D, "Doin"} }; TEST(ErrorTest, Test) { - EXPECT_EQ(1, 1); - - std::stringstream sstream; - - ASSERT_TRUE(nb::logger.isRunning()); - nb::logger.log("Hey!"); + auto err = TestError(0, TestError(1, TestError(2, TestError(3, TestError(2))))); + ASSERT_STREQ(err.what().c_str(), "Hey!\n Trace: How\n Trace: You\n Trace: Doin\n Trace: You"); } \ No newline at end of file diff --git a/engine/NBCore/tests/testLogger.cpp b/engine/NBCore/tests/testLogger.cpp new file mode 100644 index 0000000..27da8da --- /dev/null +++ b/engine/NBCore/tests/testLogger.cpp @@ -0,0 +1,46 @@ +#include + +#include "Errors.hpp" +#include "Logger.hpp" + +class TestError : public nb::Error { + using Base = Error; +public: + using Base::Base; + using Base::what; + using Base::code; + using Base::msg; + using Base::trace; + + enum Codes : unsigned int { + A, B, C, D + }; + + static const std::string type; + static const nb::ErrorCodeMap ErrorMessages; +}; + +const std::string TestError::type="TestError"; +const nb::ErrorCodeMap TestError::ErrorMessages{ + {TestError::Codes::A, "Hey!"}, + {TestError::Codes::B, "How"}, + {TestError::Codes::C, "You"}, + {TestError::Codes::D, "Doin"} +}; + +int main() { + nb::logger.log("Whoop!"); + try { + THROW(TestError(0, TestError(1, TestError(2, TestError(3, TestError(2)))))); + } catch (TestError e){ + nb::logger.log("nb::Error was thrown!"); + } + + try { + THROW(std::exception()); + } catch (std::exception e){ + nb::logger.log("std::exception was thrown!"); + } + ERROR("hello there!"); + return 0; +} \ No newline at end of file diff --git a/engine/NBCore/tests/testProcesses.cpp b/engine/NBCore/tests/testProcesses.cpp index 3e220b8..cee1e90 100644 --- a/engine/NBCore/tests/testProcesses.cpp +++ b/engine/NBCore/tests/testProcesses.cpp @@ -1,8 +1,6 @@ #define CODE_ERROR_LOCATIONS #include - -#include #include "Processes.hpp" #include diff --git a/engine/NBCore/tests/testUtils.cpp b/engine/NBCore/tests/testUtils.cpp new file mode 100644 index 0000000..a726369 --- /dev/null +++ b/engine/NBCore/tests/testUtils.cpp @@ -0,0 +1,148 @@ +#define CODE_ERROR_LOCATIONS + +#include + +#include +#include "TypeTraits.hpp" +#include "StringUtils.hpp" + +namespace nb { + +TEST(UtilsTest, TestFindAndReplace) { + ASSERT_STREQ( + find_and_replace("Jeff", "e", "efe").c_str(), + "Jefeff" + ); + + std::string tmp = find_and_replace("Naif", "a", "afa"); + ASSERT_STREQ( + find_and_replace(tmp, "i", "ifi").c_str(), + "Nafaifif" + ); + + tmp = find_and_replace("aeiou", "a", "afa"); + tmp = find_and_replace(tmp, "e", "efe"); + tmp = find_and_replace(tmp, "i", "ifi"); + tmp = find_and_replace(tmp, "o", "ofo"); + tmp = find_and_replace(tmp, "u", "ufu"); + ASSERT_STREQ( + tmp.c_str(), + "afaefeifiofoufu" + ); + + tmp = find_and_replace(tmp, "afa", "a"); + tmp = find_and_replace(tmp, "efe", "e"); + tmp = find_and_replace(tmp, "ifi", "i"); + tmp = find_and_replace(tmp, "ofo", "o"); + tmp = find_and_replace(tmp, "ufu", "u"); + ASSERT_STREQ( + tmp.c_str(), + "aeiou" + ); +} + +TEST(UtilsTest, TestFindAndReplaceWstr) { + ASSERT_STREQ( + find_and_replace(L"Jeff", L"e", L"efe").c_str(), + L"Jefeff" + ); + + std::wstring tmp = find_and_replace(L"Naif", L"a", L"afa"); + ASSERT_STREQ( + find_and_replace(tmp, L"i", L"ifi").c_str(), + L"Nafaifif" + ); + + tmp = find_and_replace(L"aeiou", L"a", L"afa"); + tmp = find_and_replace(tmp, L"e", L"efe"); + tmp = find_and_replace(tmp, L"i", L"ifi"); + tmp = find_and_replace(tmp, L"o", L"ofo"); + tmp = find_and_replace(tmp, L"u", L"ufu"); + ASSERT_STREQ( + tmp.c_str(), + L"afaefeifiofoufu" + ); + + tmp = find_and_replace(tmp, L"afa", L"a"); + tmp = find_and_replace(tmp, L"efe", L"e"); + tmp = find_and_replace(tmp, L"ifi", L"i"); + tmp = find_and_replace(tmp, L"ofo", L"o"); + tmp = find_and_replace(tmp, L"ufu", L"u"); + ASSERT_STREQ( + tmp.c_str(), + L"aeiou" + ); +} + +/* TEST(UtilsTest, TestWstrToStr) { + ASSERT_STREQ( + nb::wstr_to_str(L"Hi!").c_str(), + "Hi!" + ); + ASSERT_STREQ( + nb::wstr_to_str(L"Naif").c_str(), + "Naif" + ); + ASSERT_STREQ( + nb::wstr_to_str(L"\r\r\r\n").c_str(), + "\r\r\r\n" + ); + ASSERT_STREQ( + nb::wstr_to_str(L"Naif\ttalks\r\ra\t\nlot").c_str(), + "Naif\ttalks\r\ra\t\nlot" + ); +} */ + +/* TEST(UtilsTest, TestStrToWstr) { + ASSERT_STREQ( + nb::str_to_wstr("Hi!").c_str(), + L"Hi!" + ); + ASSERT_STREQ( + nb::str_to_wstr("Naif").c_str(), + L"Naif" + ); + ASSERT_STREQ( + nb::str_to_wstr("\r\r\r\n").c_str(), + L"\r\r\r\n" + ); + ASSERT_STREQ( + nb::str_to_wstr("Naif\ttalks\r\ra\t\nlot").c_str(), + L"Naif\ttalks\r\ra\t\nlot" + ); +} */ + +struct A { bool x(); }; +struct B { bool y(); }; +struct C { bool x(); bool y; }; +struct D : C {}; + +template +using has_x = decltype(T::x); + +template +using has_y = decltype(T::y); + +TEST(UtilsTest, TestIsDetected) { + auto ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_FALSE(ret); + + ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_FALSE(ret); + + ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_TRUE(ret); + + ret = is_detected::value; + ASSERT_TRUE(ret); + ret = is_detected::value; + ASSERT_TRUE(ret); +} + +} // namespace nb \ No newline at end of file