build system and module refactoring + simple histogram scratch (#20)
* scratch adding histogram to image views Histograms should come from some sort of histogram service. This is currently just a POC. * custom logger implementation w/o spdlog * missing cmake file * fix tests * use operator<< over direct stream exposure * rm print header * add threading test + refactor towards interface libraries omits the need for =target_include_directories= calls /everywhere/ * rm print header * rm constexpr * templated thread_pool * fix doxyfile * default enable doc building * czi reader refactor * rm erroneous include expression * clang-format * single lib include with PUBLIC visibility * compile imgui stdlib * clang format * documentation update centralize `LogLevelToString` to `ILog.hpp` update docs and examples
This commit is contained in:
committed by
Maximilian Kueffner
parent
b37814204f
commit
c00c2c71ac
@@ -1,14 +1,17 @@
|
||||
set(UTILSLIBNAME pixelariumutilslib)
|
||||
|
||||
set(UTILSLIBSRC
|
||||
ILog.hpp
|
||||
SpdLogger.hpp
|
||||
include/ILog.hpp
|
||||
include/SpdLogger.hpp
|
||||
include/PixelariumLogger.hpp
|
||||
include/simple_thread_pool.hpp
|
||||
SpdLogger.cpp
|
||||
simple_thread_pool.hpp
|
||||
simple_thread_pool.cpp)
|
||||
PixelariumLogger.cpp)
|
||||
|
||||
add_library(${UTILSLIBNAME} STATIC ${UTILSLIBSRC})
|
||||
|
||||
add_library(pixelarium::lib::utilities_static ALIAS ${UTILSLIBNAME})
|
||||
|
||||
# won't work
|
||||
# target_compile_options(${UTILSLIBNAME}
|
||||
# PRIVATE
|
||||
@@ -16,7 +19,9 @@ add_library(${UTILSLIBNAME} STATIC ${UTILSLIBSRC})
|
||||
# "$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
||||
|
||||
target_include_directories(${UTILSLIBNAME}
|
||||
INTERFACE
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
PRIVATE ${spdlog_DIR}/include)
|
||||
|
||||
target_link_libraries(${UTILSLIBNAME}
|
||||
PRIVATE spdlog::spdlog_header_only)
|
||||
PUBLIC spdlog::spdlog_header_only)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "PixelariumLogger.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace pixelarium::utils::log;
|
||||
|
||||
struct PixelariumLogger::LogStream
|
||||
{
|
||||
LogStream(const std::string& sink)
|
||||
{
|
||||
out_stream_ = std::ofstream(sink, std::ios::app);
|
||||
if (!out_stream_)
|
||||
{
|
||||
throw std::runtime_error("Failed to open log stream");
|
||||
}
|
||||
}
|
||||
|
||||
~LogStream()
|
||||
{
|
||||
if (out_stream_.is_open())
|
||||
{
|
||||
out_stream_.close();
|
||||
}
|
||||
}
|
||||
|
||||
LogStream& operator<<(const std::string& str)
|
||||
{
|
||||
this->out_stream_ << str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ofstream out_stream_;
|
||||
};
|
||||
|
||||
PixelariumLogger::PixelariumLogger(const std::string& name, const std::string& file_sink)
|
||||
: name_(name), file_sink_(file_sink)
|
||||
{
|
||||
log_stream_ = std::make_unique<LogStream>(file_sink_);
|
||||
}
|
||||
|
||||
PixelariumLogger::~PixelariumLogger() = default;
|
||||
|
||||
auto PixelariumLogger::Write(LogLevel lvl, const std::string& msg) const -> void
|
||||
{
|
||||
if (lvl < this->level_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm;
|
||||
#ifdef _WIN32
|
||||
localtime_s(&tm, &time_t);
|
||||
#else
|
||||
localtime_r(&time_t, &tm);
|
||||
#endif
|
||||
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
|
||||
|
||||
auto timestamp = std::format("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}", tm.tm_year + 1900, tm.tm_mon + 1,
|
||||
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ms.count());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
*log_stream_ << std::format("[{}] [{}] [{}] {}\n", timestamp, name_, LogLevelToString(lvl), msg);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,24 @@ enum class LogLevel
|
||||
kWarn = 1 << 3,
|
||||
kError = 1 << 4,
|
||||
};
|
||||
|
||||
constexpr auto LogLevelToString(LogLevel lvl) -> std::string
|
||||
{
|
||||
switch (lvl)
|
||||
{
|
||||
case LogLevel::kTrace:
|
||||
return "Trace";
|
||||
case LogLevel::kDebug:
|
||||
return "Debug";
|
||||
case LogLevel::kInfo:
|
||||
return "Info";
|
||||
case LogLevel::kWarn:
|
||||
return "Warning";
|
||||
case LogLevel::kError:
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Interface for logging implementations.
|
||||
class ILog
|
||||
{
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "ILog.hpp"
|
||||
|
||||
namespace pixelarium::utils::log
|
||||
{
|
||||
class PixelariumLogger : public ILog
|
||||
{
|
||||
private:
|
||||
struct LogStream;
|
||||
|
||||
public:
|
||||
explicit PixelariumLogger(const std::string& name, const std::string& file_sink);
|
||||
~PixelariumLogger();
|
||||
|
||||
void Info(const std::string& msg) const override { this->Write(LogLevel::kInfo, msg); }
|
||||
void Debug(const std::string& msg) const override { this->Write(LogLevel::kDebug, msg); }
|
||||
void Warn(const std::string& msg) const override { this->Write(LogLevel::kWarn, msg); }
|
||||
void Error(const std::string& msg) const override { this->Write(LogLevel::kError, msg); }
|
||||
void ChangeLevel(LogLevel lvl) const override { this->level_ = lvl; }
|
||||
|
||||
private:
|
||||
void Write(LogLevel, const std::string&) const;
|
||||
|
||||
private:
|
||||
std::mutex mutable mutex_;
|
||||
std::string name_;
|
||||
std::string file_sink_;
|
||||
std::unique_ptr<LogStream> log_stream_{};
|
||||
LogLevel mutable level_{LogLevel::kDebug};
|
||||
};
|
||||
} // namespace pixelarium::utils::log
|
||||
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace pixelarium::utils
|
||||
{
|
||||
template <size_t N>
|
||||
class simple_thread_pool
|
||||
{
|
||||
public:
|
||||
simple_thread_pool()
|
||||
{
|
||||
static_assert(N > 0, "Pool must have at least one thread.");
|
||||
for (size_t i{0}; i < N; ++i)
|
||||
{
|
||||
workers_.emplace_back(
|
||||
[this]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
std::function<void()> job;
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(thread_mutex_);
|
||||
|
||||
cv_.wait(lck, [this]() -> bool { return shutdown_ || !task_queue_.empty(); });
|
||||
|
||||
if (shutdown_ && task_queue_.empty()) return;
|
||||
|
||||
job = std::move(task_queue_.front());
|
||||
task_queue_.pop();
|
||||
++running_tasks_;
|
||||
}
|
||||
|
||||
job();
|
||||
--running_tasks_;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
simple_thread_pool(simple_thread_pool&) = delete;
|
||||
simple_thread_pool(const simple_thread_pool&) = delete;
|
||||
simple_thread_pool(simple_thread_pool&&) = delete;
|
||||
simple_thread_pool& operator=(simple_thread_pool&) = delete;
|
||||
simple_thread_pool& operator=(simple_thread_pool&&) = delete;
|
||||
~simple_thread_pool()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(thread_mutex_);
|
||||
shutdown_ = true;
|
||||
}
|
||||
|
||||
cv_.notify_all();
|
||||
for (auto& th : workers_)
|
||||
{
|
||||
th.join();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename Callable>
|
||||
requires std::invocable<Callable>
|
||||
auto enqueue(Callable&& fun) -> void
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(thread_mutex_);
|
||||
task_queue_.emplace(std::forward<Callable>(fun));
|
||||
}
|
||||
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
decltype(auto) RunningTasks() const
|
||||
{
|
||||
return running_tasks_.load();
|
||||
}
|
||||
|
||||
decltype(auto) Joinable() const
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(thread_mutex_);
|
||||
|
||||
return task_queue_.empty() && RunningTasks() == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::thread> workers_;
|
||||
std::condition_variable cv_;
|
||||
std::mutex mutable thread_mutex_;
|
||||
std::queue<std::function<void()>> task_queue_;
|
||||
bool shutdown_{false};
|
||||
std::atomic_size_t running_tasks_{0};
|
||||
};
|
||||
|
||||
class pixelarium_pool
|
||||
{
|
||||
public:
|
||||
[[nodiscard]]
|
||||
static decltype(auto) RunningTasks()
|
||||
{
|
||||
return pixelarium_pool::instance().RunningTasks();
|
||||
}
|
||||
|
||||
static decltype(auto) Joinable() { return pixelarium_pool::instance().Joinable(); }
|
||||
|
||||
template <typename Callable>
|
||||
requires std::invocable<Callable>
|
||||
static auto enqueue(Callable&& fun) -> void
|
||||
{
|
||||
pixelarium_pool::instance().enqueue(std::forward<Callable>(fun));
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto kThreadCount{20};
|
||||
static simple_thread_pool<kThreadCount>& instance()
|
||||
{
|
||||
static simple_thread_pool<kThreadCount> pixelarium_pool_instance;
|
||||
return pixelarium_pool_instance;
|
||||
}
|
||||
};
|
||||
} // namespace pixelarium::utils
|
||||
@@ -1,47 +0,0 @@
|
||||
#include "simple_thread_pool.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
using namespace pixelarium::utils;
|
||||
|
||||
simple_thread_pool::simple_thread_pool(size_t num_threads)
|
||||
{
|
||||
for (size_t i{0}; i < num_threads; ++i)
|
||||
{
|
||||
workers_.emplace_back(
|
||||
[this]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
std::function<void()> job;
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(thread_mutex_);
|
||||
|
||||
cv_.wait(lck, [this]() -> bool { return shutdown_ || !task_queue_.empty(); });
|
||||
|
||||
if (shutdown_ && task_queue_.empty()) return;
|
||||
|
||||
job = std::move(task_queue_.front());
|
||||
task_queue_.pop();
|
||||
}
|
||||
|
||||
job();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
simple_thread_pool::~simple_thread_pool()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(thread_mutex_);
|
||||
shutdown_ = true;
|
||||
}
|
||||
|
||||
cv_.notify_all();
|
||||
for (auto& th : workers_)
|
||||
{
|
||||
th.join();
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace pixelarium::utils
|
||||
{
|
||||
class simple_thread_pool
|
||||
{
|
||||
public:
|
||||
explicit simple_thread_pool(size_t);
|
||||
simple_thread_pool(simple_thread_pool&) = delete;
|
||||
simple_thread_pool(const simple_thread_pool&) = delete;
|
||||
simple_thread_pool(simple_thread_pool&&) = delete;
|
||||
simple_thread_pool& operator=(simple_thread_pool&) = delete;
|
||||
simple_thread_pool& operator=(simple_thread_pool&&) = delete;
|
||||
~simple_thread_pool();
|
||||
|
||||
template <typename Callable>
|
||||
requires std::invocable<Callable>
|
||||
static auto run_asynch(Callable&& fun) -> void
|
||||
{
|
||||
simple_thread_pool::Global().enqueue(std::forward<Callable>(fun));
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename Callable>
|
||||
requires std::invocable<Callable>
|
||||
auto enqueue(Callable&& fun) -> void
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lck(thread_mutex_);
|
||||
task_queue_.emplace(std::forward<Callable>(fun));
|
||||
}
|
||||
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
static auto Global() -> simple_thread_pool&
|
||||
{
|
||||
const auto kThreadCount{std::thread::hardware_concurrency() * 2};
|
||||
static simple_thread_pool global_instance(kThreadCount == 0 ? 5 : kThreadCount);
|
||||
return global_instance;
|
||||
}
|
||||
std::vector<std::thread> workers_;
|
||||
std::condition_variable cv_;
|
||||
std::mutex thread_mutex_;
|
||||
std::queue<std::function<void()>> task_queue_;
|
||||
bool shutdown_{false};
|
||||
};
|
||||
} // namespace pixelarium::utils
|
||||
Reference in New Issue
Block a user