/
TaskWrapperParameterized.h
121 lines (102 loc) · 2.91 KB
/
TaskWrapperParameterized.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#pragma once
#include <exception>
#include <memory>
#include <type_traits>
// Here you can find a wrapper that can be parameterized just like
// std::function, for example TaskWrapperP<int(float, double)>.
namespace _detail {
template<typename R>
struct vtable_p {
// This is just a base template class, so we can specialize it for a
// function signature. This one is not meant to be instantiated.
};
template<typename R, typename ...Ts>
struct vtable_p<R(Ts...)> {
R(*run)(void* ptr, Ts ...args);
void (*destroy)(void* ptr);
void (*clone)(void* storage, const void* ptr);
void (*move_clone)(void* storage, void* ptr);
};
// This template receive return type and arguments list from TaskWrapperP
// where it's instantiated. They are defined by TaskWrapperP template
// parameters.
template<typename Callable, typename R, typename... Ts>
constexpr vtable_p<R(Ts...)> vtable_p_for{
[](void* ptr, Ts ...args) -> R {
return static_cast<Callable*>(ptr)->operator()(args...);
},
[](void* ptr) {
std::destroy_at(static_cast<Callable*>(ptr));
},
[](void* storage, const void* ptr) {
new (storage) Callable {
*static_cast<const Callable*>(ptr)};
},
[](void* storage, void* ptr) {
new (storage) Callable {
std::move(*static_cast<Callable*>(ptr))};
}
};
}; // namespace _detail
template<typename R>
class TaskWrapperP {
// This is just a base template class, so we can specialize it for a
// function signature. This one is not meant to be actually instantiated.
};
// Template specialization that matches with function signature.
template<typename R, typename ...Ts>
class TaskWrapperP<R(Ts...)> {
public:
TaskWrapperP() : vtable_{ nullptr }
{}
TaskWrapperP(const TaskWrapperP& other) {
other.vtable_->clone(&buf_, &other.buf_);
vtable_ = other.vtable_;
}
TaskWrapperP(TaskWrapperP&& other) noexcept {
other.vtable_->move_clone(&buf_, &other.buf_);
vtable_ = other.vtable_;
}
~TaskWrapperP() {
if (vtable_) {
vtable_->destroy(&buf_);
}
}
TaskWrapperP& operator=(const TaskWrapperP& other) {
if (vtable_) {
vtable_->destroy(&buf_);
}
if (other.vtable_) {
other.vtable_->clone(&buf_, &other.buf_);
}
vtable_ = other.vtable_;
return *this;
}
TaskWrapperP& operator=(TaskWrapperP&& other) noexcept {
if (vtable_) {
vtable_->destroy(&buf_);
}
if (other.vtable_) {
other.vtable_->move_clone(&buf_, &other.buf_);
}
vtable_ = other.vtable_;
return *this;
}
template<typename Callable>
TaskWrapperP(Callable c)
: vtable_{ &_detail::vtable_p_for<Callable, R, Ts...> }
{
static_assert(sizeof(Callable) < sizeof(buf_),
"Wrapper buffer is too small.");
new(&buf_) Callable{ std::move(c) };
}
R operator()(Ts ...args) {
if (!vtable_) {
throw std::runtime_error{"Calling unitialized function wrapper!"};
}
return vtable_->run(&buf_, args...);
}
private:
std::aligned_storage_t<64> buf_;
const _detail::vtable_p<R(Ts...)>* vtable_;
};