'C++ handle changing types in template
I'm writing a simple event loop, where I have lambda's to be queued for execution a little like so:
eventloop->enqueue(3000, []{
// do some work
});
The queued events are stored in a template<typename T> class Job { /* fields */ }
This ensures all lambda's that I enqueue will have return type T. But I want to be able to let the lambda's have any set of parameter types, without having to create a new event loop and add that particular lambda to its type with a parameter pack like so:
template<typename T, typename... Ts> class Job { /* fields */ } this will mean all lambda's will have same set of parameters.
Is there any way to achieve what I want without involving something like boost::any (I am limited to C++11 hence boost and not std::any)
test.cpp
#include <thread>
#include <iostream>
#include "ev_loop/ev_loop.h"
int main() {
ev::EvLoop<void> a(3);
a.enqueue(ev::Job<void>([]() {
std::cout << "Running a task" << std::endl;
}));
// just for illustration
std::this_thread::sleep_for(std::chrono::hours(1));
return 0;
}
ev_loop.h
#ifndef EV_LOOP_H
#define EV_LOOP_H
#include <mutex>
#include <memory>
#include <unordered_map>
#include <condition_variable>
#include <boost/lockfree/spsc_queue.hpp>
namespace ev {
template<typename T>
class Job {
public:
Job(std::function<T()> func): func(func) {}
T Run();
private:
std::function<T()> func;
};
typedef uint32_t u32;
template<typename T>
using WorkQueue = boost::lockfree::spsc_queue<Job<T>, boost::lockfree::capacity<128>>;
template<typename T>
struct WorkerOptions {
u32 id;
std::shared_ptr<WorkQueue<T>> queue;
std::shared_ptr<std::mutex> mtx;
std::shared_ptr<std::condition_variable> cv;
};
template<typename T>
class Worker {
public:
Worker() = default;
Worker(WorkerOptions<T> options): work_queue_mutex(*options.mtx), id(options.id), queue(options.queue), cv(options.cv) {}
void run();
private:
u32 id;
std::shared_ptr<WorkQueue<T>> queue;
std::mutex &work_queue_mutex;
std::shared_ptr<std::condition_variable> cv;
};
template<typename T>
class EvLoop {
public:
EvLoop(u32 num_workers);
void enqueue(Job<T> j);
private:
std::mutex worker_mtx;
std::unordered_map<u32, WorkerOptions<T>> work_queues;
};
}
#endif
ev_loop.cpp
#include <mutex>
#include <thread>
#include <condition_variable>
#include "ev_loop.h"
using namespace ev;
template<typename T>
EvLoop<T>::EvLoop(u32 num_workers) {
for (u32 i = 0; i < num_workers; i++) {
this->work_queues[i] = WorkerOptions<T>{i+1, std::make_shared<WorkQueue<T>>(), std::make_shared<std::mutex>(), std::make_shared<std::condition_variable>()};
WorkerOptions<T> q = this->work_queues[i];
Worker<T> temp(q);
std::thread(&Worker<T>::run, temp).detach();
}
}
template<typename T>
void EvLoop<T>::enqueue(Job<T> j) {
// for example only
this->work_queues[0].queue->push(j);
this->work_queues[0].cv->notify_one();
}
template<typename T>
void Worker<T>::run() {
while (true) {
std::unique_lock<std::mutex> lock(work_queue_mutex);
cv->wait(lock, [this]{return !queue->empty();});
queue->consume_one([&](Job<T> item) {
item.Run();
// give result back to client through a channel or something
});
lock.unlock();
}
}
template<typename T>
T Job<T>::Run() {
return func();
}
template class EvLoop<void>;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
