File: copyableoptional.hh

package info (click to toggle)
dune-common 2.11.0-1~exp2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 6,056 kB
  • sloc: cpp: 54,404; python: 4,136; sh: 1,657; makefile: 17
file content (157 lines) | stat: -rw-r--r-- 5,645 bytes parent folder | download | duplicates (3)
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
#ifndef DUNE_COMMON_COPYABLE_OPTIONAL_HH
#define DUNE_COMMON_COPYABLE_OPTIONAL_HH

#include <cassert>
#include <iostream>
#include <memory>
#include <optional>
#include <type_traits>

#include <dune/common/typeutilities.hh>

namespace Dune {

/**
 * \brief A copyable type wrapper that provides copy/move assignment
 * operations for types that are only copy/move constructible.
 *
 * Some types, like lambdas, LocalFiniteElementCache, Geometries,... provide
 * a copy/move constructor but do not provide a corresponding assignment
 * operator. Mostly, the assignment operators can be implemented in terms of
 * the corresponding constructors. This type wrapper provides these assignment
 * operators by internally wrapping the type into a std::optional.
 *
 * \tparam Type  The type to be wrapped and equipped with copy operations.
 *
 * \b Requirements: `Type` must be an object type that is copy-constructible
 **/
template <class Type>
class CopyableOptional
    : public std::optional<Type>
{
  static_assert(std::is_copy_constructible_v<Type>);
  static_assert(std::is_object_v<Type>);

  using Base = std::optional<Type>;

public:

  /**
   * \brief Implementation of a default constructor, if the `Type` is itself
   * default constructible. The internal optional contains a value-initialized `Type`.
   **/
  template <class T = Type,
    std::enable_if_t<std::is_default_constructible_v<T>, int> = 0>
  constexpr CopyableOptional ()
        noexcept(std::is_nothrow_default_constructible_v<T>)
    : Base{std::in_place}
  {}

  /**
   * \brief Construct the internal data from perfect forwarding of the passed arguments.
   * Participates in overload resolution if `T` is implicitly convertible to `Type`.
   **/
  template <class T = Type,
    disableCopyMove<CopyableOptional,T> = 0,
    std::enable_if_t<std::is_constructible_v<Type,T&&>, int> = 0,
    std::enable_if_t<std::is_convertible_v<T&&,Type>, int> = 0>
  constexpr CopyableOptional (T&& value)
        noexcept(std::is_nothrow_constructible_v<Type,T&&>)
    : Base{std::in_place, std::forward<T>(value)}
  {}

  /**
   * \brief Construct the internal data from perfect forwarding of the passed arguments.
   * Participates in overload resolution if `T` is \b not implicitly convertible to `Type`.
   **/
  template <class T = Type,
    disableCopyMove<CopyableOptional,T> = 0,
    std::enable_if_t<std::is_constructible_v<Type,T&&>, int> = 0,
    std::enable_if_t<not std::is_convertible_v<T&&,Type>, int> = 0>
  explicit constexpr CopyableOptional (T&& value)
        noexcept(std::is_nothrow_constructible_v<Type,T&&>)
    : Base{std::in_place, std::forward<T>(value)}
  {}

  /// \brief Construct the internal data from perfect forwarding of the passed arguments.
  template <class... Args,
    disableCopyMove<CopyableOptional, Args...> = 0,
    std::enable_if_t<(sizeof...(Args) > 1), int> = 0,
    std::enable_if_t<std::is_constructible_v<Type,Args&&...>, int> = 0>
  constexpr CopyableOptional (Args&&... args)
        noexcept(std::is_nothrow_constructible_v<Type,Args&&...>)
    : Base{std::in_place, std::forward<Args>(args)...}
  {}

  /// \brief Copy construct the contained value
  constexpr CopyableOptional (const CopyableOptional&) = default;

  /// \brief Move construct the contained value
  constexpr CopyableOptional (CopyableOptional&&) = default;

  /// \brief Default destructor
  ~CopyableOptional () = default;

  /// \brief Copy assignment in terms of copy constructor
  constexpr CopyableOptional& operator= (const CopyableOptional& that)
        noexcept(std::is_nothrow_copy_assignable_v<Type> ||
          (!std::is_copy_assignable_v<Type> && std::is_nothrow_copy_constructible_v<Type>))
  {
    if constexpr(std::is_copy_assignable_v<Type>)
      Base::operator=(that);
    else {
      // no self-assignment
      if (this != std::addressof(that)) {
        if (that)
          Base::emplace(*that);
        else
          Base::reset();
      }
    }
    return *this;
  }

  /// \brief Move assignment in terms of move constructor
  template <class T = Type,
    std::enable_if_t<std::is_move_constructible_v<T>, int> = 0>
  constexpr CopyableOptional& operator= (CopyableOptional&& that)
        noexcept(std::is_nothrow_move_assignable_v<Type> ||
          (!std::is_move_assignable_v<Type> && std::is_nothrow_move_constructible_v<Type>))
  {
    if constexpr(std::is_move_assignable_v<Type>)
      Base::operator=(std::move(that));
    else {
      // no self-assignment
      if (this != std::addressof(that)) {
        if (that)
          Base::emplace(std::move(*that));
        else
          Base::reset();
      }
    }
    return *this;
  }

  /// \brief Perfect-forwarded assignment or construction
  template <class T = Type,
    std::enable_if_t<not std::is_same_v<std::decay_t<T>, CopyableOptional>, int> = 0,
    std::enable_if_t<(std::is_assignable_v<Type&,T> || std::is_constructible_v<Type,T>), int> = 0>
  constexpr CopyableOptional& operator= (T&& value)
        noexcept(std::is_nothrow_assignable_v<Type&,T> ||
          (!std::is_assignable_v<Type&,T> && std::is_nothrow_constructible_v<Type,T>))
  {
    if constexpr(std::is_assignable_v<Type&,T>)
      Base::operator=(std::forward<T>(value));
    else
      Base::emplace(std::forward<T>(value));
    return *this;
  }
};

} // end namespace Dune

#endif // DUNE_COMMON_COPYABLE_OPTIONAL_HH