proxygen
Replaceable.h
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <initializer_list>
20 #include <new>
21 #include <type_traits>
22 #include <utility>
23 
24 #include <folly/Portability.h>
25 #include <folly/Traits.h>
26 #include <folly/Utility.h>
27 #include <folly/lang/Launder.h>
28 
123 namespace folly {
124 template <class T>
126 
127 namespace replaceable_detail {
128 /* Mixin templates to give `replaceable<T>` the following properties:
129  *
130  * 1. Trivial destructor if `T` has a trivial destructor; user-provided
131  * otherwise
132  * 2. Move constructor if `T` has a move constructor; deleted otherwise
133  * 3. Move assignment operator if `T` has a move constructor; deleted
134  * otherwise
135  * 4. Copy constructor if `T` has a copy constructor; deleted otherwise
136  * 5. Copy assignment operator if `T` has a copy constructor; deleted
137  * otherwise
138  *
139  * Has to be done in this way because we can't `enable_if` them away
140  */
141 template <
142  class T,
145 struct dtor_mixin;
146 
147 /* Destructible and trivially destructible */
148 template <class T>
149 struct dtor_mixin<T, true, true> {};
150 
151 /* Destructible and not trivially destructible */
152 template <class T>
153 struct dtor_mixin<T, true, false> {
154  dtor_mixin() = default;
155  dtor_mixin(dtor_mixin&&) = default;
156  dtor_mixin(dtor_mixin const&) = default;
157  dtor_mixin& operator=(dtor_mixin&&) = default;
158  dtor_mixin& operator=(dtor_mixin const&) = default;
159  ~dtor_mixin() noexcept(std::is_nothrow_destructible<T>::value) {
160  T* destruct_ptr = launder(reinterpret_cast<T*>(
161  reinterpret_cast<Replaceable<T>*>(this)->storage_));
162  destruct_ptr->~T();
163  }
164 };
165 
166 /* Not destructible */
167 template <class T, bool A>
168 struct dtor_mixin<T, false, A> {
169  dtor_mixin() = default;
170  dtor_mixin(dtor_mixin&&) = default;
171  dtor_mixin(dtor_mixin const&) = default;
172  dtor_mixin& operator=(dtor_mixin&&) = default;
173  dtor_mixin& operator=(dtor_mixin const&) = default;
174  ~dtor_mixin() = delete;
175 };
176 
177 template <
178  class T,
182 
183 /* Not default-constructible and not move-constructible */
184 template <class T>
185 struct default_and_move_ctor_mixin<T, false, false> {
186  default_and_move_ctor_mixin() = delete;
190  default;
192  default;
193 
194  protected:
195  inline explicit default_and_move_ctor_mixin(int) {}
196 };
197 
198 /* Default-constructible and move-constructible */
199 template <class T>
200 struct default_and_move_ctor_mixin<T, true, true> {
202  std::is_nothrow_constructible<T>::value) {
203  ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();
204  }
207  other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {
208  ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
209  T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
210  }
213  default;
214  inline default_and_move_ctor_mixin& operator=(
215  default_and_move_ctor_mixin const&) = default;
216 
217  protected:
218  inline explicit default_and_move_ctor_mixin(int) {}
219 };
220 
221 /* Default-constructible and not move-constructible */
222 template <class T>
223 struct default_and_move_ctor_mixin<T, true, false> {
225  std::is_nothrow_constructible<T>::value) {
226  ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();
227  }
231  default;
233  default;
234 
235  protected:
236  inline explicit default_and_move_ctor_mixin(int) {}
237 };
238 
239 /* Not default-constructible but is move-constructible */
240 template <class T>
241 struct default_and_move_ctor_mixin<T, false, true> {
242  default_and_move_ctor_mixin() = delete;
245  other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {
246  ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
247  T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
248  }
251  default;
253  default;
254 
255  protected:
256  inline explicit default_and_move_ctor_mixin(int) {}
257 };
258 
259 template <
260  class T,
264 
265 /* Not (destructible and move-constructible) */
266 template <class T>
267 struct move_assignment_mixin<T, false> {
268  move_assignment_mixin() = default;
271  move_assignment_mixin& operator=(move_assignment_mixin&&) = delete;
272  move_assignment_mixin& operator=(move_assignment_mixin const&) = default;
273 };
274 
275 /* Both destructible and move-constructible */
276 template <class T>
277 struct move_assignment_mixin<T, true> {
278  move_assignment_mixin() = default;
281  inline move_assignment_mixin&
283  std::is_nothrow_destructible<T>::value&&
284  std::is_nothrow_move_constructible<T>::value) {
285  T* destruct_ptr = launder(reinterpret_cast<T*>(
286  reinterpret_cast<Replaceable<T>*>(this)->storage_));
287  destruct_ptr->~T();
288  ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
289  T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
290  return *this;
291  }
292  move_assignment_mixin& operator=(move_assignment_mixin const&) = default;
293 };
294 
297 
298 /* Not copy-constructible */
299 template <class T>
300 struct copy_ctor_mixin<T, false> {
301  copy_ctor_mixin() = default;
302  copy_ctor_mixin(copy_ctor_mixin&&) = default;
303  copy_ctor_mixin(copy_ctor_mixin const&) = delete;
304  copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;
305  copy_ctor_mixin& operator=(copy_ctor_mixin const&) = delete;
306 };
307 
308 /* Copy-constructible */
309 template <class T>
310 struct copy_ctor_mixin<T, true> {
311  copy_ctor_mixin() = default;
314  ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
315  T(*reinterpret_cast<Replaceable<T> const&>(other));
316  }
317  copy_ctor_mixin(copy_ctor_mixin&&) = default;
318  copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;
319  copy_ctor_mixin& operator=(copy_ctor_mixin const&) = default;
320 };
321 
322 template <
323  class T,
327 
328 /* Not (destructible and copy-constructible) */
329 template <class T>
330 struct copy_assignment_mixin<T, false> {
331  copy_assignment_mixin() = default;
334  copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;
335  copy_assignment_mixin& operator=(copy_assignment_mixin const&) = delete;
336 };
337 
338 /* Both destructible and copy-constructible */
339 template <class T>
340 struct copy_assignment_mixin<T, true> {
341  copy_assignment_mixin() = default;
344  copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;
345  inline copy_assignment_mixin&
349  T* destruct_ptr = launder(reinterpret_cast<T*>(
350  reinterpret_cast<Replaceable<T>*>(this)->storage_));
351  destruct_ptr->~T();
352  ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
353  T(*reinterpret_cast<Replaceable<T> const&>(other));
354  return *this;
355  }
356 };
357 
358 template <typename T>
360  : bool_constant<
361  std::is_constructible<T, Replaceable<T>&>::value ||
362  std::is_constructible<T, Replaceable<T>&&>::value ||
363  std::is_constructible<T, const Replaceable<T>&>::value ||
364  std::is_constructible<T, const Replaceable<T>&&>::value> {};
365 
366 template <typename T>
368  : bool_constant<
369  std::is_convertible<Replaceable<T>&, T>::value ||
370  std::is_convertible<Replaceable<T>&&, T>::value ||
371  std::is_convertible<const Replaceable<T>&, T>::value ||
372  std::is_convertible<const Replaceable<T>&&, T>::value> {};
373 } // namespace replaceable_detail
374 
375 // Type trait template to statically test whether a type is a specialization of
376 // Replaceable
377 template <class T>
379 
380 template <class T>
382 
383 // Function to make a Replaceable with a type deduced from its input
384 template <class T>
386  return Replaceable<std::decay_t<T>>(std::forward<T>(t));
387 }
388 
389 template <class T, class... Args>
390 constexpr Replaceable<T> make_replaceable(Args&&... args) {
391  return Replaceable<T>(in_place, std::forward<Args>(args)...);
392 }
393 
394 template <class T, class U, class... Args>
396  std::initializer_list<U> il,
397  Args&&... args) {
398  return Replaceable<T>(in_place, il, std::forward<Args>(args)...);
399 }
400 
401 template <class T>
402 class alignas(T) Replaceable
403  : public replaceable_detail::dtor_mixin<T>,
409 
410  public:
411  using value_type = T;
412 
413  /* Rule-of-zero default- copy- and move- constructors. The ugly code to make
414  * these work are above, in namespace folly::replaceable_detail.
415  */
416  constexpr Replaceable() = default;
417  constexpr Replaceable(const Replaceable&) = default;
418  constexpr Replaceable(Replaceable&&) = default;
419 
420  /* Rule-of-zero copy- and move- assignment operators. The ugly code to make
421  * these work are above, in namespace folly::replaceable_detail.
422  *
423  * Note - these destruct the `T` and then in-place construct a new one based
424  * on what is in the other replaceable; they do not invoke the assignment
425  * operator of `T`.
426  */
427  Replaceable& operator=(const Replaceable&) = default;
428  Replaceable& operator=(Replaceable&&) = default;
429 
430  /* Rule-of-zero destructor. The ugly code to make this work is above, in
431  * namespace folly::replaceable_detail.
432  */
433  ~Replaceable() = default;
434 
439  template <
440  class... Args,
441  std::enable_if_t<std::is_constructible<T, Args&&...>::value, int> = 0>
443  // clang-format off
444  noexcept(std::is_nothrow_constructible<T, Args&&...>::value)
445  // clang-format on
446  : ctor_base(0) {
447  ::new (storage_) T(std::forward<Args>(args)...);
448  }
449 
450  template <
451  class U,
452  class... Args,
453  std::enable_if_t<
454  std::is_constructible<T, std::initializer_list<U>, Args&&...>::value,
455  int> = 0>
457  in_place_t,
458  std::initializer_list<U> il,
459  Args&&... args)
460  // clang-format off
461  noexcept(std::is_nothrow_constructible<
462  T,
463  std::initializer_list<U>,
464  Args&&...>::value)
465  // clang-format on
466  : ctor_base(0) {
467  ::new (storage_) T(il, std::forward<Args>(args)...);
468  }
469 
470  template <
471  class U = T,
472  std::enable_if_t<
474  !std::is_same<std::decay_t<U>, in_place_t>::value &&
475  !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&
477  int> = 0>
478  FOLLY_CPP14_CONSTEXPR /* implicit */ Replaceable(U&& other)
479  // clang-format off
480  noexcept(std::is_nothrow_constructible<T, U&&>::value)
481  // clang-format on
482  : ctor_base(0) {
483  ::new (storage_) T(std::forward<U>(other));
484  }
485 
486  template <
487  class U = T,
488  std::enable_if_t<
490  !std::is_same<std::decay_t<U>, in_place_t>::value &&
491  !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&
492  !std::is_convertible<U&&, T>::value,
493  int> = 0>
494  FOLLY_CPP14_CONSTEXPR explicit Replaceable(U&& other)
495  // clang-format off
496  noexcept(std::is_nothrow_constructible<T, U&&>::value)
497  // clang-format on
498  : ctor_base(0) {
499  ::new (storage_) T(std::forward<U>(other));
500  }
501 
502  template <
503  class U,
504  std::enable_if_t<
507  T>::value &&
510  int> = 0>
511  /* implicit */ Replaceable(const Replaceable<U>& other)
512  // clang-format off
514  // clang-format on
515  : ctor_base(0) {
516  ::new (storage_) T(*other);
517  }
518 
519  template <
520  class U,
521  std::enable_if_t<
524  T>::value &&
526  !std::is_convertible<const U&, T>::value,
527  int> = 0>
528  explicit Replaceable(const Replaceable<U>& other)
529  // clang-format off
531  // clang-format on
532  : ctor_base(0) {
533  ::new (storage_) T(*other);
534  }
535 
536  template <
537  class U,
538  std::enable_if_t<
541  T>::value &&
543  std::is_convertible<U&&, T>::value,
544  int> = 0>
545  /* implicit */ Replaceable(Replaceable<U>&& other)
546  // clang-format off
547  noexcept(std::is_nothrow_constructible<T, U&&>::value)
548  // clang-format on
549  : ctor_base(0) {
550  ::new (storage_) T(std::move(*other));
551  }
552 
553  template <
554  class U,
555  std::enable_if_t<
558  T>::value &&
560  !std::is_convertible<U&&, T>::value,
561  int> = 0>
562  explicit Replaceable(Replaceable<U>&& other)
563  // clang-format off
564  noexcept(std::is_nothrow_constructible<T, U&&>::value)
565  // clang-format on
566  : ctor_base(0) {
567  ::new (storage_) T(std::move(*other));
568  }
569 
582  template <class... Args>
583  T& emplace(Args&&... args) noexcept {
584  T* destruct_ptr = launder(reinterpret_cast<T*>(storage_));
585  destruct_ptr->~T();
586  return *::new (storage_) T(std::forward<Args>(args)...);
587  }
588 
589  template <class U, class... Args>
590  T& emplace(std::initializer_list<U> il, Args&&... args) noexcept {
591  T* destruct_ptr = launder(reinterpret_cast<T*>(storage_));
592  destruct_ptr->~T();
593  return *::new (storage_) T(il, std::forward<Args>(args)...);
594  }
595 
602  void swap(Replaceable& other) {
603  using std::swap;
604  swap(*(*this), *other);
605  }
606 
610  constexpr const T* operator->() const {
611  return launder(reinterpret_cast<T const*>(storage_));
612  }
613 
615  return launder(reinterpret_cast<T*>(storage_));
616  }
617 
618  constexpr const T& operator*() const& {
619  return *launder(reinterpret_cast<T const*>(storage_));
620  }
621 
623  return *launder(reinterpret_cast<T*>(storage_));
624  }
625 
627  return std::move(*launder(reinterpret_cast<T*>(storage_)));
628  }
629 
630  constexpr const T&& operator*() const&& {
631  return std::move(*launder(reinterpret_cast<T const*>(storage_)));
632  }
633 
634  private:
640  std::aligned_storage_t<sizeof(T), alignof(T)> storage_[1];
641 };
642 
643 #if __cplusplus > 201402L
644 // C++17 allows us to define a deduction guide:
645 template <class T>
646 Replaceable(T)->Replaceable<T>;
647 #endif
648 } // namespace folly
copy_assignment_mixin & operator=(copy_assignment_mixin const &other) noexcept(std::is_nothrow_destructible< T >::value &&std::is_nothrow_copy_constructible< T >::value)
Definition: Replaceable.h:346
std::unique_ptr< int > A
FOLLY_CPP14_CONSTEXPR Replaceable(in_place_t, std::initializer_list< U > il, Args &&...args) noexcept(std::is_nothrow_constructible< T, std::initializer_list< U >, Args &&... >::value)
Definition: Replaceable.h:456
FOLLY_CPP14_CONSTEXPR T & operator*()&
Definition: Replaceable.h:622
default_and_move_ctor_mixin() noexcept(std::is_nothrow_constructible< T >::value)
Definition: Replaceable.h:201
#define FOLLY_CPP14_CONSTEXPR
Definition: Portability.h:424
constexpr const T & operator*() const &
Definition: Replaceable.h:618
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
STL namespace.
FOLLY_CPP14_CONSTEXPR Replaceable(U &&other) noexcept(std::is_nothrow_constructible< T, U && >::value)
Definition: Replaceable.h:478
folly::std T
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
in_place_tag in_place(in_place_tag={})
Definition: Utility.h:235
requires E e noexcept(noexcept(s.error(std::move(e))))
in_place_tag(&)(in_place_tag) in_place_t
Definition: Utility.h:229
~dtor_mixin() noexcept(std::is_nothrow_destructible< T >::value)
Definition: Replaceable.h:159
bool_constant< true > true_type
Definition: gtest-port.h:2210
Replaceable(Replaceable< U > &&other) noexcept(std::is_nothrow_constructible< T, U && >::value)
Definition: Replaceable.h:545
T & emplace(Args &&...args) noexcept
Definition: Replaceable.h:583
FOLLY_CPP14_CONSTEXPR T * operator->()
Definition: Replaceable.h:614
std::integral_constant< bool, B > bool_constant
Definition: Traits.h:145
move_assignment_mixin & operator=(move_assignment_mixin &&other) noexcept(std::is_nothrow_destructible< T >::value &&std::is_nothrow_move_constructible< T >::value)
Definition: Replaceable.h:282
void swap(Replaceable &other)
Definition: Replaceable.h:602
default_and_move_ctor_mixin() noexcept(std::is_nothrow_constructible< T >::value)
Definition: Replaceable.h:224
static const char *const value
Definition: Conv.cpp:50
T & emplace(std::initializer_list< U > il, Args &&...args) noexcept
Definition: Replaceable.h:590
Replaceable(const Replaceable< U > &other) noexcept(std::is_nothrow_constructible< T, U const & >::value)
Definition: Replaceable.h:511
void swap(exception_wrapper &a, exception_wrapper &b) noexcept
constexpr Replaceable< std::decay_t< T > > make_replaceable(T &&t)
Definition: Replaceable.h:385
default_and_move_ctor_mixin(default_and_move_ctor_mixin &&other) noexcept(std::is_nothrow_constructible< T, T && >::value)
Definition: Replaceable.h:205
const
Definition: upload.py:398
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
bool_constant< false > false_type
Definition: gtest-port.h:2209
default_and_move_ctor_mixin(default_and_move_ctor_mixin &&other) noexcept(std::is_nothrow_constructible< T, T && >::value)
Definition: Replaceable.h:243
FOLLY_NODISCARD T * launder(T *in) noexcept
Definition: Launder.h:48
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
constexpr const T * operator->() const
Definition: Replaceable.h:610
constexpr const T && operator*() const &&
Definition: Replaceable.h:630
FOLLY_CPP14_CONSTEXPR Replaceable(in_place_t, Args &&...args) noexcept(std::is_nothrow_constructible< T, Args &&... >::value)
Definition: Replaceable.h:442
copy_ctor_mixin(copy_ctor_mixin const &other) noexcept(std::is_nothrow_constructible< T, T const & >::value)
Definition: Replaceable.h:312
FOLLY_CPP14_CONSTEXPR T && operator*()&&
Definition: Replaceable.h:626