neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
plugin_variant.hpp
Go to the documentation of this file.
1// plugin_variant.hpp
2/*
3 * Copyright (c) 2019, 2020 Leigh Johnston.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of Leigh Johnston nor the names of any
19 * other contributors to this software may be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*/
35
36#pragma once
37
38#include <neolib/neolib.hpp>
39#include <type_traits>
40#include <boost/type_traits.hpp>
41#include <neolib/core/enum.hpp>
44
45namespace neolib
46{
47 namespace detail
48 {
50 {
51 template <typename V>
52 using funky_assign_t = std::function<void(V&, const void*)>;
53 template <typename V>
54 using funky_assign_list_t = std::vector<funky_assign_t<V>> ;
55 template <typename V>
57 {
58 return aList.size();
59 }
60 template <typename V, typename T, typename... Types>
62 {
63 aList.push_back(
64 [](V& aThis, const void* aData)
65 {
66 typedef std::decay_t<T> type;
67 typedef abstract_t<type> assign_type;
68 aThis = *static_cast<const assign_type*>(aData);
69 });
70 return funky_gen_assign<V, Types...>(aList);
71 }
72
73 template <typename V>
74 using funky_move_assign_t = std::function<void(V&, void*)>;
75 template <typename V>
76 using funky_move_assign_list_t = std::vector<funky_move_assign_t<V>> ;
77 template <typename V>
79 {
80 return aList.size();
81 }
82 template <typename V, typename T, typename... Types>
84 {
85 aList.push_back(
86 [](V& aThis, void* aData)
87 {
88 typedef std::decay_t<T> type;
89 typedef abstract_t<type> move_assign_type;
90 aThis = static_cast<move_assign_type&&>(*static_cast<move_assign_type*>(aData));
91 });
92 return funky_gen_move_assign<V, Types...>(aList);
93 }
94 }
95 }
96
97 template <typename Id, typename... Types>
99 public reference_counted<i_plugin_variant<Id, abstract_t<Types>...>>,
100 public std::variant<std::monostate, Types...>
101 {
102 typedef plugin_variant<Id, Types...> self_type;
104 // types
105 public:
107 using typename base_type::id_t;
108 using typename base_type::index_type;
109 typedef std::variant<std::monostate, Types...> variant_type;
110 // construction/assignment
111 public:
112 using variant_type::variant_type;
114 {
115 if (!aOther.empty())
116 do_assign(aOther.which(), aOther.data());
117 }
118 plugin_variant(self_type&& aOther) noexcept
119 {
120 if (!aOther.empty())
121 do_move_assign(aOther.which(), aOther.data());
122 }
124 {
125 if (!aOther.empty())
126 do_assign(aOther.which(), aOther.data());
127 }
128 plugin_variant(abstract_type&& aOther) noexcept
129 {
130 if (!aOther.empty())
131 do_move_assign(aOther.which(), aOther.data());
132 }
134 {
135 variant_type::operator=(aOther);
136 return *this;
137 }
138 self_type& operator=(self_type&& aOther) noexcept
139 {
140 variant_type::operator=(std::move(aOther));
141 return *this;
142 }
144 {
145 variant_type::operator=(std::monostate{});
146 return *this;
147 }
148 using abstract_type::operator=;
149 // comparison
150 public:
151 bool operator==(none_t) const
152 {
153 return std::holds_alternative<std::monostate>(*this);
154 }
155 bool operator==(const abstract_type& that) const final
156 {
157 if (index() != that.index())
158 return false;
159 bool result = false;
160 std::visit([this, &that, &result](auto&& v)
161 {
162 typedef std::decay_t<decltype(v)> type;
163 typedef abstract_t<type> comparison_type;
164 if constexpr (boost::has_equal_to<comparison_type, comparison_type>::value)
165 result = (*static_cast<const comparison_type*>(data()) == *static_cast<const comparison_type*>(that.data()));
166 else
168 }, as_std_variant());
169 return result;
170 }
171 bool operator<(const abstract_type& that) const final
172 {
173 if (index() != that.index())
174 return index() < that.index();
175 if (index() == 0u)
176 return false;
177 bool result = false;
178 std::visit([this, &that, &result](auto&& v)
179 {
180 typedef std::decay_t<decltype(v)> type;
181 typedef abstract_t<type> comparison_type;
182 if constexpr (boost::has_less<comparison_type, comparison_type>::value)
183 result = (*static_cast<const comparison_type*>(data()) < *static_cast<const comparison_type*>(that.data()));
184 else
186 }, as_std_variant());
187 return result;
188 }
189 std::partial_ordering operator<=>(const self_type& that) const
190 {
191 if (*this == that)
192 return std::partial_ordering::equivalent;
193 else if (*this < that)
194 return std::partial_ordering::less;
195 else
196 return std::partial_ordering::greater;
197 }
198 // state
199 public:
200 void clear() final
201 {
202 variant_type::operator=(std::monostate{});
203 }
204 id_t which() const final
205 {
206 if (!empty())
207 return static_cast<id_t>(index() - 1u);
208 throw bad_variant_access();
209 }
210 bool empty() const final
211 {
212 return std::holds_alternative<std::monostate>(*this);
213 }
214 // meta
215 public:
217 {
218 return *this;
219 }
221 {
222 return *this;
223 }
224 const typename i_enum_t<Id>::enumerators_t& ids() const final
225 {
226 return iEnum.enumerators();
227 }
228 std::string which_as_string() const
229 {
230 return ids().enumerators().find(which())->second.to_std_string();
231 }
232 // implementation
233 private:
234 std::size_t index() const final
235 {
236 return variant_type::index();
237 }
238 const void* data() const final
239 {
240 const void* result = nullptr;
241 std::visit([&result](auto&& v) { result = &v; }, as_std_variant());
242 return result;
243 }
244 void* data() final
245 {
246 void* result = nullptr;
247 std::visit([&result](auto&& v) { result = &v; }, as_std_variant());
248 return result;
249 }
250 abstract_type* do_clone() const final
251 {
252 return new self_type{ *this };
253 }
254 abstract_type& do_assign(id_t aType, const void* aData) final
255 {
256 static detail::plugin_variant::funky_assign_list_t<variant_type> funks;
257 static auto const n = detail::plugin_variant::funky_gen_assign<variant_type, Types...>(funks);
258 if (static_cast<std::size_t>(aType) < n)
259 funks[static_cast<std::size_t>(aType)](*this, aData);
260 else
261 throw std::bad_variant_access();
262 return *this;
263 }
264 abstract_type& do_move_assign(id_t aType, void* aData) final
265 {
266 static detail::plugin_variant::funky_move_assign_list_t<variant_type> funks;
267 static auto const n = detail::plugin_variant::funky_gen_move_assign<variant_type, Types...>(funks);
268 if (static_cast<std::size_t>(aType) < n)
269 funks[static_cast<std::size_t>(aType)](*this, aData);
270 else
271 throw std::bad_variant_access();
272 return *this;
273 }
274 // state
275 private:
276 const enum_t<id_t> iEnum;
277 };
278}
279
280namespace std
281{
282 template <typename Visitor, typename Id, typename... Types>
283 auto visit(Visitor&& vis, const neolib::plugin_variant<Id, Types...>& var)
284 {
285 return std::visit(std::forward<Visitor>(vis), var.as_std_variant());
286 }
287
288 template <typename Visitor, typename Id, typename... Types>
289 auto visit(Visitor&& vis, neolib::plugin_variant<Id, Types...>& var)
290 {
291 return std::visit(std::forward<Visitor>(vis), var.as_std_variant());
292 }
293}
294
virtual bool empty() const =0
virtual id_t which() const =0
id_t which() const final
std::string which_as_string() const
variant_type & as_std_variant()
self_type & operator=(const self_type &aOther)
std::variant< std::monostate, Types... > variant_type
plugin_variant(const abstract_type &aOther)
const variant_type & as_std_variant() const
plugin_variant(self_type &&aOther) noexcept
std::partial_ordering operator<=>(const self_type &that) const
bool operator<(const abstract_type &that) const final
plugin_variant(const self_type &aOther)
bool empty() const final
plugin_variant(abstract_type &&aOther) noexcept
bool operator==(const abstract_type &that) const final
self_type & operator=(none_t)
self_type & operator=(self_type &&aOther) noexcept
i_plugin_variant< Id, abstract_t< Types >... > abstract_type
bool operator==(none_t) const
const i_enum_t< Id >::enumerators_t & ids() const final
std::vector< funky_move_assign_t< V > > funky_move_assign_list_t
std::size_t funky_gen_assign(funky_assign_list_t< V > &aList)
std::vector< funky_assign_t< V > > funky_assign_list_t
std::size_t funky_gen_move_assign(funky_move_assign_list_t< V > &aList)
std::function< void(V &, const void *)> funky_assign_t
std::function< void(V &, void *)> funky_move_assign_t
typename detail::abstract_type< T >::type abstract_t
Definition neolib.hpp:178
i_basic_enum< std::underlying_type_t< T > > i_enum_t
Definition i_enum.hpp:211
std::monostate none_t
Definition variant.hpp:110
Definition plf_hive.h:79
constexpr decltype(auto) visit(Visitor &&vis, neolib::variant< Types... > &&var)
Definition variant.hpp:60