any_state.hpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2014, 2015 Ableton AG, Berlin. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 //
22 
27 #pragma once
28 
29 #include <atria/xform/config.hpp>
31 #include <atria/estd/memory.hpp>
32 #include <atria/meta/pack.hpp>
33 #include <atria/meta/utils.hpp>
34 #include <string>
35 #include <stdexcept>
36 
37 #if ABL_TRACE_ANY_STATE_ALLOC
38 #include <iostream>
39 #endif
40 
41 namespace atria {
42 namespace xform {
43 
48 class any_state
49 {
50 public:
51  any_state() noexcept
52  : data_(reinterpret_cast<char*>(&null_holder_))
53  , size_(0)
54  {}
55 
56  ~any_state() noexcept {
57  if (size_) {
58  content()->~holder_base();
59  delete [] data_;
60  }
61  }
62 
63  any_state(any_state&& other)
64  : data_(other.data_)
65  , size_{}
66  {
67  using std::swap;
68  swap(size_, other.size_);
69  }
70 
71  any_state(const any_state& other)
72  : data_(other.size_ ? new char[other.size_] : nullptr)
73  , size_(other.size_)
74  {
75 #if ABL_TRACE_ANY_STATE_ALLOC
76  std::cout << "alloc-c" << std::endl;
77 #endif
78  other.content()->clone(data_);
79  }
80 
81  template<typename ValueType>
82  any_state(ValueType&& value,
84  !std::is_base_of<any_state,
85  estd::decay_t<ValueType> >::value &&
86  !std::is_base_of<meta::bottom,
88  >* = 0)
89  : data_(new char[sizeof(holder<estd::decay_t<ValueType> >)])
90  , size_(sizeof(holder<estd::decay_t<ValueType> >))
91  {
92  new (data_) holder<estd::decay_t<ValueType> >(
93  std::forward<ValueType>(value));
94 #if ABL_TRACE_ANY_STATE_ALLOC
95  std::cout << "alloc-n " << typeid(estd::decay_t<ValueType>).name() << std::endl;
96 #endif
97  }
98 
99  any_state& operator=(any_state&& other)
100  {
101  using std::swap;
102  swap(data_, other.data_);
103  swap(size_, other.size_);
104  return *this;
105  }
106 
107  any_state& operator=(const any_state& rhs)
108  {
109  if (&rhs != this) {
110  realloc_(rhs.content()->size());
111  rhs.content()->clone(data_);
112  }
113  return *this;
114  }
115 
116  template <typename ValueType>
117  auto operator=(ValueType&& rhs)
119  !std::is_base_of<any_state, estd::decay_t<ValueType> >::value,
120  any_state&>
121  {
122  realloc_(sizeof(holder<estd::decay_t<ValueType> >));
123  new (data_) holder<estd::decay_t<ValueType> >(
124  std::forward<ValueType>(rhs));
125  return *this;
126  }
127 
128  template <typename T>
129  estd::decay_t<T>& as() &
130  { return as_impl(meta::pack<estd::decay_t<T> >{}); }
131 
132  template <typename T>
133  estd::decay_t<T>&& as() &&
134  { return std::move(as_impl(meta::pack<estd::decay_t<T> >{})); }
135 
136  template <typename T>
137  const estd::decay_t<T>& as() const& {
138  return const_cast<any_state*>(this)->as_impl(
140  }
141 
142  template <typename T>
143  void check() const {
144  if (!has<T>()) {
145  throw std::runtime_error(
146  std::string("Have ") + type().name() +
147  ", but expect " + typeid(estd::decay_t<T>).name());
148  }
149  }
150 
151  template <typename T>
152  bool has() const {
153  return has_impl(meta::pack<estd::decay_t<T> >{});
154  }
155 
156  const std::type_info& type() const noexcept {
157  return content()->type();
158  }
159 
160 private:
161  void realloc_(std::size_t size) {
162  if (size_ > 0)
163  content()->~holder_base();
164  if (size_ < size) {
165  if (size_ > 0)
166  delete [] data_;
167  data_ = new char[size];
168  size_ = size;
169 #if ABL_TRACE_ANY_STATE_ALLOC
170  std::cout << "alloc-r" << std::endl;
171 #endif
172  }
173  }
174 
175  template <typename T>
176  T& as_impl(meta::pack<T>) {
177 #if ABL_SAFE_ANY_STATE
178  check<T>();
179 #endif
180  return reinterpret_cast<holder<T>*>(data_)->held;
181  }
182 
183  any_state& as_impl(meta::pack<any_state>) {
184  return *this;
185  }
186 
187  template <typename T>
188  bool has_impl(meta::pack<T>) const {
189  return content()->type() == typeid(T);
190  }
191 
192  bool has_impl(meta::pack<any_state>) const {
193  return true;
194  }
195 
196  friend struct state_traits<any_state>;
197 
198  struct holder_base
199  {
200  virtual ~holder_base();
201  virtual const std::type_info& type() const noexcept = 0;
202  virtual void clone(char* data) const = 0;
203  virtual void move(char* data) const = 0;
204  virtual any_state complete() const = 0;
205  virtual bool is_reduced() const = 0;
206  virtual any_state unwrap() const = 0;
207  virtual any_state unwrap_all() const = 0;
208  virtual any_state rewrap(any_state) const = 0;
209  virtual any_state data() const = 0;
210  virtual std::size_t size() const = 0;
211  };
212 
213  template <typename T>
214  struct holder : holder_base
215  {
216  T held;
217 
218  template <typename TT>
219  holder(TT&& x) : held(std::forward<TT>(x)) {}
220 
221  const std::type_info& type() const noexcept override
222  { return typeid(T); }
223 
224  void clone(char* data) const override
225  { new (data) holder<T>(held); }
226 
227  void move(char* data) const override
228  { new (data) holder<T>(std::move(held)); }
229 
230  any_state complete() const override
231  { return state_complete(held); }
232 
233  bool is_reduced() const override
234  { return state_is_reduced(held); }
235 
236  any_state unwrap() const override
237  { return state_unwrap(held); }
238 
239  any_state unwrap_all() const override
240  { return state_unwrap_all(held); }
241 
242  any_state rewrap(any_state x) const override
243  { return state_rewrap(held, std::move(x)); }
244 
245  any_state data() const override
246  { return state_data(held, [] { return any_state{}; }); }
247 
248  std::size_t size() const override
249  { return sizeof(T); }
250  };
251 
252  struct null_holder : holder_base
253  {
254  virtual ~null_holder();
255 
256  const std::type_info& type() const noexcept override
257  { return typeid(void); }
258 
259  void clone(char* data) const override { new (data) null_holder; }
260  void move(char* data) const override { new (data) null_holder; }
261  any_state complete() const override { return {}; }
262  bool is_reduced() const override { return false; }
263  any_state unwrap() const override { return {}; }
264  any_state unwrap_all() const override { return {}; }
265  any_state rewrap(any_state x) const override { return x; }
266  any_state data() const override { return {}; }
267  std::size_t size() const override { return 0; }
268  };
269 
270  holder_base* content() const { return reinterpret_cast<holder_base*>(data_); }
271 
272  char* data_;
273  std::size_t size_;
274 
275  static null_holder null_holder_;
276 };
277 
278 template <>
280 {
281  template <typename T>
282  static auto complete(T&& t)
284  std::forward<T>(t).content()->complete())
285 
286  template <typename T>
287  static auto is_reduced(T&& t)
289  std::forward<T>(t).content()->is_reduced())
290 
291  template <typename T>
292  static auto unwrap(T&& t)
294  std::forward<T>(t).content()->unwrap())
295 
296  template <typename T>
297  static auto unwrap_all(T&& t)
299  std::forward<T>(t).content()->unwrap_all())
300 
301  template <typename T, typename U>
302  static auto rewrap(T&& t, U&& x)
304  std::forward<T>(t).content()->rewrap(std::forward<U>(x)))
305 
306  template <typename T, typename D>
307  static auto data(T&& t, D&& d)
309  {
310  using data_t = estd::decay_t<decltype(std::forward<D>(d)())>;
311  auto data = t.content()->data();
312  return data.template has<data_t>()
313  ? data.template as<data_t>()
314  : std::forward<D>(d)();
315  }
316 };
317 
318 } // namespace xform
319 } // namespace atria
Polymorphically holds any value implementing the state_traits.
Definition: any_state.hpp:48
static auto complete(T &&state) -> decltype(std::forward< T >(state))
Unwraps all the layers of state wrappers returning the deepmost.
static auto is_reduced(T &&) -> bool
Returns whether the value is idempotent, and thus, the reduction can finish.
#define ABL_DECLTYPE_RETURN(body_expr)
Utility for defining generic functions with a deduced return type, that are composed of a single expr...
Definition: utils.hpp:109
typename std::enable_if< X, T >::type enable_if_t
Similar to C++14 std::enable_if_t.
Definition: type_traits.hpp:84
auto state_unwrap(T &&s) -> decltype(state_traits_t< T >::unwrap(std::forward< T >(s)))
Convenience function for calling state_traits::unwrap
typename std::decay< T >::type decay_t
Similar to C++14 std::decay_t.
Definition: type_traits.hpp:54
auto state_data(T &&s, D &&d) -> decltype(state_traits_t< T >::data(std::forward< T >(s), std::forward< D >(d)))
Convenience function for calling state_traits::data
Interface for a type specializing the State concept.
MPL-compatible sequence that just holds a vector of types as a paremeter pack.
Definition: pack.hpp:57
static auto unwrap(T &&state) -> decltype(std::forward< T >(state))
Unwraps this layers of state wrappers, returning the nested state for the next reducing function...
auto state_rewrap(T &&s, U &&x) -> decltype(state_traits_t< T >::rewrap(std::forward< T >(s), std::forward< U >(x)))
Convenience function for calling state_traits::unwrap_all
auto state_complete(T &&s) -> decltype(state_traits_t< T >::complete(std::forward< T >(s)))
Convenience function for calling state_traits::complete
static auto data(T &&, D &&d) -> decltype(std::forward< D >(d)())
Returns the associated from the current state.
static auto unwrap_all(T &&state) -> decltype(std::forward< T >(state))
Unwraps all layers of state wrappers, returning the most nested state for the innermost reducing func...
static auto rewrap(T &&, U &&x) -> decltype(std::forward< U >(x))
Copies all layers of state wrappers but replaces the innermost state with substate.
C++ amazing templates and reusable implementations awesomeness.
Definition: _doc.hpp:35
auto state_is_reduced(T &&s) -> bool
Convenience function for calling state_traits::is_reduced
auto state_unwrap_all(T &&s) -> decltype(state_traits_t< T >::unwrap_all(std::forward< T >(s)))
Convenience function for calling state_traits::unwrap_all
This is a type that pretends to be convertible to anything.
Definition: utils.hpp:48
Fork me on GitHub