I recently started work on my own Async file input "library" in C++. I got it done today and I decided to put it on Code Review to see how well I actually did, and where I can still improve the code.
The log++
#include
is just some logging header I created for writing colored output to the console.
EDIT: Since the time I have posted this I made some huge changes to the code, so I guess I will post an updated version of it below:
AsyncTask.h
#pragma once
#include <iostream>
#include <fstream>
#include <thread>
#include <string>
#include <sstream>
#include <mutex>
#include <functional>
#include <condition_variable>
#include "logpp/log++.h"
class AsyncTask
{
public:
AsyncTask();
template<typename Callable, typename ...Args>
explicit AsyncTask(Callable task, Args&&... args);
~AsyncTask();
/*Manually starts the task if it is not already running*/
template<typename Callable, typename ...Args>
void start(Callable func, Args&&... args);
/*Interrupts the task until resume() is called*/
void pause();
/*Unpauses the task*/
void resume();
/*Blocks calling thread until the task is done*/
void wait();
bool is_complete();
/*Stops the task and resets all it's data*/
virtual void reset();
private:
std::thread m_exec_thread;
bool m_running;
bool m_paused;
std::condition_variable m_pause_cv;
std::mutex m_pause_mutex;
protected:
void handle_pause_update();
void toggle_running();
};
template<typename Callable, typename... Args>
AsyncTask::AsyncTask(Callable task, Args&&... args) : m_running(false), m_paused(false)
{
start(task, std::forward<Args>(args)...);
}
template<typename Callable, typename ...Args>
void AsyncTask::start(Callable func, Args&&... args)
{
/*Only start the task if it is not already running*/
if (!m_running)
{
m_exec_thread = std::thread(func, std::forward<Args>(args)...);
m_running = true;
}
}
class AsyncReadTask : public AsyncTask
{
public:
AsyncReadTask();
explicit AsyncReadTask(std::string const& filename);
/*Obtain data that was read from the file. Will reset the task*/
std::stringstream get_data();
void start(std::string const& filename);
virtual void reset() override;
private:
std::stringstream m_data;
std::string m_file;
void read(std::string const& file_name);
};
AsyncTask.cpp
#include "AsyncTask.h"
#pragma region AsyncTaskImpl
AsyncTask::AsyncTask()
{
}
void AsyncTask::handle_pause_update()
{
while (m_paused) //check if we have to pause
{
std::unique_lock<std::mutex> lk { m_pause_mutex };
m_pause_cv.wait(lk);
lk.unlock();
}
}
void AsyncTask::pause()
{
if (m_paused) //don't double pause
return;
std::lock_guard<std::mutex> lk { m_pause_mutex };
m_paused = true;
}
void AsyncTask::resume()
{
std::lock_guard<std::mutex> lk { m_pause_mutex };
m_paused = false;
m_pause_cv.notify_one();
}
void AsyncTask::wait()
{
while (m_running)
{
//wait
}
}
bool AsyncTask::is_complete()
{
return !m_running;
}
void AsyncTask::reset()
{
wait();
m_running = false;
}
void AsyncTask::toggle_running()
{
m_running = !m_running;
}
AsyncTask::~AsyncTask()
{
m_exec_thread.join();
}
#pragma endregion AsyncTaskImpl
#pragma region AsyncReadTaskImpl
std::stringstream AsyncReadTask::get_data()
{
wait();
auto data = std::move(m_data);
reset();
return std::move(data);
}
void AsyncReadTask::read(std::string const& file_name)
{
/*---Open File---*/
std::ifstream in(file_name);
logpp::Console::log_assert(in.good() && in.is_open(), "Could not open file: " + file_name);
/*---Read data---*/
std::string line;
while (std::getline(in, line))
{
line += "\n"; //getline removes the newline character, but we want to give the stringstream the exact same data as the file
m_data << line;
handle_pause_update();
}
/*---Cleanup---*/
in.close();
toggle_running();
}
AsyncReadTask::AsyncReadTask()
{
}
AsyncReadTask::AsyncReadTask(std::string const& filename) : AsyncTask(&AsyncReadTask::read, this, filename), m_file(filename)
{
}
void AsyncReadTask::start(std::string const& file_name)
{
AsyncTask::start(&AsyncReadTask::read, this, file_name);
}
void AsyncReadTask::reset()
{
AsyncTask::reset();
m_data.clear();
m_file = "";
}
#pragma endregion AsyncReadTaskImpl
The full source can be found on GitHub too.
#include <condition_variable>
Not sure why MSVC doesn't need that. \$\endgroup\$