SimulationCraft
SimulationCraft is a tool to explore combat mechanics in the popular MMO RPG World of Warcraft (tm).
generic.hpp
1 // ==========================================================================
2 // Dedmonwakeen's Raid DPS/TPS Simulator.
3 // Send questions to [email protected]
4 // ==========================================================================
5 
6 // ==========================================================================
7 // Collection of generic programming code
8 // ==========================================================================
9 
10 #ifndef SC_GENERIC_HPP
11 #define SC_GENERIC_HPP
12 
13 #include "config.hpp"
14 
15 #include <algorithm>
16 #include <cassert>
17 #include <cmath>
18 #include <functional>
19 #include <type_traits>
20 
21 // iterable enumeration templates ===========================================
22 
23 /*
24  * Enumeration types in C++ implicitly convert to int but not from int (e.g.,
25  * "for ( attribute_e i = ATTR_STRENGTH; i < ATTR_MAX; ++i )" won't compile).
26  * This is why so much of the code uses int when it really means an enum type.
27  * Providing the kind of operations we want to use for enums lets us tighten up
28  * our use of the type system and avoid accidentally passing some other thing
29  * that converts to int when we really mean an enumeration type.
30  *
31  * The template functions tell the compiler it can perform prefix and postfix
32  * operators ++ and -- on any type by converting it to int and back. The magic
33  * with std::enable_if<> restricts those operations to types T for which the
34  * type trait "is_iterable_enum<T>" is true. This trait gives us a way to
35  * selectively control the functionality for a specific type T by specializing
36  * is_iterable_enum<T> as std::true_type or std::false_type.
37  */
38 
39 // All enumerations are iterable by default.
40 template <typename T>
41 struct is_iterable_enum : public std::is_enum<T>
42 {
43 };
44 
45 template <typename T>
46 inline std::enable_if_t<is_iterable_enum<T>::value, T&> operator--( T& s )
47 {
48  return s = static_cast<T>( static_cast<std::underlying_type_t<T>>(s) - 1 );
49 }
50 
51 template <typename T>
52 inline std::enable_if_t<is_iterable_enum<T>::value, T> operator--( T& s, int )
53 {
54  T tmp = s;
55  --s;
56  return tmp;
57 }
58 
59 template <typename T>
60 inline std::enable_if_t<is_iterable_enum<T>::value, T&> operator++( T& s )
61 {
62  return s = static_cast<T>( static_cast<std::underlying_type_t<T>>(s) + 1 );
63 }
64 
65 template <typename T>
66 inline std::enable_if_t<is_iterable_enum<T>::value, T> operator++( T& s, int )
67 {
68  T tmp = s;
69  ++s;
70  return tmp;
71 }
72 
73 // Generic programming tools ================================================
74 
82 {
83 protected:
84  noncopyable() = default;
85  noncopyable( noncopyable&& ) = default;
86  noncopyable& operator=( noncopyable&& ) = default;
87 
88  noncopyable( const noncopyable& ) = delete; // NOLINT(modernize-use-equals-delete)
89  noncopyable& operator=( const noncopyable& ) = delete; // NOLINT(modernize-use-equals-delete)
90 };
91 
98 class nonmoveable : private noncopyable
99 {
100 protected:
101  nonmoveable() = default;
102  nonmoveable( nonmoveable&& ) = delete; // NOLINT(modernize-use-equals-delete)
103  nonmoveable& operator=( nonmoveable&& ) = delete; // NOLINT(modernize-use-equals-delete)
104 };
105 
106 // Adapted from (read "stolen") boost::checked_deleter
108 {
109  template <typename T>
110  void operator()( T* t ) const
111  {
112  using force_T_to_be_complete = int[ sizeof( T ) ? 1 : -1 ]; // NOLINT(modernize-avoid-c-arrays)
113  (void)sizeof( force_T_to_be_complete );
114  delete t;
115  }
116 };
117 
118 // Generic algorithms =======================================================
119 
120 template <typename I, typename D>
121 void dispose( I first, I last, D disposer )
122 {
123  while ( first != last )
124  disposer( *first++ );
125 }
126 
127 template <typename I>
128 inline void dispose( I first, I last )
129 {
130  dispose( first, last, delete_disposer_t() );
131 }
132 
133 // Machinery for range-based generic algorithms =============================
134 
135 namespace range
136 { // ========================================================
137 namespace detail {
138 
139 struct begin_ {
140  template <typename R>
141  auto operator()( R&& r ) const {
142  using std::begin;
143  return begin( std::forward<R>( r ) );
144  }
145  template <typename T>
146  auto operator()( const std::pair<T, T>& p ) const {
147  return p.first;
148  }
149 };
150 
151 struct end_ {
152  template <typename R>
153  auto operator()( R&& r ) const {
154  using std::end;
155  return end( std::forward<R>( r ) );
156  }
157  template <typename T>
158  auto operator()( const std::pair<T, T>& p ) const {
159  return p.second;
160  }
161 };
162 
163 } // namespace detail
164 
165 template <typename T>
166 auto begin( T&& t )
167 {
168  return detail::begin_{}( std::forward<T>( t ) );
169 }
170 
171 template <typename T>
172 auto cbegin( const T& t )
173 {
174  return range::begin( t );
175 }
176 
177 template <typename T>
178 auto end( T&& t )
179 {
180  return detail::end_{}( std::forward<T>( t ) );
181 }
182 
183 template <typename T>
184 auto cend( const T& t )
185 {
186  return range::end( t );
187 }
188 
189 template <typename R>
190 using iterator_t = decltype(range::begin(std::declval<R&>()));
191 
192 template <typename R>
193 using value_type_t = typename std::iterator_traits<iterator_t<R>>::value_type;
194 
195 template <typename R>
196 using reference_type_t = typename std::iterator_traits<iterator_t<R>>::reference;
197 
198 // Default projection for projection-enabled algorithms =====================
199 
200 struct identity {
201  template <typename T>
202  constexpr T&& operator()(T&& value) const noexcept {
203  return std::forward<T>(value);
204  }
205  using is_transparent = std::true_type;
206 };
207 
208 // Range-based generic algorithms ===========================================
209 
210 template <typename Range, typename Out>
211 inline Out copy( const Range& r, Out o )
212 {
213  return std::copy( range::begin( r ), range::end( r ), o );
214 }
215 
216 template <typename Range, typename Out, typename UnaryPredicate>
217 inline Out copy_if( const Range& r, Out o, UnaryPredicate pred )
218 {
219  return std::copy_if( range::begin( r ), range::end( r ), o, pred );
220 }
221 
222 template <typename Range, typename D>
223 inline Range& dispose( Range& r, D disposer )
224 {
225  dispose( range::begin( r ), range::end( r ), disposer );
226  return r;
227 }
228 
229 template <typename Range>
230 inline Range& dispose( Range& r )
231 {
232  return dispose( r, delete_disposer_t() );
233 }
234 
235 template <typename Range>
236 inline Range& fill( Range& r, value_type_t<Range> const& t )
237 {
238  std::fill( range::begin( r ), range::end( r ), t );
239  return r;
240 }
241 
242 template <typename Range, typename T, typename Proj = identity,
243  typename U = decltype( std::declval<T&&>() + std::declval<std::invoke_result_t<Proj, reference_type_t<Range>>>() )>
244 auto accumulate( Range&& r, T init, Proj proj = {} ) -> U
245 {
246  auto first = range::begin( r );
247  auto last = range::end( r );
248 
249  U accum = std::move( init );
250  for ( ; first != last; ++first )
251  accum = std::move( accum ) + std::invoke( proj, *first );
252  return accum;
253 }
254 
255 #if defined( SC_GCC )
256 // Workaround for GCC 4.6+ optimization ( -O3 ) issue with filling C-arrays
257 template <typename T, size_t N>
258 inline T ( &fill( T ( &r )[ N ], const T& t ) )[ N ]
259 {
260  for ( size_t i = 0; i < N; ++i )
261  {
262  r[ i ] = t;
263  }
264  return r;
265 }
266 #endif
267 
268 template <typename Range, typename T,
269  typename Enable = decltype( std::declval<reference_type_t<Range>>() == std::declval<const T&>() )>
270 inline iterator_t<Range> find( Range& r, T const& t )
271 {
272  return std::find( range::begin( r ), range::end( r ), t );
273 }
274 
275 template <typename Range, typename T, typename Proj,
276  typename Enable = decltype( std::declval<std::invoke_result_t<Proj, reference_type_t<Range>>>() == std::declval<const T&>() )>
277 inline iterator_t<Range> find( Range& r, T const& value, Proj proj )
278 {
279  const auto pred = [&value, &proj]( auto&& v ) {
280  return std::invoke( proj, std::forward<decltype(v)>( v ) ) == value;
281  };
282  return std::find_if( range::begin( r ), range::end( r ), pred );
283 }
284 
285 template <typename Range, typename UnaryPredicate>
286 inline iterator_t<Range> find_if( Range& r, UnaryPredicate p )
287 {
288  return std::find_if( range::begin( r ), range::end( r ), p );
289 }
290 
291 template <typename Range, typename UnaryPredicate>
292 inline bool any_of( Range&& r, UnaryPredicate p )
293 {
294  return std::any_of( range::begin( r ), range::end( r ), p );
295 }
296 
302 template <typename Range, typename Value, typename Proj = identity>
303 inline bool contains( Range&& r, const Value& v, Proj proj = Proj{} )
304 {
305  return range::find( r, v, proj ) != range::end( r );
306 }
307 
308 template <typename Range, typename F>
309 inline F for_each( Range&& r, F f )
310 {
311  std::for_each( range::begin( r ), range::end( r ),
312  [ &f ]( auto&& v ) { std::invoke( f, std::forward<decltype(v)>( v ) ); } );
313  return f;
314 }
315 
316 template <typename Range, typename Out, typename Predicate>
317 inline Out remove_copy_if( Range& r, Out o, Predicate p )
318 {
319  return std::remove_copy_if( range::begin( r ), range::end( r ), o, p );
320 }
321 
322 template <typename Range, typename Out, typename F>
323 Out transform( Range&& r, Out o, F op )
324 {
325  return std::transform( range::begin( r ), range::end( r ), o,
326  [ &op ]( auto&& v ) { return std::invoke( op, std::forward<decltype(v)>( v ) ); } );
327 }
328 
329 template <typename Range, typename Range2, typename Out, typename F>
330 Out transform( Range&& r, Range2&& r2, Out o, F op_ )
331 {
332  const auto op = [ &op_ ] ( auto&& l, auto&& r ) {
333  return std::invoke( op_, std::forward<decltype(l)>( l ), std::forward<decltype(r)>( r ) );
334  };
335  return std::transform( range::begin( r ), range::end( r ), range::begin( r2 ), o, op );
336 }
337 
338 template <typename Range, typename F>
339 inline iterator_t<Range> transform_self( Range& r, F f )
340 {
341  return std::transform( range::begin( r ), range::end( r ), range::begin( r ),
342  f );
343 }
344 
345 template <typename Range1, typename Range2, typename Out>
346 inline Out set_difference( const Range1& left, const Range2& right, Out o )
347 {
348  return std::set_difference( range::begin( left ), range::end( left ),
349  range::begin( right ), range::end( right ), o );
350 }
351 
352 template <typename Range1, typename Range2, typename Out, typename Compare>
353 inline Out set_difference( const Range1& left, const Range2& right, Out o,
354  Compare c )
355 {
356  return std::set_difference( range::begin( left ), range::end( left ),
357  range::begin( right ), range::end( right ), o,
358  c );
359 }
360 
361 template <typename Range1, typename Range2, typename Out>
362 inline Out set_intersection( const Range1& left, const Range2& right, Out o )
363 {
364  return std::set_intersection( range::begin( left ), range::end( left ),
365  range::begin( right ), range::end( right ), o );
366 }
367 
368 template <typename Range1, typename Range2, typename Out, typename Compare>
369 inline Out set_intersection( const Range1& left, const Range2& right, Out o,
370  Compare c )
371 {
372  return std::set_intersection( range::begin( left ), range::end( left ),
373  range::begin( right ), range::end( right ), o,
374  c );
375 }
376 
377 template <typename Range1, typename Range2, typename Out>
378 inline Out set_union( const Range1& left, const Range2& right, Out o )
379 {
380  return std::set_union( range::begin( left ), range::end( left ),
381  range::begin( right ), range::end( right ), o );
382 }
383 
384 template <typename Range1, typename Range2, typename Out, typename Compare>
385 inline Out set_union( const Range1& left, const Range2& right, Out o,
386  Compare c )
387 {
388  return std::set_union( range::begin( left ), range::end( left ),
389  range::begin( right ), range::end( right ), o, c );
390 }
391 
392 template <typename Range>
393 inline Range& sort( Range& r )
394 {
395  std::sort( range::begin( r ), range::end( r ) );
396  return r;
397 }
398 
399 template <typename Range, typename Comp>
400 inline Range& sort( Range& r, Comp c )
401 {
402  std::sort( range::begin( r ), range::end( r ), c );
403  return r;
404 }
405 
406 template <typename Range>
407 inline iterator_t<Range> unique( Range& r )
408 {
409  return std::unique( range::begin( r ), range::end( r ) );
410 }
411 
412 template <typename Range, typename Comp>
413 inline iterator_t<Range> unique( Range& r, Comp c )
414 {
415  return std::unique( range::begin( r ), range::end( r ), c );
416 }
417 
418 template <typename Range>
419 inline iterator_t<Range> max_element( Range& r )
420 {
421  return std::max_element( range::begin( r ), range::end( r ) );
422 }
423 
424 template <typename Range>
425 inline iterator_t<Range> min_element( Range& r )
426 {
427  return std::min_element( range::begin( r ), range::end( r ) );
428 }
429 
430 template <typename Range1, typename Range2>
431 inline void append( Range1& destination, Range2&& source )
432 {
433  destination.insert( destination.end(), source.begin(), source.end() );
434 }
435 
436 template <typename Range, typename Predicate>
437 inline auto count_if( Range&& r, Predicate p ) -> typename std::iterator_traits<decltype(range::begin( r ))>::difference_type
438 {
439  return std::count_if( range::begin( r ), range::end( r ), p );
440 }
441 
442 template <typename Range, typename T, typename Compare = std::less<>, typename Proj = identity>
443 iterator_t<Range> lower_bound( Range& r, const T& value, Compare comp = Compare{}, Proj proj = Proj{} )
444 {
445  const auto pred = [&value, &proj, &comp]( auto&& v ) {
446  return std::invoke( comp, std::invoke( proj, std::forward<decltype(v)>( v ) ), value );
447  };
448  return std::partition_point( range::begin( r ), range::end( r ), pred );
449 }
450 
451 template <typename Range, typename T, typename Compare = std::less<>, typename Proj = identity>
452 iterator_t<Range> upper_bound( Range& r, const T& value, Compare comp = Compare{}, Proj proj = Proj{} )
453 {
454  const auto pred = [&value, &proj, &comp]( auto&& v ) {
455  return !std::invoke( comp, value, std::invoke( proj, std::forward<decltype(v)>( v ) ) );
456  };
457  return std::partition_point( range::begin( r ), range::end( r ), pred );
458 }
459 
460 template <typename Range, typename T, typename Compare = std::less<>, typename Proj = identity>
461 std::pair<iterator_t<Range>, iterator_t<Range>>
462  equal_range( Range& r, const T& value, Compare comp = Compare{}, Proj proj = Proj{} )
463 {
464  auto b = lower_bound( r, value, comp, proj );
465  if ( b == range::end( r ) )
466  return { b, b };
467  return { b, upper_bound( r, value, comp, proj ) };
468 }
469 
470 template <typename Range, typename Pred, typename Proj = identity>
471 iterator_t<Range> partition( Range& r, Pred pred_, Proj proj = Proj{} )
472 {
473  auto pred = [&pred_, &proj]( auto&& v ) -> bool {
474  return std::invoke( pred_, std::invoke( proj, std::forward<decltype(v)>( v ) ) );
475  };
476  return std::partition( range::begin( r ), range::end( r ), pred );
477 }
478 
479 } // namespace range ========================================================
480 
481 // Adapter for container of owned pointers; automatically deletes the
482 // pointers on destruction.
483 template <typename Container>
484 class auto_dispose : public Container
485 {
486 private:
487  void dispose_()
488  {
489  range::dispose( *this );
490  }
491 
492 public:
493  ~auto_dispose()
494  {
495  dispose_();
496  }
497  void dispose()
498  {
499  dispose_();
500  Container::clear();
501  }
502 };
503 
508 template <typename To, typename From>
509 inline To debug_cast( From* ptr )
510 {
511 #ifdef NDEBUG
512  return static_cast<To>( ptr );
513 #else
514  To result = dynamic_cast<To>( ptr );
515  if ( ptr )
516  assert( result );
517  return result;
518 #endif
519 }
520 
526 template <typename To, typename From>
527 inline To as( From f )
528 {
529  To t = static_cast<To>( f );
530  // Casting between arithmetic types
531  static_assert( std::is_arithmetic<To>::value, "Output type is not arithmetic." );
532  static_assert( std::is_arithmetic<From>::value, "Input type is not arithmetic." );
533  // is "safe" if (a) it's reversible, and
534  assert( f == static_cast<From>( t ) );
535  // (b) both values have the same sign.
536  assert( ( From() < f ) == ( To() < t ) );
537  return t;
538 }
539 
540 template <typename T>
541 inline T clamp( T value, T low, T high )
542 {
543  assert( !( high < low ) );
544  return ( value < low ? low : ( high < value ? high : value ) );
545 }
546 
551 template <typename Sequence>
552 void erase_unordered( Sequence& s, typename Sequence::iterator pos )
553 {
554  assert( !s.empty() && pos != s.end() );
555  typename Sequence::iterator last = s.end();
556  --last;
557  if ( pos != last )
558  *pos = *last; // *pos = std::move( *last );
559  s.pop_back();
560 }
561 
562 // Experimental propagate_const class, which preserves constness for class
563 // member pointers, as if they were value/reference types.
564 // Source: http://en.cppreference.com/w/cpp/experimental/propagate_const ,
565 // simplified adaption by scamille@2017-03-15
566 template <class T>
568 {
569 public:
570  using element_type =
571  typename std::remove_reference<decltype( *std::declval<T&>() )>::type;
572 
573  propagate_const() = default;
574 
575  propagate_const( const propagate_const& p ) = delete;
576 
577  //propagate_const( propagate_const&& p ) = default;
578 
579  template <typename U>
580  propagate_const( U&& u ) : _M_t( std::forward<U>( u ) )
581  {
582  }
583 
584  explicit operator bool() const
585  {
586  return static_cast<bool>( _M_t );
587  }
588 
589  const element_type* operator->() const
590  {
591  return this->get();
592  }
593 
594  operator const element_type*() const
595  {
596  return this->get();
597  }
598 
599  const element_type& operator*() const
600  {
601  return *this->get();
602  }
603 
604  const element_type* get() const
605  {
606  return _M_t;
607  }
608 
609  element_type*& get_ref()
610  {
611  return _M_t;
612  }
613 
614  // [propagate_const.non_const_observers], non-const observers
615 
616  element_type* operator->()
617  {
618  return this->get();
619  }
620 
621  operator element_type*()
622  {
623  return this->get();
624  }
625 
626  element_type& operator*()
627  {
628  return *this->get();
629  }
630 
631  element_type* get()
632  {
633  return _M_t;
634  }
635 
636  template <typename U>
637  propagate_const& operator=( U&& u )
638  {
639  this->_M_t = std::forward<U>( u );
640  return *this;
641  }
642 
643 private:
644  T _M_t; // exposition only
645 };
646 
647 #endif // SC_GENERIC_HPP
Definition: generic.hpp:151
Definition: generic.hpp:484
Definition: generic.hpp:41
Definition: generic.hpp:135
Represents a JSON value. Use Value for UTF8 encoding and default allocator.
Definition: document.h:66
Definition: sc_data.cpp:17
Definition: generic.hpp:567
Definition: generic.hpp:139
Definition: generic.hpp:107
Definition: core.h:1208
helper class to make a class non-moveable
Definition: generic.hpp:98
helper class to make a class non-copyable
Definition: generic.hpp:81
Definition: generic.hpp:200