structure.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 
30 
31 #include <ableton/build_system/Warnings.hpp>
32 ABL_DISABLE_WARNINGS
33 #include <boost/operators.hpp>
34 #include <boost/fusion/include/adapt_struct.hpp>
35 #include <boost/fusion/include/fold.hpp>
36 #include <boost/fusion/include/comparison.hpp>
37 #include <boost/fusion/include/out.hpp>
38 ABL_RESTORE_WARNINGS
39 #include <atomic>
40 #include <functional>
41 #include <iosfwd>
42 
43 namespace atria {
44 namespace funken {
45 
46 namespace detail {
47 
49 template <typename T>
50 std::size_t hash_value(const T& v)
51 {
52  return std::hash<T>{}(v);
53 }
54 
56 template <typename T>
57 void hash_combine(std::size_t& seed, const T& v)
58 {
59  seed ^= hash_value(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
60 }
61 
62 struct hash_combine_fn
63 {
64  template <typename T>
65  std::size_t operator()(std::size_t seed, const T& v) const
66  {
67  hash_combine(seed, v);
68  return seed;
69  }
70 };
71 
72 struct first_equal_to_second
73 {
74  template <typename T>
75  bool operator()(const T& x) const
76  {
77  using namespace boost::fusion;
78  return at_c<0>(x) == at_c<1>(x);
79  }
80 };
81 
82 } // namespace detail
83 
87 template <typename T>
88 struct structure
89  : boost::equality_comparable<structure<T> >
90  , boost::less_than_comparable<structure<T> >
91 {
92  structure() = default;
93  structure(structure&& x) = default;
94  structure(const structure& x) = default;
95  structure& operator=(structure&& x) = default;
96  structure& operator=(const structure& x) = default;
97 
98 
99  std::size_t hash() const
100  {
101  // Assume computed hash is never 0, I'm not sure this holds... in
102  // Clojure they do this sometimes with Murmur3, please research
103  // this. Otherwise, I just feel sorry for you if your hash is 0 :)
104  if (mHash)
105  {
106  return mHash;
107  }
108  else
109  {
110  using namespace boost::fusion;
111  auto hash = fold(self(), 0, detail::hash_combine_fn{});
112  mHash = hash;
113  return hash;
114  }
115  }
116 
117  void modified()
118  {
119  mHash = 0;
120  }
121 
122  const T& self() const { return *static_cast<const T*>(this); }
123  T& self() { return *static_cast<const T*>(this); }
124 
125 private:
126  mutable std::size_t mHash = 0;
127 };
128 
129 template <typename T>
130 bool operator==(const structure<T>& a, const structure<T>& b)
131 {
132  if (&a == &b)
133  return true;
134  if (a.hash() != b.hash())
135  return false;
136  return boost::fusion::equal_to(a.self(), b.self());
137 }
138 
139 template <typename T>
140 bool operator<(const structure<T>& a, const structure<T>& b)
141 {
142  return boost::fusion::less(a.self(), b.self());
143 }
144 
145 template <typename T>
146 auto operator<<(std::ostream& os, const T& x)
147  -> estd::enable_if_t<std::is_convertible<T&, structure<T>&>::value,
148  std::ostream&>
149 {
150  os << typeid(x).name();
151  boost::fusion::out(os, x);
152  return os;
153 }
154 
159 template <typename T>
160 auto modified(T&)
162 {}
163 
164 template <typename T>
165 void modified(structure<T>& x)
166 {
167  x.modified();
168 }
169 
170 } // namespace funken
171 } // namespace atria
172 
173 namespace std {
174 
175 template <typename T>
176 struct hash<atria::funken::structure<T> >
177 {
178  std::size_t operator()(const atria::funken::structure<T>& v) const
179  {
180  return v.hash();
181  }
182 };
183 
184 } // namespace std
185 
186 #define ABL_FUNKEN_STRUCT(structure_full_name, structure_members) \
187  BOOST_FUSION_ADAPT_STRUCT(structure_full_name, structure_members) \
188  namespace std { \
189  template<> \
190  struct hash<structure_full_name> \
191  : hash<atria::funken::structure<structure_full_name> > \
192  {}; \
193  } /* namespace std */ \
194 
auto out(OutT &&object) -> estd::enable_if_t< (Out_value< OutT >()), detail::output_impl< detail::signal_type_t< OutT > > >
Creates an out from another out value.
Definition: out.hpp:102
typename std::enable_if< X, T >::type enable_if_t
Similar to C++14 std::enable_if_t.
Definition: type_traits.hpp:84
STL namespace.
auto modified(T &) -> estd::enable_if_t<!std::is_convertible< T &, structure< T > & >::value >
Call this on an structure value to indicate that is has indeed changed, invalidating its cached value...
Definition: structure.hpp:160
C++ amazing templates and reusable implementations awesomeness.
Definition: _doc.hpp:35
Fork me on GitHub