v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
stdfunctionreplacement.h
Go to the documentation of this file.
1 
2 // https://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction/
3 
4 #pragma once
5 #include <utility>
6 #include <type_traits>
7 #include <functional>
8 #include <exception>
9 #include <typeinfo>
10 #include <memory>
11 
12 #ifdef _MSC_VER
13 #define FUNC_NOEXCEPT
14 #define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR)
15 #define FUNC_CONSTEXPR const
16 #else
17 #define FUNC_NOEXCEPT noexcept
18 #define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR) noexcept(detail::is_inplace_allocated<FUNCTOR, ALLOCATOR>::value)
19 #define FUNC_CONSTEXPR constexpr
20 #endif
21 #ifdef __GNUC__
22 #pragma GCC diagnostic push
23 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
24 #endif
25 
26 #define FUNC_MOVE(value) static_cast<typename std::remove_reference<decltype(value)>::type &&>(value)
27 #define FUNC_FORWARD(type, value) static_cast<type &&>(value)
28 
29 namespace func
30 {
31 #ifndef FUNC_NO_EXCEPTIONS
32  struct bad_function_call : std::exception
33  {
34  const char * what() const FUNC_NOEXCEPT override
35  {
36  return "Bad function call";
37  }
38  };
39 #endif
40 
41  template<typename>
44  {
45  };
46 
47  template<typename>
48  class function;
49 
50  namespace detail
51  {
52  struct manager_storage_type;
53  struct function_manager;
55  {
56  protected:
57  size_t padding_first;
59  };
60 
61  struct empty_struct
62  {
63  };
64 
65 # ifndef FUNC_NO_EXCEPTIONS
66  template<typename Result, typename... Arguments>
67  Result empty_call(const functor_padding &, Arguments...)
68  {
69  throw bad_function_call();
70  }
71 # endif
72 
73  template<typename T, typename Allocator>
75  {
76  static const bool value
77  // so that it fits
78  = sizeof(T) <= sizeof(functor_padding)
79  // so that it will be aligned
80  && std::alignment_of<functor_padding>::value % std::alignment_of<T>::value == 0
81  // so that we can offer noexcept move
82  && std::is_nothrow_move_constructible<T>::value
83  // so that the user can override it
85  };
86 
87  template<typename T>
88  T to_functor(T && func)
89  {
90  return FUNC_FORWARD(T, func);
91  }
92  template<typename Result, typename Class, typename... Arguments>
93  auto to_functor(Result (Class::*func)(Arguments...)) -> decltype(std::mem_fn(func))
94  {
95  return std::mem_fn(func);
96  }
97  template<typename Result, typename Class, typename... Arguments>
98  auto to_functor(Result (Class::*func)(Arguments...) const) -> decltype(std::mem_fn(func))
99  {
100  return std::mem_fn(func);
101  }
102 
103  template<typename T>
105  {
106  typedef decltype(to_functor(std::declval<T>())) type;
107  };
108 
109  template<typename T>
110  bool is_null(const T &)
111  {
112  return false;
113  }
114  template<typename Result, typename... Arguments>
115  bool is_null(Result (* const & function_pointer)(Arguments...))
116  {
117  return function_pointer == nullptr;
118  }
119  template<typename Result, typename Class, typename... Arguments>
120  bool is_null(Result (Class::* const & function_pointer)(Arguments...))
121  {
122  return function_pointer == nullptr;
123  }
124  template<typename Result, typename Class, typename... Arguments>
125  bool is_null(Result (Class::* const & function_pointer)(Arguments...) const)
126  {
127  return function_pointer == nullptr;
128  }
129 
130  template<typename, typename>
132  {
133  static const bool value = false;
134  };
135 
136  template<typename Result, typename... Arguments>
137  struct is_valid_function_argument<function<Result (Arguments...)>, Result (Arguments...)>
138  {
139  static const bool value = false;
140  };
141 
142  template<typename T, typename Result, typename... Arguments>
143  struct is_valid_function_argument<T, Result (Arguments...)>
144  {
145 # ifdef _MSC_VER
146  // as of january 2013 visual studio doesn't support the SFINAE below
147  static const bool value = true;
148 # else
149  template<typename U>
150  static decltype(to_functor(std::declval<U>())(std::declval<Arguments>()...)) check(U *);
151  template<typename>
152  static empty_struct check(...);
153 
154  static const bool value = std::is_convertible<decltype(check<T>(nullptr)), Result>::value;
155 # endif
156  };
157 
159 
161  {
162  template<typename Allocator>
164  {
165  return reinterpret_cast<Allocator &>(manager);
166  }
167  template<typename Allocator>
169  {
170  return reinterpret_cast<const Allocator &>(manager);
171  }
172 
174  manager_type manager;
175  };
176 
177  template<typename T, typename Allocator, typename Enable = void>
179  {
180  template<typename Result, typename... Arguments>
181  static Result call(const functor_padding & storage, Arguments... arguments)
182  {
183  // do not call get_functor_ref because I want this function to be fast
184  // in debug when nothing gets inlined
185  return const_cast<T &>(reinterpret_cast<const T &>(storage))(FUNC_FORWARD(Arguments, arguments)...);
186  }
187 
188  static void store_functor(manager_storage_type & storage, T to_store)
189  {
190  new (&get_functor_ref(storage)) T(FUNC_FORWARD(T, to_store));
191  }
193  {
194  new (&get_functor_ref(lhs)) T(FUNC_MOVE(get_functor_ref(rhs)));
195  }
197  {
198  get_functor_ref(storage).~T();
199  }
201  {
202  return const_cast<T &>(reinterpret_cast<const T &>(storage.functor));
203  }
204  };
205  template<typename T, typename Allocator>
206  struct function_manager_inplace_specialization<T, Allocator, typename std::enable_if<!is_inplace_allocated<T, Allocator>::value>::type>
207  {
208  template<typename Result, typename... Arguments>
209  static Result call(const functor_padding & storage, Arguments... arguments)
210  {
211  // do not call get_functor_ptr_ref because I want this function to be fast
212  // in debug when nothing gets inlined
213  return (*reinterpret_cast<const typename std::allocator_traits<Allocator>::pointer &>(storage))(FUNC_FORWARD(Arguments, arguments)...);
214  }
215 
216  static void store_functor(manager_storage_type & self, T to_store)
217  {
218  Allocator & allocator = self.get_allocator<Allocator>();;
219  static_assert(sizeof(typename std::allocator_traits<Allocator>::pointer) <= sizeof(self.functor), "The allocator's pointer type is too big");
220  typename std::allocator_traits<Allocator>::pointer * ptr = new (&get_functor_ptr_ref(self)) typename std::allocator_traits<Allocator>::pointer(std::allocator_traits<Allocator>::allocate(allocator, 1));
221  std::allocator_traits<Allocator>::construct(allocator, *ptr, FUNC_FORWARD(T, to_store));
222  }
224  {
225  static_assert(std::is_nothrow_move_constructible<typename std::allocator_traits<Allocator>::pointer>::value, "we can't offer a noexcept swap if the pointer type is not nothrow move constructible");
226  new (&get_functor_ptr_ref(lhs)) typename std::allocator_traits<Allocator>::pointer(FUNC_MOVE(get_functor_ptr_ref(rhs)));
227  // this next assignment makes the destroy function easier
228  get_functor_ptr_ref(rhs) = nullptr;
229  }
230  static void destroy_functor(Allocator & allocator, manager_storage_type & storage) FUNC_NOEXCEPT
231  {
232  typename std::allocator_traits<Allocator>::pointer & pointer = get_functor_ptr_ref(storage);
233  if (!pointer) return;
234  std::allocator_traits<Allocator>::destroy(allocator, pointer);
235  std::allocator_traits<Allocator>::deallocate(allocator, pointer, 1);
236  }
238  {
239  return *get_functor_ptr_ref(storage);
240  }
241  static typename std::allocator_traits<Allocator>::pointer & get_functor_ptr_ref(manager_storage_type & storage) FUNC_NOEXCEPT
242  {
243  return reinterpret_cast<typename std::allocator_traits<Allocator>::pointer &>(storage.functor);
244  }
245  static const typename std::allocator_traits<Allocator>::pointer & get_functor_ptr_ref(const manager_storage_type & storage) FUNC_NOEXCEPT
246  {
247  return reinterpret_cast<const typename std::allocator_traits<Allocator>::pointer &>(storage.functor);
248  }
249  };
250 
251  template<typename T, typename Allocator>
252  static const function_manager & get_default_manager();
253 
254  template<typename T, typename Allocator>
255  static void create_manager(manager_storage_type & storage, Allocator && allocator)
256  {
257  new (&storage.get_allocator<Allocator>()) Allocator(FUNC_MOVE(allocator));
258  storage.manager = &get_default_manager<T, Allocator>();
259  }
260 
261  // this struct acts as a vtable. it is an optimization to prevent
262  // code-bloat from rtti. see the documentation of boost::function
264  {
265  template<typename T, typename Allocator>
267  {
268 # ifdef _MSC_VER
269  function_manager result =
270 # else
271  return function_manager
272 # endif
273  {
274  &templated_call_move_and_destroy<T, Allocator>,
275  &templated_call_copy<T, Allocator>,
276  &templated_call_copy_functor_only<T, Allocator>,
277  &templated_call_destroy<T, Allocator>,
278 # ifndef FUNC_NO_RTTI
279  &templated_call_type_id<T, Allocator>,
280  &templated_call_target<T, Allocator>
281  # endif
282  };
283 # ifdef _MSC_VER
284  return result;
285 # endif
286  }
287 
288  void (* const call_move_and_destroy)(manager_storage_type & lhs, manager_storage_type && rhs);
289  void (* const call_copy)(manager_storage_type & lhs, const manager_storage_type & rhs);
290  void (* const call_copy_functor_only)(manager_storage_type & lhs, const manager_storage_type & rhs);
291  void (* const call_destroy)(manager_storage_type & manager);
292 # ifndef FUNC_NO_RTTI
293  const std::type_info & (* const call_type_id)();
294  void * (* const call_target)(const manager_storage_type & manager, const std::type_info & type);
295 # endif
296 
297  template<typename T, typename Allocator>
299  {
301  specialization::move_functor(lhs, FUNC_MOVE(rhs));
302  specialization::destroy_functor(rhs.get_allocator<Allocator>(), rhs);
303  create_manager<T, Allocator>(lhs, FUNC_MOVE(rhs.get_allocator<Allocator>()));
304  rhs.get_allocator<Allocator>().~Allocator();
305  }
306  template<typename T, typename Allocator>
308  {
310  create_manager<T, Allocator>(lhs, Allocator(rhs.get_allocator<Allocator>()));
311  specialization::store_functor(lhs, specialization::get_functor_ref(rhs));
312  }
313  template<typename T, typename Allocator>
315  {
317  specialization::destroy_functor(self.get_allocator<Allocator>(), self);
318  self.get_allocator<Allocator>().~Allocator();
319  }
320  template<typename T, typename Allocator>
322  {
324  specialization::store_functor(lhs, specialization::get_functor_ref(rhs));
325  }
326 # ifndef FUNC_NO_RTTI
327  template<typename T, typename>
328  static const std::type_info & templated_call_type_id()
329  {
330  return typeid(T);
331  }
332  template<typename T, typename Allocator>
333  static void * templated_call_target(const manager_storage_type & self, const std::type_info & type)
334  {
336  if (type == typeid(T))
337  return &specialization::get_functor_ref(self);
338  else
339  return nullptr;
340  }
341 # endif
342  };
343  template<typename T, typename Allocator>
344  inline static const function_manager & get_default_manager()
345  {
346  static FUNC_CONSTEXPR function_manager default_manager = function_manager::create_default_manager<T, Allocator>();
347  return default_manager;
348  }
349 
350  template<typename Result, typename...>
351  struct typedeffer
352  {
353  typedef Result result_type;
354  };
355  template<typename Result, typename Argument>
357  {
358  typedef Result result_type;
359  typedef Argument argument_type;
360  };
361  template<typename Result, typename First_Argument, typename Second_Argument>
363  {
364  typedef Result result_type;
365  typedef First_Argument first_argument_type;
366  typedef Second_Argument second_argument_type;
367  };
368  }
369 
370  template<typename Result, typename... Arguments>
371  class function<Result (Arguments...)>
372  : public detail::typedeffer<Result, Arguments...>
373  {
374  public:
375  function() FUNC_NOEXCEPT
376  {
377  initialize_empty();
378  }
379  function(std::nullptr_t) FUNC_NOEXCEPT
380  {
381  initialize_empty();
382  }
383  function(function && other) FUNC_NOEXCEPT
384  {
385  initialize_empty();
386  swap(other);
387  }
388  function(const function & other)
389  : call(other.call)
390  {
391  other.manager_storage.manager->call_copy(manager_storage, other.manager_storage);
392  }
393  template<typename T>
394  function(T functor,
395  typename std::enable_if<detail::is_valid_function_argument<T, Result (Arguments...)>::value, detail::empty_struct>::type = detail::empty_struct()) FUNC_TEMPLATE_NOEXCEPT(T, std::allocator<typename detail::functor_type<T>::type>)
396  {
397  if (detail::is_null(functor))
398  {
399  initialize_empty();
400  }
401  else
402  {
403  typedef typename detail::functor_type<T>::type functor_type;
404  initialize(detail::to_functor(FUNC_FORWARD(T, functor)), std::allocator<functor_type>());
405  }
406  }
407  template<typename Allocator>
408  function(std::allocator_arg_t, const Allocator &)
409  {
410  // ignore the allocator because I don't allocate
411  initialize_empty();
412  }
413  template<typename Allocator>
414  function(std::allocator_arg_t, const Allocator &, std::nullptr_t)
415  {
416  // ignore the allocator because I don't allocate
417  initialize_empty();
418  }
419  template<typename Allocator, typename T>
420  function(std::allocator_arg_t, const Allocator & allocator, T functor,
421  typename std::enable_if<detail::is_valid_function_argument<T, Result (Arguments...)>::value, detail::empty_struct>::type = detail::empty_struct())
422  FUNC_TEMPLATE_NOEXCEPT(T, Allocator)
423  {
424  if (detail::is_null(functor))
425  {
426  initialize_empty();
427  }
428  else
429  {
430  initialize(detail::to_functor(FUNC_FORWARD(T, functor)), Allocator(allocator));
431  }
432  }
433  template<typename Allocator>
434  function(std::allocator_arg_t, const Allocator & allocator, const function & other)
435  : call(other.call)
436  {
437  typedef typename std::allocator_traits<Allocator>::template rebind_alloc<function> MyAllocator;
438 
439  // first try to see if the allocator matches the target type
440  detail::manager_type manager_for_allocator = &detail::get_default_manager<typename std::allocator_traits<Allocator>::value_type, Allocator>();
441  if (other.manager_storage.manager == manager_for_allocator)
442  {
443  detail::create_manager<typename std::allocator_traits<Allocator>::value_type, Allocator>(manager_storage, Allocator(allocator));
444  manager_for_allocator->call_copy_functor_only(manager_storage, other.manager_storage);
445  }
446  // if it does not, try to see if the target contains my type. this
447  // breaks the recursion of the last case. otherwise repeated copies
448  // would allocate more and more memory
449  else
450  {
451  detail::manager_type manager_for_function = &detail::get_default_manager<function, MyAllocator>();
452  if (other.manager_storage.manager == manager_for_function)
453  {
454  detail::create_manager<function, MyAllocator>(manager_storage, MyAllocator(allocator));
455  manager_for_function->call_copy_functor_only(manager_storage, other.manager_storage);
456  }
457  else
458  {
459  // else store the other function as my target
460  initialize(other, MyAllocator(allocator));
461  }
462  }
463  }
464  template<typename Allocator>
465  function(std::allocator_arg_t, const Allocator &, function && other) FUNC_NOEXCEPT
466  {
467  // ignore the allocator because I don't allocate
468  initialize_empty();
469  swap(other);
470  }
471 
472  function & operator=(function other) FUNC_NOEXCEPT
473  {
474  swap(other);
475  return *this;
476  }
478  {
479  manager_storage.manager->call_destroy(manager_storage);
480  }
481 
482  Result operator()(Arguments... arguments) const
483  {
484  return call(manager_storage.functor, FUNC_FORWARD(Arguments, arguments)...);
485  }
486 
487  template<typename T, typename Allocator>
488  void assign(T && functor, const Allocator & allocator) FUNC_TEMPLATE_NOEXCEPT(T, Allocator)
489  {
490  function(std::allocator_arg, allocator, functor).swap(*this);
491  }
492 
493  void swap(function & other) FUNC_NOEXCEPT
494  {
495  detail::manager_storage_type temp_storage;
496  other.manager_storage.manager->call_move_and_destroy(temp_storage, FUNC_MOVE(other.manager_storage));
497  manager_storage.manager->call_move_and_destroy(other.manager_storage, FUNC_MOVE(manager_storage));
498  temp_storage.manager->call_move_and_destroy(manager_storage, FUNC_MOVE(temp_storage));
499 
500  std::swap(call, other.call);
501  }
502 
503 
504 # ifndef FUNC_NO_RTTI
505  const std::type_info & target_type() const FUNC_NOEXCEPT
506  {
507  return manager_storage.manager->call_type_id();
508  }
509  template<typename T>
510  T * target() FUNC_NOEXCEPT
511  {
512  return static_cast<T *>(manager_storage.manager->call_target(manager_storage, typeid(T)));
513  }
514  template<typename T>
515  const T * target() const FUNC_NOEXCEPT
516  {
517  return static_cast<const T *>(manager_storage.manager->call_target(manager_storage, typeid(T)));
518  }
519 # endif
520 
521  operator bool() const FUNC_NOEXCEPT
522  {
523 
524 # ifdef FUNC_NO_EXCEPTIONS
525  return call != nullptr;
526 # else
527  return call != &detail::empty_call<Result, Arguments...>;
528 # endif
529  }
530 
531  private:
532  detail::manager_storage_type manager_storage;
533  Result (*call)(const detail::functor_padding &, Arguments...);
534 
535  template<typename T, typename Allocator>
536  void initialize(T functor, Allocator && allocator)
537  {
539  detail::create_manager<T, Allocator>(manager_storage, FUNC_FORWARD(Allocator, allocator));
541  }
542 
543  typedef Result(*Empty_Function_Type)(Arguments...);
545  {
546  typedef std::allocator<Empty_Function_Type> Allocator;
547  static_assert(detail::is_inplace_allocated<Empty_Function_Type, Allocator>::value, "The empty function should benefit from small functor optimization");
548 
549  detail::create_manager<Empty_Function_Type, Allocator>(manager_storage, Allocator());
551 # ifdef FUNC_NO_EXCEPTIONS
552  call = nullptr;
553 # else
554  call = &detail::empty_call<Result, Arguments...>;
555 # endif
556  }
557  };
558 
559  template<typename T>
560  bool operator==(std::nullptr_t, const function<T> & rhs) FUNC_NOEXCEPT
561  {
562  return !rhs;
563  }
564  template<typename T>
565  bool operator==(const function<T> & lhs, std::nullptr_t) FUNC_NOEXCEPT
566  {
567  return !lhs;
568  }
569  template<typename T>
570  bool operator!=(std::nullptr_t, const function<T> & rhs) FUNC_NOEXCEPT
571  {
572  return rhs;
573  }
574  template<typename T>
575  bool operator!=(const function<T> & lhs, std::nullptr_t) FUNC_NOEXCEPT
576  {
577  return lhs;
578  }
579 
580  template<typename T>
581  void swap(function<T> & lhs, function<T> & rhs)
582  {
583  lhs.swap(rhs);
584  }
585 
586 } // end namespace func
587 
588 namespace std
589 {
590  template<typename Result, typename... Arguments, typename Allocator>
591  struct uses_allocator<func::function<Result (Arguments...)>, Allocator>
593  {
594  };
595 }
596 
597 #ifdef __GNUC__
598 #pragma GCC diagnostic pop
599 #endif
600 #undef FUNC_NOEXCEPT
601 #undef FUNC_TEMPLATE_NOEXCEPT
602 #undef FUNC_FORWARD
603 #undef FUNC_MOVE
604 #undef FUNC_CONSTEXPR
static void destroy_functor(Allocator &, manager_storage_type &storage) FUNC_NOEXCEPT
static void create_manager(manager_storage_type &storage, Allocator &&allocator)
static void templated_call_copy_functor_only(manager_storage_type &lhs, const manager_storage_type &rhs)
static const function_manager & get_default_manager()
T to_functor(T &&func)
static const std::allocator_traits< Allocator >::pointer & get_functor_ptr_ref(const manager_storage_type &storage) FUNC_NOEXCEPT
const function_manager * manager_type
bool operator!=(std::nullptr_t, const function< T > &rhs) FUNC_NOEXCEPT
Allocator & get_allocator() FUNC_NOEXCEPT
static const std::type_info & templated_call_type_id()
STL namespace.
#define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR)
static void * templated_call_target(const manager_storage_type &self, const std::type_info &type)
bool_constant< true > true_type
Definition: gtest-port.h:2210
static void store_functor(manager_storage_type &storage, T to_store)
#define FUNC_CONSTEXPR
bool_constant< false > false_type
Definition: gtest-port.h:2209
void *(*const call_target)(const manager_storage_type &manager, const std::type_info &type)
static FUNC_CONSTEXPR function_manager create_default_manager()
void(*const call_move_and_destroy)(manager_storage_type &lhs, manager_storage_type &&rhs)
const Allocator & get_allocator() const FUNC_NOEXCEPT
static void templated_call_move_and_destroy(manager_storage_type &lhs, manager_storage_type &&rhs)
#define FUNC_MOVE(value)
static void templated_call_copy(manager_storage_type &lhs, const manager_storage_type &rhs)
static T & get_functor_ref(const manager_storage_type &storage) FUNC_NOEXCEPT
static Result call(const functor_padding &storage, Arguments...arguments)
const std::type_info &(*const call_type_id)()
bool is_null(const T &)
static void move_functor(manager_storage_type &lhs, manager_storage_type &&rhs) FUNC_NOEXCEPT
#define FUNC_FORWARD(type, value)
void(*const call_copy_functor_only)(manager_storage_type &lhs, const manager_storage_type &rhs)
Result empty_call(const functor_padding &, Arguments...)
bool operator==(std::nullptr_t, const function< T > &rhs) FUNC_NOEXCEPT
void initialize(T functor, Allocator &&allocator)
auto to_functor(Result(Class::*func)(Arguments...)) -> decltype(std::mem_fn(func))
static void templated_call_destroy(manager_storage_type &self)
const char * what() const FUNC_NOEXCEPT override
#define FUNC_NOEXCEPT
void swap(function< T > &lhs, function< T > &rhs)
bool is_null(Result(Class::*const &function_pointer)(Arguments...))