CMake/project reorg
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
add_subdirectory(./NBCoreUtils)
|
||||
add_subdirectory(./NBEvents)
|
||||
add_subdirectory(./NBWindow)
|
||||
@@ -0,0 +1,7 @@
|
||||
include_directories(./.)
|
||||
|
||||
add_library(NBCore
|
||||
./src/Errors.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(./tests)
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#ifndef _NB_DATASINK
|
||||
#define _NB_DATASINK
|
||||
|
||||
namespace nb {
|
||||
|
||||
template <typename T>
|
||||
class SinkBase {
|
||||
using InputType = T;
|
||||
public:
|
||||
virtual bool in(const T&) = 0;
|
||||
|
||||
protected:
|
||||
SinkBase();
|
||||
|
||||
};
|
||||
|
||||
/* template <typename T>
|
||||
class Sink : public SinkBase {
|
||||
public:
|
||||
Sink& operator=(const Sink&) = 0;
|
||||
|
||||
template<typename U>
|
||||
virtual bool in(const U&) = 0;
|
||||
|
||||
protected:
|
||||
Sink();
|
||||
|
||||
}; */
|
||||
|
||||
template <typename T>
|
||||
class SourceBase {
|
||||
using OutputType = T;
|
||||
public:
|
||||
SourceBase& operator=(const SourceBase&) = 0;
|
||||
|
||||
virtual bool out(T*) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
SourceBase();
|
||||
SinkBase<T>* _sink;
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Pipe : public SinkBase<T>, public SourceBase<T>{
|
||||
using Input = SourceBase<T>;
|
||||
using Output = SinkBase<T>;
|
||||
public:
|
||||
Pipe(Sink<T>& sink) : Input::_sink{&sink} {}
|
||||
|
||||
bool in(const T&) = 0;
|
||||
|
||||
protected:
|
||||
using Input::_sink;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* template <typename T>
|
||||
class Source : SourceBase {
|
||||
public:
|
||||
Source& operator=(const Source&) = 0;
|
||||
|
||||
template<typename U>
|
||||
virtual bool out(const U&) = 0;
|
||||
|
||||
protected:
|
||||
Source();
|
||||
|
||||
}; */
|
||||
|
||||
} // namespace nb
|
||||
#endif // _NB_DATASINK
|
||||
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
#ifndef _NB_ERROR
|
||||
#define _NB_ERROR
|
||||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#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
|
||||
|
||||
namespace nb {
|
||||
|
||||
typedef std::unordered_map<unsigned int, const char*> ErrorCodeMap;
|
||||
|
||||
template<class ErrorType>
|
||||
class ErrorBase : public std::exception {
|
||||
public:
|
||||
ErrorBase(
|
||||
const unsigned int code,
|
||||
unsigned int line=0,
|
||||
std::string filename=""
|
||||
) noexcept : ErrorBase(
|
||||
code,
|
||||
ErrorBase<ErrorType>::lookup(code),
|
||||
line,
|
||||
filename
|
||||
) {}
|
||||
ErrorBase(
|
||||
const unsigned int code,
|
||||
const std::exception& trace,
|
||||
unsigned int line=0,
|
||||
std::string filename=""
|
||||
) noexcept : ErrorBase(
|
||||
code,
|
||||
ErrorBase<ErrorType>::lookup(code),
|
||||
trace,
|
||||
line,
|
||||
filename
|
||||
) {}
|
||||
|
||||
static std::string lookup(unsigned int);
|
||||
unsigned int code() const noexcept {
|
||||
return static_cast<const ErrorType*>(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<const std::unordered_map<unsigned int, const char*>, decltype(ErrorType::ErrorMessages)>::value,
|
||||
"const std::unordered_map<unsigned int, const char*> ErrorMessages must be "
|
||||
"a class member."
|
||||
);
|
||||
static_assert(std::is_enum_v<typename ErrorType::ErrorCodes>, "enum ErrorCodes must be a class member.");
|
||||
static_assert(std::is_same<std::underlying_type_t<typename ErrorType::ErrorCodes>, unsigned int>::value,
|
||||
"enum ErrorCodes must be of underlying type unsigned int."
|
||||
);
|
||||
static_assert(std::is_same<const std::string, decltype(ErrorType::type)>::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<Error> {
|
||||
public:
|
||||
enum ErrorCodes : unsigned int {
|
||||
GENERAL, UNDEFINED, BADERRORCODE
|
||||
};
|
||||
|
||||
using ErrorBase<Error>::ErrorBase;
|
||||
|
||||
friend ErrorBase<Error>;
|
||||
|
||||
protected:
|
||||
static const std::string type;
|
||||
static const ErrorCodeMap ErrorMessages;
|
||||
|
||||
};
|
||||
|
||||
template<class ErrorType>
|
||||
std::string ErrorBase<ErrorType>::lookup(unsigned int code) {
|
||||
for (auto kv : ErrorType::ErrorMessages) {
|
||||
if (kv.first==code) {
|
||||
return std::string(kv.second);
|
||||
}
|
||||
}
|
||||
|
||||
throw Error(Error::ErrorCodes::BADERRORCODE);
|
||||
}
|
||||
|
||||
} // namespace nb
|
||||
|
||||
|
||||
#endif // _NB_ERROR
|
||||
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
#ifndef _NB_LOGGER
|
||||
#define _NB_LOGGER
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <ostream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "ThreadSafeQueue.hpp"
|
||||
|
||||
namespace nb {
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&);
|
||||
typedef std::unordered_map<uint8_t, LogProcessFunction> LogProcessFunctionMap;
|
||||
|
||||
template<typename StreamType>
|
||||
class Logger {
|
||||
public:
|
||||
Logger(StreamType& stream) : _ostream(&stream) {}
|
||||
|
||||
bool isRunning() const;
|
||||
unsigned int count() const;
|
||||
|
||||
bool run();
|
||||
bool stop();
|
||||
LogEvent pop();
|
||||
void flush() {
|
||||
while(_queue.size()) {
|
||||
pop();
|
||||
}
|
||||
}
|
||||
void deleteAll() {
|
||||
_queue.empty();
|
||||
}
|
||||
void msg(const std::string& _msg, uint8_t lvl=0) {
|
||||
_queue.push(LogEvent{std::chrono::system_clock::now(), lvl, _msg});
|
||||
}
|
||||
void warn(const std::string& _msg, uint8_t lvl=1) {
|
||||
msg(_msg, lvl);
|
||||
}
|
||||
void warn(const std::exception& _err, uint8_t lvl=1) {
|
||||
msg(_err.what(), lvl);
|
||||
}
|
||||
void error(const std::string& _msg, uint8_t lvl=0xFF) {
|
||||
msg(_msg, lvl);
|
||||
}
|
||||
void error(const std::exception& _err, uint8_t lvl=0xFF) {
|
||||
msg(_err.what(), lvl);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void process(const LogEvent& event) {
|
||||
uint8_t lvl = event.lvl;
|
||||
try {
|
||||
*_ostream << _function_map.at(lvl)(event.time, event.msg).c_str();
|
||||
} catch (std::out_of_range e) {
|
||||
*_ostream << _function_map.at(0)(event.time, event.msg).c_str();
|
||||
//*_ostream << event.msg;
|
||||
}
|
||||
*_ostream << "\n";
|
||||
}
|
||||
|
||||
static const LogProcessFunctionMap _function_map;
|
||||
|
||||
ThreadsafeQueue<LogEvent> _queue;
|
||||
StreamType* const _ostream;
|
||||
std::atomic<bool> _running;
|
||||
std::shared_ptr<std::thread> _runningThread = nullptr;
|
||||
};
|
||||
|
||||
template <typename ST>
|
||||
const LogProcessFunctionMap Logger<ST>::_function_map = {
|
||||
{0, [](const LoggerTimePoint& tp, const std::string& msg){
|
||||
return msg;
|
||||
}}
|
||||
};
|
||||
|
||||
template <typename ST>
|
||||
bool Logger<ST>::isRunning() const {
|
||||
return _running && bool(_runningThread);
|
||||
}
|
||||
|
||||
template <typename ST>
|
||||
unsigned int Logger<ST>::count() const {
|
||||
return _queue.size();
|
||||
}
|
||||
|
||||
template <typename ST>
|
||||
bool Logger<ST>::run() {
|
||||
if (!isRunning()) {
|
||||
_runningThread = std::make_shared<std::thread>([&]{
|
||||
while(this->isRunning()) {
|
||||
this->flush();
|
||||
}
|
||||
});
|
||||
}
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
template <typename ST>
|
||||
bool Logger<ST>::stop() {
|
||||
if (isRunning()) {
|
||||
_running = false;
|
||||
_runningThread->join();
|
||||
_runningThread = nullptr;
|
||||
flush();
|
||||
}
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
template <typename ST>
|
||||
LogEvent Logger<ST>::pop() {
|
||||
std::shared_ptr<LogEvent> event;
|
||||
_queue.pop(event);
|
||||
process(*event);
|
||||
return *event;
|
||||
}
|
||||
|
||||
} // namespace nb
|
||||
#endif // _NB_LOGGER
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#ifndef _NB_SMARTSTREAM
|
||||
#define _NB_SMARTSTREAM
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nb {
|
||||
|
||||
using RGB = std::array<unsigned char, 3>;
|
||||
|
||||
std::string toANSI(const RGB& color) {
|
||||
return "\x1b[38;2;"+std::to_string(color[0])+";"+std::to_string(color[1])+
|
||||
";"+std::to_string(color[2])+"m";
|
||||
}
|
||||
|
||||
std::string toANSI(const unsigned int& color) {
|
||||
return "\x1b[38;5;"+std::to_string(color)+"m";
|
||||
}
|
||||
|
||||
template <typename U, typename Derived=void>
|
||||
struct SmartText {
|
||||
SmartText(const U& val) : msg(val) {}
|
||||
|
||||
const U msg;
|
||||
|
||||
template<typename C, typename T>
|
||||
void process(std::basic_ostream<C,T>& stream) const;
|
||||
};
|
||||
|
||||
template <typename Derived>
|
||||
struct SmartText<std::string, Derived> {
|
||||
template <std::size_t N>
|
||||
SmartText(char const(&val) [N]) : msg(val) {}
|
||||
SmartText(const std::string& val) : msg(val) {}
|
||||
SmartText(const char* val) : msg(val) {}
|
||||
|
||||
const std::string msg;
|
||||
|
||||
template<typename C, typename T>
|
||||
void process(std::basic_ostream<C,T>& stream) const;
|
||||
};
|
||||
|
||||
template <typename U, typename Derived>
|
||||
template <typename C, typename T>
|
||||
void SmartText<U,Derived>::process(std::basic_ostream<C,T>& stream) const {
|
||||
if (std::is_void<Derived>::value) {
|
||||
stream << msg;
|
||||
} else {
|
||||
static_cast<Derived*>(this)->process(stream);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename C, typename T, typename U, typename D>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& stream, const SmartText<U,D>& smt) {
|
||||
if (std::is_void<D>::value) {
|
||||
stream << smt.msg;
|
||||
} else {
|
||||
static_cast<const D*>(&smt)->process(stream);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
struct TerminalColor : public SmartText<U, TerminalColor<U>> {
|
||||
typedef SmartText<U, TerminalColor<U>> Base;
|
||||
using Base::msg;
|
||||
|
||||
explicit TerminalColor(unsigned int color, const U& val)
|
||||
: Base(val), colorSequence(toANSI(color)) { }
|
||||
explicit TerminalColor(const RGB& color, const U& val)
|
||||
: Base(val), colorSequence(toANSI(color)) { }
|
||||
|
||||
const std::string colorSequence;
|
||||
|
||||
template <typename C, typename T>
|
||||
void process(std::basic_ostream<C, T>& stream) const {
|
||||
if (&stream == &std::cout) {
|
||||
stream << colorSequence << msg << "\x1b[0m";
|
||||
} else {
|
||||
stream << msg;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // _NB_SMARTSTREAM
|
||||
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
#ifndef _NB_UTIL_THREADSAFE_QUEUE
|
||||
#define _NB_UTIL_THREADSAFE_QUEUE
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <type_traits>
|
||||
|
||||
namespace nb {
|
||||
|
||||
template<typename T, bool isPoly>
|
||||
class ThreadsafeQueue_impl;
|
||||
|
||||
// Non-polymorphic
|
||||
template<typename T>
|
||||
class ThreadsafeQueue_impl<T, false> {
|
||||
public:
|
||||
bool empty() const {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _queue.empty();
|
||||
}
|
||||
unsigned int size() const {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _queue.size();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void push(U&& val) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_queue.push(std::forward<U>(val));
|
||||
_cond.notify_one();
|
||||
}
|
||||
bool pop(std::shared_ptr<T>& dest) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (_queue.empty()) { return false; }
|
||||
dest = std::make_shared<T>(std::move(_queue.front()));
|
||||
_queue.pop();
|
||||
return true;
|
||||
}
|
||||
std::shared_ptr<T> popBlock() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cond.wait(lock, [&]{ return !_queue.empty(); });
|
||||
auto ret = std::make_shared<T>(std::move(_queue.front()));
|
||||
_queue.pop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
ThreadsafeQueue_impl() {}
|
||||
|
||||
std::queue<T> _queue;
|
||||
std::condition_variable _cond;
|
||||
mutable std::mutex _mutex;
|
||||
|
||||
};
|
||||
|
||||
// Polymorphic
|
||||
template<typename T>
|
||||
class ThreadsafeQueue_impl<T, true> {
|
||||
public:
|
||||
bool empty() const {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _queue.empty();
|
||||
}
|
||||
unsigned int size() const {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _queue.size();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void push(U&& val) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
std::shared_ptr<T> newval = std::make_shared<T>(std::forward<U>(val));
|
||||
_queue.push(newval);
|
||||
_cond.notify_one();
|
||||
}
|
||||
bool pop(std::shared_ptr<T>& dest) {
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (_queue.empty()) { return false; }
|
||||
dest = std::move(_queue.front());
|
||||
_queue.pop();
|
||||
return true;
|
||||
}
|
||||
std::shared_ptr<T> popBlock() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cond.wait(lock, [&]{ return !_queue.empty(); });
|
||||
auto ret = std::move(_queue.front());
|
||||
_queue.pop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected:
|
||||
ThreadsafeQueue_impl() {}
|
||||
|
||||
std::queue<std::shared_ptr<T>> _queue;
|
||||
std::condition_variable _cond;
|
||||
mutable std::mutex _mutex;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ThreadsafeQueue : public ThreadsafeQueue_impl<T, std::is_polymorphic<T>::value> {
|
||||
public:
|
||||
using Implementation = ThreadsafeQueue_impl<T, std::is_polymorphic<T>::value>;
|
||||
|
||||
ThreadsafeQueue() = default;
|
||||
~ThreadsafeQueue() = default;
|
||||
|
||||
ThreadsafeQueue(ThreadsafeQueue& rhs) = delete;
|
||||
ThreadsafeQueue& operator=(ThreadsafeQueue& rhs) = delete;
|
||||
ThreadsafeQueue(ThreadsafeQueue&& rhs) = delete;
|
||||
ThreadsafeQueue& operator=(ThreadsafeQueue&& rhs) = delete;
|
||||
|
||||
operator bool() const { return !Implementation::empty(); }
|
||||
|
||||
};
|
||||
|
||||
} // namespace nb
|
||||
|
||||
|
||||
#endif // _NB_UTIL_THREADSAFE_QUEUE
|
||||
@@ -0,0 +1,12 @@
|
||||
#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."}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.26.0)
|
||||
project(gtest_Graphics VERSION 0.1.0 LANGUAGES C CXX)
|
||||
|
||||
add_subdirectory(./ErrorTest)
|
||||
add_subdirectory(./SmartStreamTest)
|
||||
@@ -0,0 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.26.0)
|
||||
project(gtest_Graphics VERSION 0.1.0 LANGUAGES C CXX)
|
||||
|
||||
add_executable(ErrorTest main.cpp)
|
||||
target_link_libraries(ErrorTest NBCore)
|
||||
@@ -0,0 +1,54 @@
|
||||
#define CODE_ERROR_LOCATIONS
|
||||
|
||||
#include "Errors.hpp"
|
||||
#include <iostream>
|
||||
#include "Logger.hpp"
|
||||
|
||||
using namespace nb;
|
||||
|
||||
class TestError : public ErrorBase<TestError> {
|
||||
public:
|
||||
using ErrorBase<TestError>::ErrorBase;
|
||||
|
||||
enum ErrorCodes : unsigned int {
|
||||
A, B, C, D
|
||||
};
|
||||
static const std::string type;
|
||||
static const 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"}
|
||||
};
|
||||
|
||||
int main() {
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
THROW(TestError, TestError::ErrorCodes::D);
|
||||
} catch (const std::exception& e){
|
||||
THROW(TestError, TestError::ErrorCodes::C, e);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
THROW(TestError, TestError::ErrorCodes::B, e);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
THROW(TestError, TestError::ErrorCodes::A, e);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
// THROW(Error, Error::ErrorCodes::UNDEFINED, e);
|
||||
Logger log(std::cout);
|
||||
std::cout << "Logger is starting: " << log.run() << std::endl;
|
||||
std::cout << "Logger is running: " << log.isRunning() << std::endl;
|
||||
log.error(e);
|
||||
log.stop();
|
||||
std::cout << "Logger is running: " << log.isRunning() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.26.0)
|
||||
project(gtest_Graphics VERSION 0.1.0 LANGUAGES C CXX)
|
||||
|
||||
add_executable(SmartStreamTest main.cpp)
|
||||
target_link_libraries(SmartStreamTest NBCore)
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
#include "SmartStreams.hpp"
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
auto test = nb::TerminalColor(9, 2030);
|
||||
|
||||
std::cout << test;
|
||||
|
||||
std::ofstream file;
|
||||
file.open("test.txt");
|
||||
if (file.is_open()) {
|
||||
file << test;
|
||||
}
|
||||
file.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
include_directories(./.)
|
||||
|
||||
add_library(NBEvents
|
||||
./src/Events.cpp
|
||||
)
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
#ifndef _NB_EVENTS
|
||||
#define _NB_EVENTS
|
||||
|
||||
#include <atomic>
|
||||
#include <stdexcept>
|
||||
#include <queue>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
using state_register = std::atomic<uint64_t>;
|
||||
|
||||
namespace NB {
|
||||
|
||||
extern std::invalid_argument null_mask_error;
|
||||
|
||||
void NULL_FUNC();
|
||||
|
||||
class Events {
|
||||
friend class NBEventListener;
|
||||
public:
|
||||
Events(const uint64_t, void (*initFunc)() = NULL_FUNC, const char* initName="");
|
||||
Events(const uint64_t, const char* initName="");
|
||||
Events(const Events&);
|
||||
Events& operator=(const Events&);
|
||||
~Events();
|
||||
|
||||
const std::string getName() const;
|
||||
const uint64_t getMask() const;
|
||||
void setMask(const uint64_t);
|
||||
void setName(const char*);
|
||||
void setFunc(void (*newFunc)());
|
||||
virtual const uint64_t check(const uint64_t) const = 0;
|
||||
virtual Events* clone() const = 0;
|
||||
|
||||
protected:
|
||||
uint64_t mask = 0x0;
|
||||
void (*func)() = nullptr;
|
||||
std::string name = "";
|
||||
|
||||
};
|
||||
|
||||
class NBEventBasic : public Events {
|
||||
friend class NBEventListener;
|
||||
public:
|
||||
using Events::Events;
|
||||
|
||||
NBEventBasic* clone() const override;
|
||||
const uint64_t check(const uint64_t) const override;
|
||||
|
||||
};
|
||||
|
||||
class NBEventState : public Events {
|
||||
friend class NBEventListener;
|
||||
public:
|
||||
using Events::Events;
|
||||
|
||||
NBEventState* clone() const override;
|
||||
const uint64_t check(const uint64_t) const override;
|
||||
};
|
||||
|
||||
enum NBStateChangeType : uint8_t {
|
||||
STATE_RAISE, STATE_DROP, STATE_SET
|
||||
};
|
||||
|
||||
struct NBStateChange {
|
||||
uint8_t type;
|
||||
uint64_t mask;
|
||||
};
|
||||
|
||||
class NBEventListener {
|
||||
public:
|
||||
NBEventListener(Events**, uint16_t, const uint64_t initState=(uint64_t)0x0, state_register* initStatePtr=nullptr);
|
||||
template<size_t SIZE>
|
||||
NBEventListener(std::array<Events*, SIZE> eventArray, const uint64_t initState=(uint64_t)0x0, state_register* initStatePtr=nullptr)
|
||||
: NBEventListener(eventArray.data(), eventArray.size(), initState, initStatePtr) {}
|
||||
|
||||
state_register* getStatePtr() const;
|
||||
const uint64_t getState() const;
|
||||
void raiseFlags(const uint64_t);
|
||||
void raiseFlags(const Events&);
|
||||
void dropFlags(const uint64_t);
|
||||
void dropFlags(const Events&);
|
||||
const bool snoop(const uint64_t) const;
|
||||
const bool snoop(const Events&) const;
|
||||
void listen();
|
||||
void listen(const Events&);
|
||||
|
||||
protected:
|
||||
void _setState(const uint64_t);
|
||||
|
||||
Events** eventList;
|
||||
uint16_t numEvents;
|
||||
state_register* state;
|
||||
std::mutex bufferLock;
|
||||
std::queue<NBStateChange> stateBuffer;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "Events.h"
|
||||
|
||||
namespace NB {
|
||||
|
||||
void NULL_FUNC() {}
|
||||
|
||||
std::invalid_argument null_mask_error("NULL MASK NOT ALLOWED");
|
||||
|
||||
Events::Events(const uint64_t initMask, void (*initFunc)(), const char* initName):
|
||||
mask(initMask), func(initFunc), name(initName) {
|
||||
if (mask == 0x0) {
|
||||
throw null_mask_error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Events::Events(const uint64_t initMask, const char* initName):
|
||||
Events(initMask, NULL_FUNC, initName) {}
|
||||
|
||||
Events::Events(const Events& cpy)
|
||||
:Events(cpy.mask, cpy.func, cpy.name.c_str()) {}
|
||||
|
||||
Events& Events::operator=(const Events& cpy) {
|
||||
func = cpy.func;
|
||||
name = cpy.name;
|
||||
mask = cpy.mask;
|
||||
if (mask==0x0) {
|
||||
throw null_mask_error;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Events::~Events() { func = nullptr; }
|
||||
|
||||
const std::string Events::getName() const { return name; }
|
||||
|
||||
const uint64_t Events::getMask() const { return mask; }
|
||||
|
||||
void Events::setMask(const uint64_t newMask) {
|
||||
mask = newMask;
|
||||
}
|
||||
|
||||
void Events::setName(const char* newName) {
|
||||
name = newName;
|
||||
}
|
||||
|
||||
void Events::setFunc(void (*newFunc)()) {
|
||||
func = newFunc;
|
||||
}
|
||||
|
||||
NBEventBasic* NBEventBasic::clone() const { return new NBEventBasic(*this); }
|
||||
|
||||
NBEventState* NBEventState::clone() const { return new NBEventState(*this); }
|
||||
|
||||
const uint64_t NBEventBasic::check(const uint64_t refState) const {
|
||||
if ((mask!=0) && ((refState&mask)==mask)) {
|
||||
func();
|
||||
return refState&(~mask);
|
||||
}
|
||||
return refState;
|
||||
}
|
||||
|
||||
const uint64_t NBEventState::check(const uint64_t refState) const {
|
||||
if ((mask!=0) && ((refState&mask)==mask)) {
|
||||
func();
|
||||
}
|
||||
return refState;
|
||||
}
|
||||
|
||||
NBEventListener::NBEventListener(Events** initEventList, uint16_t initNum, const uint64_t initState, state_register* initStatePtr) {
|
||||
if (initStatePtr == nullptr) {
|
||||
state = new state_register;
|
||||
} else {
|
||||
state = initStatePtr;
|
||||
}
|
||||
|
||||
state->store(initState);
|
||||
numEvents = initNum;
|
||||
eventList = new Events*[numEvents];
|
||||
for (uint16_t i = 0; i < initNum; ++i) {
|
||||
eventList[i] = initEventList[i]->clone();
|
||||
}
|
||||
}
|
||||
|
||||
state_register* NBEventListener::getStatePtr() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
const uint64_t NBEventListener::getState() const {
|
||||
return state->load();
|
||||
}
|
||||
|
||||
void NBEventListener::raiseFlags(const uint64_t newState) {
|
||||
bufferLock.lock();
|
||||
stateBuffer.push(NBStateChange{STATE_RAISE, newState});
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
void NBEventListener::raiseFlags(const Events& newEvent) {
|
||||
raiseFlags(newEvent.mask);
|
||||
}
|
||||
|
||||
void NBEventListener::dropFlags(const uint64_t dropState) {
|
||||
bufferLock.lock();
|
||||
stateBuffer.push(NBStateChange{STATE_DROP, dropState});
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
void NBEventListener::dropFlags(const Events& dropEvent) {
|
||||
dropFlags(dropEvent.mask);
|
||||
}
|
||||
|
||||
const bool NBEventListener::snoop(const uint64_t refState) const {
|
||||
return ((getState()&refState)==refState);
|
||||
}
|
||||
|
||||
const bool NBEventListener::snoop(const Events& refEvent) const {
|
||||
return snoop(refEvent.mask);
|
||||
}
|
||||
|
||||
void NBEventListener::_setState(const uint64_t newState) {
|
||||
bufferLock.lock();
|
||||
std::queue<NBStateChange>().swap(stateBuffer);
|
||||
bufferLock.unlock();
|
||||
state->store(newState);
|
||||
}
|
||||
|
||||
void NBEventListener::listen() {
|
||||
uint64_t oldState = getState();
|
||||
for (uint16_t i = 0; i < numEvents; ++i) {
|
||||
oldState = eventList[i]->check(oldState);
|
||||
}
|
||||
NBStateChange curr;
|
||||
bufferLock.lock();
|
||||
for (; !stateBuffer.empty(); stateBuffer.pop()) {
|
||||
curr = stateBuffer.front();
|
||||
switch (curr.type) {
|
||||
case STATE_RAISE:
|
||||
oldState |= curr.mask;
|
||||
break;
|
||||
case STATE_DROP:
|
||||
oldState &= ~curr.mask;
|
||||
break;
|
||||
case STATE_SET:
|
||||
oldState = curr.mask;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
bufferLock.unlock();
|
||||
_setState(oldState);
|
||||
}
|
||||
|
||||
void NBEventListener::listen(const Events& refEvent) {
|
||||
uint64_t oldState = getState();
|
||||
oldState = refEvent.check(oldState);
|
||||
_setState(oldState);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
include_directories(./.)
|
||||
|
||||
|
||||
add_library(NBWindow
|
||||
./src/Window.cpp
|
||||
./../../../glad/src/glad.c
|
||||
)
|
||||
target_link_libraries(NBWindow glfw)
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#ifndef _NB_OPENGL_LOADER
|
||||
#define _NB_OPENGL_LOADER
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace NB {
|
||||
|
||||
static unsigned int __NB_GL_DEBUG_ERROR_CODE__;
|
||||
|
||||
static std::string formatDebugString(const std::string& msg, const std::string& file="", int line=-1) {
|
||||
std::string ret = "";
|
||||
if (file != "") {
|
||||
ret += "In file " + file;
|
||||
if (line >= 0) {
|
||||
ret += " at line " + std::to_string(line);
|
||||
}
|
||||
ret += ":\n\t";
|
||||
}
|
||||
return ret + msg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define NB_GL_DEBUG_THROW(cmd, when) while(NB::__NB_GL_DEBUG_ERROR_CODE__=glGetError()){\
|
||||
std::cout << "[GL ERROR]: " << NB::__NB_GL_DEBUG_ERROR_CODE__;\
|
||||
std::cout << " " << when << " " << cmd << "\n";\
|
||||
}
|
||||
#define NB_GL_DEBUG(cmd) NB_GL_DEBUG_THROW(#cmd, "before"); cmd; NB_GL_DEBUG_THROW(#cmd, "after");
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#ifndef _NB_WINDOW
|
||||
#define _NB_WINDOW
|
||||
|
||||
#include "GLLoad.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace NB {
|
||||
|
||||
class GLError : public std::runtime_error {
|
||||
public:
|
||||
GLError(const std::string&);
|
||||
};
|
||||
|
||||
class WindowError : public std::runtime_error {
|
||||
public:
|
||||
WindowError(const std::string&);
|
||||
};
|
||||
|
||||
class Window {
|
||||
public:
|
||||
static bool StrictInitialization;
|
||||
|
||||
static int getGLFWHint(int);
|
||||
static int setGLFWHint(int, int);
|
||||
static Window* getCurrent();
|
||||
|
||||
Window(const std::array<uint16_t, 2>, const char*, GLFWmonitor* initMonitor=NULL, GLFWwindow* initWindow=NULL);
|
||||
Window(const uint16_t, const uint16_t, const char*, GLFWmonitor* initMonitor=NULL, GLFWwindow* initWindow=NULL);
|
||||
~Window();
|
||||
|
||||
int getWindowHint(int) const;
|
||||
GLFWwindow* getWindow() const;
|
||||
std::array<uint16_t, 2> getSize() const;
|
||||
float getAspectRatio() const;
|
||||
std::string getName() const;
|
||||
|
||||
int init();
|
||||
int setWindowHint(int, int);
|
||||
void setCurrent();
|
||||
void resize(const std::array<uint16_t, 2>);
|
||||
void resize(const uint16_t x, const uint16_t y);
|
||||
|
||||
protected:
|
||||
static std::map<GLFWwindow*, Window*> WindowContexts;
|
||||
static std::map<int, int> GLFWHints;
|
||||
static Window* _current;
|
||||
static bool _glfw_init;
|
||||
static bool checkKillGLFW();
|
||||
|
||||
std::map<int, int> windowHints;
|
||||
std::array<uint16_t, 2> windowSize;
|
||||
float _aspect_ratio;
|
||||
std::string windowName;
|
||||
GLFWwindow* window;
|
||||
GLFWmonitor* monitor;
|
||||
GLFWwindow* shareWindow;
|
||||
bool _init = false;
|
||||
int gladResponse = -1;
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "Window.h"
|
||||
namespace NB {
|
||||
|
||||
std::map<GLFWwindow*, Window*> Window::WindowContexts = {};
|
||||
std::map<int, int> Window::GLFWHints = {};
|
||||
bool Window::StrictInitialization = true;
|
||||
bool Window::_glfw_init = false;
|
||||
Window* Window::_current = nullptr;
|
||||
static std::map<int, int> defailt_window_hints = {
|
||||
{GLFW_CONTEXT_VERSION_MAJOR, 3},
|
||||
{GLFW_CONTEXT_VERSION_MINOR, 1},
|
||||
{GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE}
|
||||
#ifdef _NB_GL_DEBUG_ON
|
||||
, {GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE}
|
||||
#endif
|
||||
};
|
||||
|
||||
GLError::GLError(const std::string& msg) : std::runtime_error(msg) {}
|
||||
|
||||
WindowError::WindowError(const std::string &msg) : std::runtime_error(msg) {}
|
||||
|
||||
int Window::getGLFWHint(int hint_key) {
|
||||
if (GLFWHints.find(hint_key) == GLFWHints.end()) {
|
||||
return 0;
|
||||
} else {
|
||||
return GLFWHints.at(hint_key);
|
||||
}
|
||||
}
|
||||
|
||||
int Window::setGLFWHint(int hint_key, int hint_val) {
|
||||
if (Window::_glfw_init) {
|
||||
throw GLError("Cannot set GLFW hint after window is initialized.");
|
||||
} else {
|
||||
GLFWHints[hint_key] = hint_val;
|
||||
}
|
||||
return GLFWHints[hint_key];
|
||||
}
|
||||
|
||||
Window* Window::getCurrent() {
|
||||
return _current;
|
||||
}
|
||||
|
||||
bool Window::checkKillGLFW() {
|
||||
if(Window::WindowContexts.size() == 0 && _glfw_init) {
|
||||
glfwTerminate();
|
||||
Window::_glfw_init = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Window::Window(const uint16_t x, const uint16_t y, const char* initName, GLFWmonitor* initMonitor, GLFWwindow* initWindow) {
|
||||
if (!Window::_glfw_init) {
|
||||
for (const auto& hint : Window::GLFWHints) {
|
||||
glfwInitHint(hint.first, hint.second);
|
||||
}
|
||||
int glfwResponse = glfwInit();
|
||||
if (glfwResponse) {
|
||||
Window::_glfw_init = true;
|
||||
} else {
|
||||
if (Window::StrictInitialization) {
|
||||
throw GLError("Failed to load GLFW with glfwInit()="+std::to_string(glfwResponse)+".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
windowSize = {x, y};
|
||||
_aspect_ratio = float(x)/float(y);
|
||||
windowName = std::string(initName);
|
||||
monitor = initMonitor;
|
||||
shareWindow = initWindow;
|
||||
|
||||
for (const auto& hint: defailt_window_hints) {
|
||||
windowHints[hint.first] = hint.second;
|
||||
}
|
||||
}
|
||||
|
||||
Window::Window(const std::array<uint16_t, 2> initSize, const char* initName, GLFWmonitor* initMonitor, GLFWwindow* initWindow) :
|
||||
Window(initSize[0], initSize[1], initName, initMonitor, initWindow){}
|
||||
|
||||
Window::~Window() {
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
glfwDestroyWindow(window);
|
||||
Window::WindowContexts.erase(window);
|
||||
if (Window::WindowContexts.size()==0) {
|
||||
glfwTerminate();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::setCurrent() {
|
||||
_current = this;
|
||||
glfwMakeContextCurrent(window);
|
||||
}
|
||||
|
||||
int Window::getWindowHint(int hint_key) const {
|
||||
if (windowHints.find(hint_key) == windowHints.end()) {
|
||||
return 0;
|
||||
} else {
|
||||
return windowHints.at(hint_key);
|
||||
}
|
||||
}
|
||||
|
||||
int Window::setWindowHint(int hint_key, int hint_val) {
|
||||
if (_init) {
|
||||
throw WindowError("Cannot set window hint after window is initialized.");
|
||||
} else {
|
||||
windowHints[hint_key] = hint_val;
|
||||
}
|
||||
return windowHints[hint_key];
|
||||
}
|
||||
|
||||
int Window::init() {
|
||||
if (!_glfw_init) {
|
||||
throw GLError("GLFW has not been initialized.");
|
||||
}
|
||||
for (const auto& hint : windowHints) {
|
||||
glfwWindowHint(hint.first, hint.second);
|
||||
}
|
||||
|
||||
window = glfwCreateWindow(windowSize[0], windowSize[1], windowName.c_str(), monitor, shareWindow);
|
||||
if (window == NULL) {
|
||||
Window::WindowContexts.erase(window);
|
||||
if (Window::WindowContexts.size()==0 && Window::_glfw_init) { glfwTerminate(); }
|
||||
if (Window::StrictInitialization) {
|
||||
throw WindowError("Could not create window in glfwCreateWindow().");
|
||||
}
|
||||
}
|
||||
glfwMakeContextCurrent(window);
|
||||
Window::WindowContexts[window] = this;
|
||||
|
||||
gladResponse = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
||||
if (!gladResponse) {
|
||||
Window::checkKillGLFW();
|
||||
if (Window::StrictInitialization) {
|
||||
throw GLError("Failed to load GLAD with \
|
||||
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)="+\
|
||||
std::to_string(gladResponse)+"."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_init = true;
|
||||
glViewport(0, 0, windowSize[0], windowSize[1]);
|
||||
return gladResponse;
|
||||
}
|
||||
|
||||
GLFWwindow* Window::getWindow() const {
|
||||
return window;
|
||||
}
|
||||
|
||||
float Window::getAspectRatio() const { return _aspect_ratio; }
|
||||
|
||||
std::array<uint16_t, 2> Window::getSize() const {
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
std::string Window::getName() const {
|
||||
return windowName;
|
||||
}
|
||||
|
||||
void Window::resize(const std::array<uint16_t, 2> newSize) {
|
||||
windowSize = newSize;
|
||||
_aspect_ratio = float(newSize[0]) / float(newSize[1]);
|
||||
glViewport(0, 0, windowSize[0], windowSize[1]);
|
||||
glfwSetWindowSize(window, windowSize[0], windowSize[1]);
|
||||
}
|
||||
|
||||
void Window::resize(const uint16_t x, const uint16_t y) { resize({x, y}); }
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user