Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge pull request #202 from Takishipp/clear_fct
[simgrid.git] / include / xbt / functional.hpp
1 /* Copyright (c) 2015-2016. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #ifndef XBT_FUNCTIONAL_HPP
8 #define XBT_FUNCTIONAL_HPP
9
10 #include <cstddef>
11 #include <cstdlib>
12 #include <cstring>
13
14 #include <array>
15 #include <exception>
16 #include <functional>
17 #include <memory>
18 #include <string>
19 #include <tuple>
20 #include <type_traits>
21 #include <utility>
22 #include <vector>
23
24 #include "xbt/sysdep.h"
25 #include "xbt/utility.hpp"
26
27 namespace simgrid {
28 namespace xbt {
29
30 template<class F>
31 class MainFunction {
32 private:
33   F code_;
34   std::shared_ptr<const std::vector<std::string>> args_;
35 public:
36   MainFunction(F code, std::vector<std::string> args) :
37     code_(std::move(code)),
38     args_(std::make_shared<const std::vector<std::string>>(std::move(args)))
39   {}
40   void operator()() const
41   {
42     char noarg[] = {'\0'};
43     const int argc = args_->size();
44     std::vector<std::string> args = *args_;
45     if (not args.empty()) {
46       std::unique_ptr<char* []> argv(new char*[argc + 1]);
47       for (int i = 0; i != argc; ++i)
48         argv[i]  = args[i].empty() ? noarg : &args[i].front();
49       argv[argc] = nullptr;
50       code_(argc, argv.get());
51     } else
52       code_(argc, nullptr);
53   }
54 };
55
56 template<class F> inline
57 std::function<void()> wrapMain(F code, std::vector<std::string> args)
58 {
59   return MainFunction<F>(std::move(code), std::move(args));
60 }
61
62 template<class F> inline
63 std::function<void()> wrapMain(F code, int argc, const char*const argv[])
64 {
65   std::vector<std::string> args(argv, argv + argc);
66   return MainFunction<F>(std::move(code), std::move(args));
67 }
68
69 namespace bits {
70 template <class F, class Tuple, std::size_t... I>
71 constexpr auto apply(F&& f, Tuple&& t, simgrid::xbt::index_sequence<I...>)
72   -> decltype(std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...))
73 {
74   return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
75 }
76 }
77
78 /** Call a functional object with the values in the given tuple (from C++17)
79  *
80  *  @code{.cpp}
81  *  int foo(int a, bool b);
82  *
83  *  auto args = std::make_tuple(1, false);
84  *  int res = apply(foo, args);
85  *  @endcode
86  **/
87 template <class F, class Tuple>
88 constexpr auto apply(F&& f, Tuple&& t)
89   -> decltype(simgrid::xbt::bits::apply(
90     std::forward<F>(f),
91     std::forward<Tuple>(t),
92     simgrid::xbt::make_index_sequence<
93       std::tuple_size<typename std::decay<Tuple>::type>::value
94     >()))
95 {
96   return simgrid::xbt::bits::apply(
97     std::forward<F>(f),
98     std::forward<Tuple>(t),
99     simgrid::xbt::make_index_sequence<
100       std::tuple_size<typename std::decay<Tuple>::type>::value
101     >());
102 }
103
104 template<class T> class Task;
105
106 /** Type-erased run-once task
107  *
108  *  * Like std::function but callable only once.
109  *    However, it works with move-only types.
110  *
111  *  * Like std::packaged_task<> but without the shared state.
112  */
113 template<class R, class... Args>
114 class Task<R(Args...)> {
115 private:
116
117   // Placeholder for some class type:
118   struct whatever {};
119
120   // Union used for storage:
121 #if 0
122   typedef typename std::aligned_union<0,
123     void*,
124     std::pair<void(*)(),void*>,
125     std::pair<void(whatever::*)(), whatever*>
126   >::type TaskUnion;
127 #else
128   union TaskUnion {
129     void* ptr;
130     std::pair<void(*)(),void*> funcptr;
131     std::pair<void(whatever::*)(), whatever*> memberptr;
132     char any1[sizeof(std::pair<void(*)(),void*>)];
133     char any2[sizeof(std::pair<void(whatever::*)(), whatever*>)];
134     TaskUnion() {}
135     ~TaskUnion() {}
136   };
137 #endif
138
139   // Is F suitable for small buffer optimization?
140   template<class F>
141   static constexpr bool canSBO()
142   {
143     return sizeof(F) <= sizeof(TaskUnion) &&
144       alignof(F) <= alignof(TaskUnion);
145   }
146
147   static_assert(canSBO<std::reference_wrapper<whatever>>(),
148     "SBO not working for reference_wrapper");
149
150   // Call (and possibly destroy) the function:
151   typedef R (*call_function)(TaskUnion&, Args...);
152   // Destroy the function (of needed):
153   typedef void (*destroy_function)(TaskUnion&);
154   // Move the function (otherwise memcpy):
155   typedef void (*move_function)(TaskUnion& dest, TaskUnion& src);
156
157   // Vtable of functions for manipulating whatever is in the TaskUnion:
158   struct TaskVtable {
159     call_function call;
160     destroy_function destroy;
161     move_function move;
162   };
163
164   TaskUnion buffer_;
165   const TaskVtable* vtable_ = nullptr;
166
167   void clear()
168   {
169     if (vtable_ && vtable_->destroy)
170       vtable_->destroy(buffer_);
171   }
172
173 public:
174
175   Task() {}
176   Task(std::nullptr_t) {}
177   ~Task()
178   {
179     this->clear();
180   }
181
182   Task(Task const&) = delete;
183
184   Task(Task&& that)
185   {
186     if (that.vtable_ && that.vtable_->move)
187       that.vtable_->move(buffer_, that.buffer_);
188     else
189       std::memcpy(&buffer_, &that.buffer_, sizeof(buffer_));
190     vtable_ = that.vtable_;
191     that.vtable_ = nullptr;
192   }
193   Task& operator=(Task that)
194   {
195     this->clear();
196     if (that.vtable_ && that.vtable_->move)
197       that.vtable_->move(buffer_, that.buffer_);
198     else
199       std::memcpy(&buffer_, &that.buffer_, sizeof(buffer_));
200     vtable_ = that.vtable_;
201     that.vtable_ = nullptr;
202     return *this;
203   }
204
205 private:
206
207   template<class F>
208   typename std::enable_if<canSBO<F>()>::type
209   init(F code)
210   {
211     const static TaskVtable vtable {
212       // Call:
213       [](TaskUnion& buffer, Args... args) -> R {
214         F* src = reinterpret_cast<F*>(&buffer);
215         F code = std::move(*src);
216         src->~F();
217         code(std::forward<Args>(args)...);
218       },
219       // Destroy:
220       std::is_trivially_destructible<F>::value ?
221       static_cast<destroy_function>(nullptr) :
222       [](TaskUnion& buffer) {
223         F* code = reinterpret_cast<F*>(&buffer);
224         code->~F();
225       },
226       // Move:
227       [](TaskUnion& dst, TaskUnion& src) {
228         F* src_code = reinterpret_cast<F*>(&src);
229         F* dst_code = reinterpret_cast<F*>(&dst);
230         new(dst_code) F(std::move(*src_code));
231         src_code->~F();
232       }
233     };
234     new(&buffer_) F(std::move(code));
235     vtable_ = &vtable;
236   }
237
238   template <class F> typename std::enable_if<not canSBO<F>()>::type init(F code)
239   {
240     const static TaskVtable vtable {
241       // Call:
242       [](TaskUnion& buffer, Args... args) -> R {
243         // Delete F when we go out of scope:
244         std::unique_ptr<F> code(*reinterpret_cast<F**>(&buffer));
245         return (*code)(std::forward<Args>(args)...);
246       },
247       // Destroy:
248       [](TaskUnion& buffer) {
249         F* code = *reinterpret_cast<F**>(&buffer);
250         delete code;
251       },
252       // Move:
253       nullptr
254     };
255     *reinterpret_cast<F**>(&buffer_) = new F(std::move(code));
256     vtable_ = &vtable;
257   }
258
259 public:
260
261   template<class F>
262   Task(F code)
263   {
264     this->init(std::move(code));
265   }
266
267   operator bool() const { return vtable_ != nullptr; }
268   bool operator!() const { return vtable_ == nullptr; }
269
270   R operator()(Args... args)
271   {
272     if (vtable_ == nullptr)
273       throw std::bad_function_call();
274     const TaskVtable* vtable = vtable_;
275     vtable_ = nullptr;
276     return vtable->call(buffer_, std::forward<Args>(args)...);
277   }
278 };
279
280 template<class F, class... Args>
281 class TaskImpl {
282 private:
283   F code_;
284   std::tuple<Args...> args_;
285   typedef decltype(simgrid::xbt::apply(std::move(code_), std::move(args_))) result_type;
286 public:
287   TaskImpl(F code, std::tuple<Args...> args) :
288     code_(std::move(code)),
289     args_(std::move(args))
290   {}
291   result_type operator()()
292   {
293     return simgrid::xbt::apply(std::move(code_), std::move(args_));
294   }
295 };
296
297 template<class F, class... Args>
298 auto makeTask(F code, Args... args)
299 -> Task< decltype(code(std::move(args)...))() >
300 {
301   TaskImpl<F, Args...> task(std::move(code), std::make_tuple(std::move(args)...));
302   return std::move(task);
303 }
304
305 }
306 }
307
308 #endif