Separating out into DataSink CRTP model. Removed SmartStreams

This commit is contained in:
NaifBanana 2026-01-27 22:25:12 -06:00
parent 4c8c622f0b
commit 50e37b8421
5 changed files with 130 additions and 281 deletions

View File

@ -23,7 +23,7 @@ endif()
find_package(OpenGL)
add_subdirectory(../glfw ../glfw/build)
include_directories(./dep ./include ../glfw/include ../glad/include)
include_directories(../glfw/include ../glad/include)
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)

View File

@ -2,74 +2,113 @@
#ifndef _NB_DATASINK
#define _NB_DATASINK
#include <thread>
#include "ThreadSafeQueue.hpp"
namespace nb {
template <typename T>
class SinkBase {
using InputType = T;
template<typename DataType>
class DataSink {
public:
virtual bool in(const T&) = 0;
DataSink(const DataSink&) = delete;
DataSink(DataSink&&) = delete;
DataSink& operator=(const DataSink&) = delete;
protected:
SinkBase();
virtual bool isRunning() const noexcept {
return _running;
}
virtual bool stop() noexcept = 0;
virtual bool run() = 0;
virtual bool in(const DataType&) = 0;
protected:
DataSink() = default;
std::atomic<bool> _running;
};
/* template <typename T>
class Sink : public SinkBase {
template<typename DataType, typename BufferType, typename ProcessorType>
class BufferedDataProcessor : public DataSink<DataType> {
using Base = DataSink<DataType>;
public:
Sink& operator=(const Sink&) = 0;
template<typename U>
virtual bool in(const U&) = 0;
using Base::Base;
bool in(const DataType& val) { this->push(val); return true;}
protected:
Sink();
virtual unsigned int count() const = 0;
virtual void push(const DataType&) = 0;
virtual DataType pop() = 0;
virtual void flush() = 0;
virtual void clear() = 0;
virtual bool process(const DataType& val) = 0;
}; */
template <typename T>
class SourceBase {
using OutputType = T;
public:
SourceBase& operator=(const SourceBase&) = 0;
virtual bool out(T*) = 0;
protected:
SourceBase();
SinkBase<T>* _sink;
using Base::_running;
BufferType _buffer;
};
template <typename T>
class Pipe : public SinkBase<T>, public SourceBase<T>{
using Input = SourceBase<T>;
using Output = SinkBase<T>;
template<typename DataType, typename ProcessorType>
class MultithreadedDataProcessor : public BufferedDataProcessor<DataType, ThreadsafeQueue<DataType>, ProcessorType> {
using Base = BufferedDataProcessor<DataType, ThreadsafeQueue<DataType>, ProcessorType>;
public:
Pipe(Sink<T>& sink) : Input::_sink{&sink} {}
~MultithreadedDataProcessor() { this->stop(); }
bool in(const T&) = 0;
bool isRunning() const noexcept override {
return this->_running && _runningThread;
}
bool run() override {
if (!isRunning()) {
this->_running = true;
_runningThread = std::make_shared<std::thread>([&]{
auto this_type = static_cast<ProcessorType*>(this);
while(this_type->isRunning()) {
this_type->flush();
}
});
}
return isRunning();
}
bool stop() noexcept override {
if (isRunning()) {
this->_running = false;
_runningThread->join();
_runningThread = nullptr;
auto this_type = static_cast<ProcessorType*>(this);
}
return !isRunning();
}
protected:
using Input::_sink;
using Base::Base;
unsigned int count() const override {
return this->_buffer.size();
}
virtual void push(const DataType& val) {
this->_buffer.push(val);
};
virtual DataType pop() {
std::shared_ptr<DataType> event;
this->_buffer.pop(event);
process(*event);
return *event;
}
virtual void flush() override {
auto this_type = static_cast<ProcessorType*>(this);
while(this_type->count()) {
this_type->pop();
}
}
virtual void clear() override {
this->_buffer.empty();
}
virtual bool process(const DataType&) = 0;
std::shared_ptr<std::thread> _runningThread;
};
/* 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

View File

@ -9,6 +9,7 @@
#include <unordered_map>
#include <vector>
#include "DataSink.hpp"
#include "Processes.hpp"
#include "ThreadSafeQueue.hpp"
@ -31,75 +32,30 @@ struct LogEvent{
typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&);
typedef std::unordered_map<uint8_t, LogProcessFunction> LogProcessFunctionMap;
template<typename LoggerName, typename ST=std::ostream>
class LoggerBase {
/* template<typename LogType, typename Logger, typename ST=std::ostream>
class LoggerBase;
template<typename Logger>
class LoggerBase<typename Logger::LogType, Logger, typename Logger::StreamType>
: public MultithreadedDataProcessor<typename Logger::LogType, Logger>{
protected:
typename Logger::StreamType _ostreams;
}; */
class DebugLogger : public MultithreadedDataProcessor<LogEvent, DebugLogger>{
public:
using Type = LoggerName;
using StreamType = ST;
DebugLogger(std::ostream& stream) : _ostream(&stream) {}
LoggerBase(StreamType& stream) : _ostreams({&stream}) {}
LoggerBase(const LoggerBase&) = delete;
LoggerBase(LoggerBase&&) = delete;
~DebugLogger() { stop(); }
LoggerBase& operator=(const LoggerBase&) = delete;
~LoggerBase() {
stop();
virtual bool stop() noexcept override {
auto ret = MultithreadedDataProcessor<LogEvent, DebugLogger>::stop();
flush();
return ret;
}
bool isRunning() const noexcept {
return _running && bool(_runningThread);
}
unsigned int count() const {
return _queue.size();
}
bool run() {
if (!isRunning()) {
_running = true;
_runningThread = std::make_shared<std::thread>([&]{
auto this_type = static_cast<Type*>(this);
while(this_type->isRunning()) {
this_type->flush();
}
});
}
return isRunning();
}
bool stop() noexcept {
if (isRunning()) {
_running = false;
_runningThread->join();
_runningThread = nullptr;
flush();
}
return isRunning();
}
LogEvent pop() {
std::shared_ptr<LogEvent> event;
_queue.pop(event);
static_cast<Type*>(this)->process(*event);
return *event;
}
void flush() noexcept {
while(_queue.size()) {
pop();
}
}
void deleteAll() {
_queue.empty();
}
template <typename T>
void log(const T&, const uint8_t);
void log(const std::string& msg, const uint8_t& lvl) {
_queue.push(LogEvent{
void log(const std::string& msg, const uint8_t& lvl=0xFF) {
push(LogEvent{
std::chrono::system_clock::now(),
lvl,
msg,
@ -109,48 +65,31 @@ public:
}
template <size_t N>
void log(char const(&msg) [N], const uint8_t& lvl) {
static_cast<Type*>(this)->log(std::string(msg), lvl);
void log(char const(&msg) [N], const uint8_t& lvl=0xFF) {
log(std::string(msg), lvl);
}
void log(const std::exception& err, const uint8_t& lvl) {
_queue.push(LogEvent{
std::chrono::system_clock::now(),
lvl,
err.what(),
std::this_thread::get_id(),
GetPID(),
});
void log(const std::exception& err, const uint8_t& lvl=0xFF) {
log(err.what(), lvl);
}
void msg(const std::string& msg, const uint8_t& lvl=0x00) {
static_cast<Type*>(this)->log(msg, lvl);
}
template<typename U>
void warn(const U& val, const uint8_t& lvl=0x01) { log(val, lvl); }
void warn(const std::string& msg, const uint8_t& lvl=0x01) {
static_cast<Type*>(this)->log(msg, lvl);
}
void warn(const std::exception& err, const uint8_t lvl=0x01) {
static_cast<Type*>(this)->log(err, lvl);
}
void error(const std::string& msg, const uint8_t lvl=0xFF) {
static_cast<Type*>(this)->log(msg, lvl);
}
void error(const std::exception& err, const uint8_t lvl=0xFF) {
static_cast<Type*>(this)->log(err, lvl);
}
template<typename U>
void error(const U& val, const uint8_t& lvl=0xFF) { log(val, lvl); }
protected:
void process(const LogEvent&);
std::ostream* const _ostream;
ThreadsafeQueue<LogEvent> _queue;
std::vector<StreamType*> const _ostreams;
std::atomic<bool> _running;
std::shared_ptr<std::thread> _runningThread = nullptr;
virtual bool process(const LogEvent& msg) override {
*_ostream << msg.pid << " - " << msg.tid << " : " << msg.msg << "\n";
return true;
}
};
class BasicLogger : public LoggerBase<BasicLogger> {
/* class BasicLogger : public LoggerBase<BasicLogger> {
public:
using Base = LoggerBase<BasicLogger>;
BasicLogger(std::ostream& stream) : Base(stream) {}
@ -158,7 +97,7 @@ public:
virtual void process(const LogEvent& event) {
*_ostreams[0] << event.msg << "\n";
}
};
}; */
} // namespace nb
#endif // _NB_LOGGER

View File

@ -1,124 +0,0 @@
#pragma once
#ifndef _NB_SMARTSTREAM
#define _NB_SMARTSTREAM
#include <array>
#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include <type_traits>
#include <utility>
#include <vector>
namespace nb {
template <typename StreamType, typename... Mixins>
class SmartStream;
template<typename Wrapper, typename U, typename Mix>
struct MixinHasProcess {
protected:
template <typename M>
static auto test(int)->decltype(
std::declval<M>().template process<Wrapper>(std::declval<const U&>()),
std::true_type{}
);
template <typename>
static std::false_type test(...);
public:
static const bool value = decltype(test<Mix>(0))::value;
};
template<typename Wrapper, typename U, typename... Mixins>
struct PackHasProcess;
template<typename Wrapper, typename U, typename Mix>
struct PackHasProcess<Wrapper, U, Mix> {
static const bool value = MixinHasProcess<Wrapper, U, Mix>::value;
using type = typename std::conditional<
value,
Mix,
void
>::type;
};
template<typename Wrapper, typename U, typename Mix1, typename... Mixins>
struct PackHasProcess<Wrapper, U, Mix1, Mixins...> {
protected:
static const bool base_value = MixinHasProcess<Wrapper, U, Mix1>::value;
public:
static const bool value = std::conditional<
base_value,
std::true_type,
typename PackHasProcess<Wrapper, U, Mixins...>::value
>::type::value;
using type = typename std::conditional<
base_value,
Mix1,
typename PackHasProcess<Wrapper, U, Mixins...>::type
>::type;
};
/* template <typename T, typename=void>
struct HasStreamType : std::false_type {};
template <typename T>
struct HasStreamType<T, > : std::false_type {}; */
template<typename ST, typename... Mixins>
class SmartStream : public Mixins... {
using StreamType = typename std::conditional<
decltype(std::declval<ST::StreamType>(), std::true_type{}),
ST::StreamType,
ST
>::type;
protected:
StreamType* const _stream;
public:
SmartStream(StreamType& stream) : _stream(&stream), Mixins(&stream)... {}
using Mixins::process...;
template <typename U, typename StreamFmt=SmartStream<StreamType, Mixins...>>
typename std::enable_if<PackHasProcess<SmartStream, U, Mixins...>::value, void>::type
toStream(const U& val, const StreamFmt* fmtas=nullptr) {
PackHasProcess<SmartStream, U, Mixins...>::type::template process<SmartStream>(val);
}
template <typename U, typename StreamFmt=SmartStream<StreamType, Mixins...>>
typename std::enable_if<!PackHasProcess<SmartStream, U, Mixins...>::value, void>::type
toStream(const U& val, const StreamFmt* fmtas=nullptr) {
*_stream << val;
}
template <typename U>
friend SmartStream& operator<<(SmartStream& stream, const U& val) {
stream.toStream(val);
return stream;
}
};
struct SmartText {
int x;
};
template <typename StreamType>
struct ANSIFormatter {
template <typename Wrapper>
void process(const SmartText& val, StreamType* fmtas = nullptr) {
static_cast<Wrapper*>(this)->toStream(val.x);
}
protected:
ANSIFormatter(StreamType* stream) : _stream(stream) {}
StreamType* const _stream;
};
}
#endif // _NB_SMARTSTREAM

View File

@ -26,20 +26,15 @@ const ErrorCodeMap TestError::ErrorMessages{
{TestError::ErrorCodes::D, "Doin"}
};
class TestLogger : public nb::LoggerBase<TestLogger> {
public:
using Base = LoggerBase<TestLogger>;
using Base::Base;
virtual void process(const LogEvent& event) {
*_ostreams[0] << event.pid << " : " << event.msg << std::endl;
}
};
TEST(ErrorTest, Test) {
EXPECT_EQ(1, 1);
TestLogger log(std::cout);
nb::DebugLogger log(std::cout);
ASSERT_TRUE(log.run());
log.msg("Hey!");
ASSERT_TRUE(log.isRunning());
while (!log.isRunning()){
1+1;
}
log.log("Hey!");
}