Separating out into DataSink CRTP model. Removed SmartStreams
This commit is contained in:
parent
4c8c622f0b
commit
50e37b8421
@ -23,7 +23,7 @@ endif()
|
|||||||
find_package(OpenGL)
|
find_package(OpenGL)
|
||||||
add_subdirectory(../glfw ../glfw/build)
|
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_DOCS OFF CACHE BOOL "" FORCE)
|
||||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||||
|
|||||||
@ -2,74 +2,113 @@
|
|||||||
#ifndef _NB_DATASINK
|
#ifndef _NB_DATASINK
|
||||||
#define _NB_DATASINK
|
#define _NB_DATASINK
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "ThreadSafeQueue.hpp"
|
||||||
|
|
||||||
namespace nb {
|
namespace nb {
|
||||||
|
|
||||||
template <typename T>
|
template<typename DataType>
|
||||||
class SinkBase {
|
class DataSink {
|
||||||
using InputType = T;
|
|
||||||
public:
|
public:
|
||||||
virtual bool in(const T&) = 0;
|
DataSink(const DataSink&) = delete;
|
||||||
|
DataSink(DataSink&&) = delete;
|
||||||
|
DataSink& operator=(const DataSink&) = delete;
|
||||||
|
|
||||||
|
virtual bool isRunning() const noexcept {
|
||||||
|
return _running;
|
||||||
|
}
|
||||||
|
virtual bool stop() noexcept = 0;
|
||||||
|
virtual bool run() = 0;
|
||||||
|
virtual bool in(const DataType&) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SinkBase();
|
DataSink() = default;
|
||||||
|
|
||||||
|
std::atomic<bool> _running;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* template <typename T>
|
template<typename DataType, typename BufferType, typename ProcessorType>
|
||||||
class Sink : public SinkBase {
|
class BufferedDataProcessor : public DataSink<DataType> {
|
||||||
|
using Base = DataSink<DataType>;
|
||||||
public:
|
public:
|
||||||
Sink& operator=(const Sink&) = 0;
|
using Base::Base;
|
||||||
|
bool in(const DataType& val) { this->push(val); return true;}
|
||||||
template<typename U>
|
|
||||||
virtual bool in(const U&) = 0;
|
|
||||||
|
|
||||||
protected:
|
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;
|
||||||
|
|
||||||
}; */
|
using Base::_running;
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class SourceBase {
|
|
||||||
using OutputType = T;
|
|
||||||
public:
|
|
||||||
SourceBase& operator=(const SourceBase&) = 0;
|
|
||||||
|
|
||||||
virtual bool out(T*) = 0;
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
SourceBase();
|
|
||||||
SinkBase<T>* _sink;
|
|
||||||
|
|
||||||
|
BufferType _buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template<typename DataType, typename ProcessorType>
|
||||||
class Pipe : public SinkBase<T>, public SourceBase<T>{
|
class MultithreadedDataProcessor : public BufferedDataProcessor<DataType, ThreadsafeQueue<DataType>, ProcessorType> {
|
||||||
using Input = SourceBase<T>;
|
using Base = BufferedDataProcessor<DataType, ThreadsafeQueue<DataType>, ProcessorType>;
|
||||||
using Output = SinkBase<T>;
|
|
||||||
public:
|
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:
|
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
|
} // namespace nb
|
||||||
#endif // _NB_DATASINK
|
#endif // _NB_DATASINK
|
||||||
@ -9,6 +9,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "DataSink.hpp"
|
||||||
#include "Processes.hpp"
|
#include "Processes.hpp"
|
||||||
#include "ThreadSafeQueue.hpp"
|
#include "ThreadSafeQueue.hpp"
|
||||||
|
|
||||||
@ -31,75 +32,30 @@ struct LogEvent{
|
|||||||
typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&);
|
typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&);
|
||||||
typedef std::unordered_map<uint8_t, LogProcessFunction> LogProcessFunctionMap;
|
typedef std::unordered_map<uint8_t, LogProcessFunction> LogProcessFunctionMap;
|
||||||
|
|
||||||
template<typename LoggerName, typename ST=std::ostream>
|
/* template<typename LogType, typename Logger, typename ST=std::ostream>
|
||||||
class LoggerBase {
|
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:
|
public:
|
||||||
using Type = LoggerName;
|
DebugLogger(std::ostream& stream) : _ostream(&stream) {}
|
||||||
using StreamType = ST;
|
|
||||||
|
|
||||||
LoggerBase(StreamType& stream) : _ostreams({&stream}) {}
|
~DebugLogger() { stop(); }
|
||||||
LoggerBase(const LoggerBase&) = delete;
|
|
||||||
LoggerBase(LoggerBase&&) = delete;
|
|
||||||
|
|
||||||
LoggerBase& operator=(const LoggerBase&) = delete;
|
virtual bool stop() noexcept override {
|
||||||
|
auto ret = MultithreadedDataProcessor<LogEvent, DebugLogger>::stop();
|
||||||
~LoggerBase() {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
flush();
|
||||||
}
|
return ret;
|
||||||
return isRunning();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LogEvent pop() {
|
void log(const std::string& msg, const uint8_t& lvl=0xFF) {
|
||||||
std::shared_ptr<LogEvent> event;
|
push(LogEvent{
|
||||||
_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{
|
|
||||||
std::chrono::system_clock::now(),
|
std::chrono::system_clock::now(),
|
||||||
lvl,
|
lvl,
|
||||||
msg,
|
msg,
|
||||||
@ -109,48 +65,31 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
void log(char const(&msg) [N], const uint8_t& lvl) {
|
void log(char const(&msg) [N], const uint8_t& lvl=0xFF) {
|
||||||
static_cast<Type*>(this)->log(std::string(msg), lvl);
|
log(std::string(msg), lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log(const std::exception& err, const uint8_t& lvl) {
|
void log(const std::exception& err, const uint8_t& lvl=0xFF) {
|
||||||
_queue.push(LogEvent{
|
log(err.what(), lvl);
|
||||||
std::chrono::system_clock::now(),
|
|
||||||
lvl,
|
|
||||||
err.what(),
|
|
||||||
std::this_thread::get_id(),
|
|
||||||
GetPID(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void msg(const std::string& msg, const uint8_t& lvl=0x00) {
|
template<typename U>
|
||||||
static_cast<Type*>(this)->log(msg, lvl);
|
void warn(const U& val, const uint8_t& lvl=0x01) { log(val, lvl); }
|
||||||
}
|
|
||||||
|
|
||||||
void warn(const std::string& msg, const uint8_t& lvl=0x01) {
|
template<typename U>
|
||||||
static_cast<Type*>(this)->log(msg, lvl);
|
void error(const U& val, const uint8_t& lvl=0xFF) { log(val, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void process(const LogEvent&);
|
std::ostream* const _ostream;
|
||||||
|
|
||||||
|
virtual bool process(const LogEvent& msg) override {
|
||||||
|
*_ostream << msg.pid << " - " << msg.tid << " : " << msg.msg << "\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ThreadsafeQueue<LogEvent> _queue;
|
|
||||||
std::vector<StreamType*> const _ostreams;
|
|
||||||
std::atomic<bool> _running;
|
|
||||||
std::shared_ptr<std::thread> _runningThread = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BasicLogger : public LoggerBase<BasicLogger> {
|
/* class BasicLogger : public LoggerBase<BasicLogger> {
|
||||||
public:
|
public:
|
||||||
using Base = LoggerBase<BasicLogger>;
|
using Base = LoggerBase<BasicLogger>;
|
||||||
BasicLogger(std::ostream& stream) : Base(stream) {}
|
BasicLogger(std::ostream& stream) : Base(stream) {}
|
||||||
@ -158,7 +97,7 @@ public:
|
|||||||
virtual void process(const LogEvent& event) {
|
virtual void process(const LogEvent& event) {
|
||||||
*_ostreams[0] << event.msg << "\n";
|
*_ostreams[0] << event.msg << "\n";
|
||||||
}
|
}
|
||||||
};
|
}; */
|
||||||
|
|
||||||
} // namespace nb
|
} // namespace nb
|
||||||
#endif // _NB_LOGGER
|
#endif // _NB_LOGGER
|
||||||
@ -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
|
|
||||||
@ -26,20 +26,15 @@ const ErrorCodeMap TestError::ErrorMessages{
|
|||||||
{TestError::ErrorCodes::D, "Doin"}
|
{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) {
|
TEST(ErrorTest, Test) {
|
||||||
EXPECT_EQ(1, 1);
|
EXPECT_EQ(1, 1);
|
||||||
|
|
||||||
TestLogger log(std::cout);
|
nb::DebugLogger log(std::cout);
|
||||||
ASSERT_TRUE(log.run());
|
ASSERT_TRUE(log.run());
|
||||||
log.msg("Hey!");
|
ASSERT_TRUE(log.isRunning());
|
||||||
|
while (!log.isRunning()){
|
||||||
|
1+1;
|
||||||
|
}
|
||||||
|
log.log("Hey!");
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user