P4C
The P4 Compiler
Loading...
Searching...
No Matches
assoc.h
1
19#pragma once
20
21#include <lib/cstring.h>
22#include <lib/log.h>
23#include <lib/map.h>
24#include <lib/ordered_map.h>
25#include <lib/ordered_set.h>
26
27#include <map>
28#include <set>
29#include <string>
30#include <tuple>
31#include <type_traits>
32#include <unordered_map>
33#include <unordered_set>
34#include <utility>
35
36#include <boost/functional/hash.hpp>
37
62namespace assoc {
63
70enum class Iterable { Auto, No, Yes };
71
72#define BFN_ASSOC_OP_DEF(name, OP) \
73 friend bool operator OP(const name &lhs, const name &rhs) { \
74 return static_cast<const ABase &>(lhs) OP static_cast<const ABase &>(rhs); \
75 }
76
78namespace detail {
79
80template <typename T>
81struct _is_stable : std::true_type {};
82
85template <typename T>
86struct is_stable : _is_stable<typename std::decay<T>::type> {};
87
88template <typename T>
89struct _is_stable<T *> : std::false_type {};
90
91#if 0
95template<>
96struct _is_stable<std::string> : std::false_type { };
97
98template<>
99struct _is_stable<cstring> : std::false_type { };
100
101// TODO(C++update): string_view
102#endif
103
104template <typename T1, typename T2>
105struct _is_stable<std::pair<T1, T2>>
106 : std::integral_constant<bool, _is_stable<T1>::value && is_stable<T2>::value> {};
107
108template <>
109struct _is_stable<std::tuple<>> : std::true_type {};
110
111template <typename TH, typename... Ts>
112struct _is_stable<std::tuple<TH, Ts...>>
113 : std::integral_constant<bool, is_stable<TH>::value && _is_stable<std::tuple<Ts...>>::value> {};
114
115template <typename... Ts>
116struct _void {
117 using type = void;
118};
119
123template <typename Base, Iterable Itble, typename = void>
124class CondIterableAssocBase : protected Base {
125 public:
126 using hasher = typename Base::hasher;
127 using key_equal = typename Base::key_equal;
128
129 using Base::Base;
130
131 public:
135 static constexpr bool iterable = Itble == Iterable::Yes;
136};
137
140template <typename Base, Iterable Itble>
141class CondIterableAssocBase<Base, Itble, typename _void<typename Base::key_compare>::type>
142 : protected Base {
143 public:
144 using key_type = typename Base::key_type;
145 using key_compare = typename Base::key_compare;
146 using value_compare = typename Base::value_compare;
147
148 using Base::Base;
149
150 private:
151 using DefaultCompare = std::less<key_type>;
152 using DefaultTransparentCompare = std::less<void>;
153
154 public:
155 static constexpr bool iterable =
156 Itble == Iterable::Yes ||
157 (Itble == Iterable::Auto && // either the comparator is different then the default (or a
158 // transparent version thereof)
159 ((!std::is_same<key_compare, DefaultCompare>::value &&
160 !std::is_same<key_compare, DefaultTransparentCompare>::value)
161 // or the key is not a pointer or string or tuple thereof
163};
164
168template <typename Base, Iterable Itble>
169class CondIterableAssoc : public CondIterableAssocBase<Base, Itble> {
171
172 public:
173 using key_type = typename Base::key_type;
174 using value_type = typename Base::value_type;
175 using size_type = typename Base::size_type;
176 using difference_type = typename Base::difference_type;
177 using allocator_type = typename Base::allocator_type;
178 using reference = typename Base::reference;
179 using const_reference = typename Base::const_reference;
180 using pointer = typename Base::pointer;
181 using const_pointer = typename Base::const_pointer;
182 using iterator = typename Base::iterator;
183 using const_iterator = typename Base::const_iterator;
184 using reverse_iterator = typename Base::reverse_iterator;
185 using const_reverse_iterator = typename Base::const_reverse_iterator;
186 // TODO(C++update): node_type, insert_retur_type
187
188 public:
189 // inherit constructors
190 using ImmBase::ImmBase;
191
192 using Base::operator=;
193 using Base::get_allocator;
194
195 iterator begin() noexcept {
196 this->_ensure_iterable();
197 return Base::begin();
198 }
199 const_iterator begin() const noexcept {
200 this->_ensure_iterable();
201 return Base::begin();
202 }
203 const_iterator cbegin() const noexcept {
204 this->_ensure_iterable();
205 return Base::cbegin();
206 }
207
208 iterator unstable_begin() noexcept { return Base::begin(); }
209 const_iterator unstable_begin() const noexcept { return Base::begin(); }
210 const_iterator unstable_cbegin() const noexcept { return Base::cbegin(); }
211
212 using Base::cend;
213 using Base::end;
214
215 reverse_iterator rbegin() noexcept {
216 this->_ensure_iterable();
217 return Base::rbegin();
218 }
219 const_reverse_iterator rbegin() const noexcept {
220 this->_ensure_iterable();
221 return Base::rbegin();
222 }
223 const_reverse_iterator crbegin() const noexcept {
224 this->_ensure_iterable();
225 return Base::crbegin();
226 }
227
228 using Base::crend;
229 using Base::rend;
230
231 using Base::empty;
232 using Base::max_size;
233 using Base::size;
234
235 using Base::clear;
236 using Base::emplace;
237 using Base::emplace_hint;
238 using Base::erase;
239 using Base::insert;
240 using Base::swap;
241
242 using Base::count;
243 using Base::find;
244
245 std::pair<iterator, iterator> equal_range(const key_type &key) {
246 this->_ensure_iterable();
247 return Base::equal_range(key);
248 }
249 std::pair<const iterator, const_iterator> equal_range(const key_type &key) const {
250 this->_ensure_iterable();
251 return Base::equal_range(key);
252 }
253
254 iterator lower_bound(const key_type &key) {
255 this->_ensure_iterable();
256 return Base::lower_bound(key);
257 }
258 const_iterator lower_bound(const key_type &key) const {
259 this->_ensure_iterable();
260 return Base::lower_bound(key);
261 }
262
263 iterator upper_bound(const key_type &key) {
264 this->_ensure_iterable();
265 return Base::upper_bound(key);
266 }
267 const_iterator upper_bound(const key_type &key) const {
268 this->_ensure_iterable();
269 return Base::upper_bound(key);
270 }
271
272 // TODO(C++update): in C++20
273 bool contains(const key_type &key) const { return find(key) != end(); }
274
275 using Base::key_comp;
276 using Base::value_comp;
277
278 protected:
279 void _ensure_iterable() const {
280 static_assert(ImmBase::iterable,
281 "To ensure the compiler is deterministic and preserves behaviour when "
282 "source values are renamed it is not possible to iterate over maps of "
283 "pointer and string types. Either use `ordered_map` to iterate in the "
284 "insertion order, or specify a comparator that uses some ordering that is "
285 "guaranteed to be stable accross runs and with renaming in the source file. "
286 "If you know for sure it is safe to iterate in string order in this case "
287 "(e.g. map of constant strings or other strings that do not depend on "
288 "the source code, you can use `iterable_map` instead of `map`. ");
289 }
290};
291} // namespace detail
292
297template <typename Key, typename T, typename Compare = std::less<Key>,
298 typename Allocator = std::allocator<std::pair<const Key, T>>,
299 Iterable Itble = Iterable::Auto>
300class map : public detail::CondIterableAssoc<std::map<Key, T, Compare, Allocator>, Itble> {
301 using ABase = std::map<Key, T, Compare, Allocator>;
303
304 public:
305 using mapped_type = typename Base::mapped_type;
306
307 using Base::Base;
308
309 // the initializer_list constructor must be explicitly specified for GCC 6
310 // to pick it up
311 map(std::initializer_list<typename Base::value_type> init, const Compare &comp = Compare(),
312 const Allocator &alloc = Allocator())
313 : Base(init, comp, alloc) {}
314
315 // seems like GCC 6 ignores the default constructor in some cases
316 // FIXME(vstill): remove when GCC 6 is no longer needed
317 map() = default;
318
319 using ABase::operator=;
320
321 using ABase::at;
322 using ABase::operator[];
323 // TODO(C++update): C++17 noexcept
324 void swap(map &other) { ABase::swap(other); }
325
326 using ABase::extract;
327 using ABase::insert_or_assign;
328 using ABase::merge;
329 using ABase::try_emplace;
330
331 BFN_ASSOC_OP_DEF(map, ==)
332 BFN_ASSOC_OP_DEF(map, !=)
333 BFN_ASSOC_OP_DEF(map, <)
334 BFN_ASSOC_OP_DEF(map, >)
335 BFN_ASSOC_OP_DEF(map, <=)
336 BFN_ASSOC_OP_DEF(map, >=)
337 // TODO(C++update): C++20 <=>
338
339
344 const std::map<Key, T, Compare, Allocator> &unstable_iterable() const {
345 return static_cast<const ABase &>(*this);
346 }
347};
348
353template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>,
354 Iterable Itble = Iterable::Auto>
355class set : public detail::CondIterableAssoc<std::set<Key, Compare, Allocator>, Itble> {
356 using ABase = std::set<Key, Compare, Allocator>;
358
359 public:
360 // Inherit constructors
361 using Base::Base;
362
363 // the initializer_list constructor must be explicitly specified for GCC 6
364 // to pick it up
365 set(std::initializer_list<typename Base::value_type> init, const Compare &comp = Compare(),
366 const Allocator &alloc = Allocator())
367 : Base(init, comp, alloc) {}
368
369 // seems like GCC 6 ignores the default constructor in some cases
370 // FIXME(vstill): remove when GCC 6 is no longer needed
371 set() = default;
372
373 using ABase::operator=;
374
375 // TODO(C++update): C++17 noexcept
376 void swap(set &other) { Base::swap(other); }
377
378 BFN_ASSOC_OP_DEF(set, ==)
379 BFN_ASSOC_OP_DEF(set, !=)
380 BFN_ASSOC_OP_DEF(set, <)
381 BFN_ASSOC_OP_DEF(set, >)
382 BFN_ASSOC_OP_DEF(set, <=)
383 BFN_ASSOC_OP_DEF(set, >=)
384 // TODO(C++update): C++20 <=>
385
386
391 const std::set<Key, Compare, Allocator> &unstable_iterable() const {
392 return static_cast<const Base &>(*this);
393 }
394};
395
400template <typename Key, typename T, typename Hash = boost::hash<Key>,
401 typename Equal = std::equal_to<Key>,
402 typename Allocator = std::allocator<std::pair<const Key, T>>>
403class hash_map : private std::unordered_map<Key, T, Hash, Equal, Allocator> {
404 using ABase = std::unordered_map<Key, T, Hash, Equal, Allocator>;
405
406 public:
407 using mapped_type = typename ABase::mapped_type;
408
409 using ABase::ABase;
410 using ABase::operator=;
411
412 using ABase::at;
413 using ABase::operator[];
414
415 using ABase::cend;
416 using ABase::end;
417
418 using ABase::empty;
419 using ABase::max_size;
420 using ABase::size;
421
422 using ABase::clear;
423 using ABase::emplace;
424 using ABase::emplace_hint;
425 using ABase::extract;
426 using ABase::insert;
427 using ABase::insert_or_assign;
428 using ABase::merge;
429 using ABase::try_emplace;
430
431 using ABase::erase;
432 using ABase::swap;
433
434 using ABase::count;
435 using ABase::find;
436
437 BFN_ASSOC_OP_DEF(hash_map, ==)
438 BFN_ASSOC_OP_DEF(hash_map, !=)
439
440 void swap(hash_map &other) noexcept { ABase::swap(other); }
441
447 const ABase &unstable_iterable() const { return static_cast<const ABase &>(*this); }
448};
449
454template <typename T, typename Hash = boost::hash<T>, typename Equal = std::equal_to<T>,
455 typename Allocator = std::allocator<T>>
456class hash_set : private std::unordered_set<T, Hash, Equal, Allocator> {
457 using ABase = std::unordered_set<T, Hash, Equal, Allocator>;
458
459 public:
460 using ABase::ABase;
461
462 using ABase::operator=;
463
464 using ABase::cend;
465 using ABase::end;
466
467 using ABase::empty;
468 using ABase::max_size;
469 using ABase::size;
470
471 using ABase::clear;
472 using ABase::emplace;
473 using ABase::emplace_hint;
474 using ABase::erase;
475 using ABase::insert;
476 using ABase::swap;
477
478 using ABase::count;
479 using ABase::find;
480
481 BFN_ASSOC_OP_DEF(hash_set, ==)
482 BFN_ASSOC_OP_DEF(hash_set, !=)
483
484 void swap(hash_set &other) noexcept { ABase::swap(other); }
485
491 const ABase &unstable_iterable() const { return static_cast<const ABase &>(*this); }
492};
493
494template <typename Key, typename T, typename Compare = std::less<Key>,
495 typename Allocator = std::allocator<std::pair<const Key, T>>>
496using iterable_map = map<Key, T, Compare, Allocator, Iterable::Yes>;
497
498template <typename Key, typename T, typename Compare = std::less<Key>,
499 typename Allocator = std::allocator<std::pair<const Key, T>>>
500using non_iterable_map = map<Key, T, Compare, Allocator, Iterable::No>;
501
502template <typename Key, typename T, typename Compare = std::less<Key>,
503 typename Allocator = std::allocator<std::pair<const Key, T>>>
505
506template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>>
507using iterable_set = set<Key, Compare, Allocator, Iterable::Yes>;
508
509template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>>
510using non_iterable_set = set<Key, Compare, Allocator, Iterable::No>;
511
512template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>>
514
515// Note that in these functions (which correspond to the ones found in p4c/lib/map.h) the use of
516// unstable_iterable is safe as the get functions are doing just lookups. This function basicaly
517// extracts reference to the underlying standard container from the `assoc::` container wrapper.
518template <class K, class T, class V, class Comp, class Alloc, Iterable It>
519inline V get(const map<K, V, Comp, Alloc, It> &m, T key, V def = V()) {
520 return get(m.unstable_iterable(), key, def);
521}
522
523template <class K, class T, class V, class Comp, class Alloc, Iterable It>
524inline V *getref(map<K, V, Comp, Alloc, It> &m, T key) {
525 return getref(m.unstable_iterable(), key);
526}
527
528template <class K, class T, class V, class Comp, class Alloc, Iterable It>
529inline const V *getref(const map<K, V, Comp, Alloc, It> &m, T key) {
530 return getref(m.unstable_iterable(), key);
531}
532
533template <class K, class T, class V, class Comp, class Alloc, Iterable It>
534inline V get(const map<K, V, Comp, Alloc, It> *m, T key, V def = V()) {
535 return m ? get(*m, key, def) : def;
536}
537
538template <class K, class T, class V, class Comp, class Alloc, Iterable It>
539inline V *getref(map<K, V, Comp, Alloc, It> *m, T key) {
540 return m ? getref(*m, key) : 0;
541}
542
543template <class K, class T, class V, class Comp, class Alloc, Iterable It>
544inline const V *getref(const map<K, V, Comp, Alloc, It> *m, T key) {
545 return m ? getref(*m, key) : 0;
546}
547
548template <typename T>
549std::ostream &operator<<(std::ostream &out, const assoc::set<T> &set) {
550 return format_container(out, set, '(', ')');
551}
552
553template <typename T>
554std::ostream &operator<<(std::ostream &out, const assoc::hash_set<T> &set) {
555 return format_container(out, set, '(', ')');
556}
557
558} // namespace assoc
Definition ordered_map.h:32
Definition ordered_set.h:32
static constexpr bool iterable
Definition assoc.h:135
Definition assoc.h:169
Definition assoc.h:403
const ABase & unstable_iterable() const
Definition assoc.h:447
Definition assoc.h:456
const ABase & unstable_iterable() const
Definition assoc.h:491
Definition assoc.h:300
const std::map< Key, T, Compare, Allocator > & unstable_iterable() const
Definition assoc.h:344
Definition assoc.h:355
const std::set< Key, Compare, Allocator > & unstable_iterable() const
Definition assoc.h:391
std::ostream & format_container(std::ostream &out, const Cont &container, char lbrace, char rbrace)
Definition log.h:164
Definition assoc.h:116
Definition assoc.h:62
Iterable
Definition assoc.h:70
STL namespace.
Definition assoc.h:81
Definition assoc.h:86