Spicy
reference.h
1 // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <memory>
6 #include <string>
7 #include <utility>
8 
9 #include <hilti/rt/any.h>
10 #include <hilti/rt/extension-points.h>
11 #include <hilti/rt/types/bytes.h>
12 #include <hilti/rt/types/string.h>
13 #include <hilti/rt/types/struct.h>
14 #include <hilti/rt/util.h>
15 
16 namespace hilti::rt {
17 
19 template<typename T>
20 using Controllable = std::enable_shared_from_this<T>;
21 
46 template<typename T>
48 public:
53  ValueReference() : _ptr(std::make_shared<T>()) {}
54 
61  ValueReference(T t) : _ptr(std::make_shared<T>(std::move(t))) {}
62 
73  explicit ValueReference(std::shared_ptr<T> t) : _ptr(std::move(t)) {}
74 
80  if ( auto ptr = other._get() )
81  _ptr = std::make_shared<T>(*ptr);
82  else
83  _ptr = std::shared_ptr<T>();
84  }
85 
87  ValueReference(ValueReference&& other) noexcept = default;
88 
91 
97  bool isNull() const {
98  assert(_ptr.index() != std::variant_npos);
99  return _get() == nullptr;
100  }
101 
106  const T* get() const { return _get(); }
107 
121  std::shared_ptr<T> asSharedPtr() const {
122  assert(_ptr.index() != std::variant_npos);
123 
124  if ( auto x = std::get_if<std::shared_ptr<T>>(&_ptr) )
125  return *x;
126 
127  try {
128  if ( auto ptr = std::get<T*>(_ptr) ) {
129  if constexpr ( std::is_base_of<Controllable<T>, T>::value )
130  return ptr->shared_from_this();
131  else
132  throw IllegalReference("cannot dynamically create reference for type");
133  }
134  else
135  throw IllegalReference("unexpected state of value reference");
136  } catch ( const std::bad_weak_ptr& ) {
137  throw IllegalReference("reference to non-heap instance");
138  }
139  }
140 
145  void reset() { _ptr = std::shared_ptr<T>(); }
146 
152  const T& operator*() const { return *_safeGet(); }
153 
159  T& operator*() { return *_safeGet(); }
160 
166  const T* operator->() const { return _safeGet(); }
167 
173  T* operator->() { return _safeGet(); }
174 
181  bool operator==(const ValueReference<T>& other) const { return *_safeGet() == *other._safeGet(); }
182 
189  bool operator==(const T& other) const { return *_safeGet() == other; }
190 
197  bool operator!=(const ValueReference<T>& other) const { return *_safeGet() != *other._safeGet(); }
198 
205  bool operator!=(const T& other) const { return *_safeGet() != other; }
206 
214  *_safeGet() = std::move(other);
215  return *this;
216  }
217 
225  if ( &other != this )
226  *_safeGet() = *other._safeGet();
227 
228  return *this;
229  }
230 
238  if ( &other != this ) {
239  // We can't move the actual value as other references may be
240  // referring to it.
241  *_safeGet() = *other._safeGet();
242  other._ptr = nullptr;
243  }
244 
245  return *this;
246  }
247 
255  static ValueReference self(T* t) {
256  static_assert(std::is_base_of<Controllable<T>, T>::value);
257  return ValueReference(t);
258  }
259 
260 private:
270  explicit ValueReference(T* t) : _ptr(t) { static_assert(std::is_base_of<Controllable<T>, T>::value); }
271 
272  const T* _get() const {
273  if ( auto ptr = std::get_if<T*>(&_ptr) )
274  return *ptr;
275 
276  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
277  return (*ptr).get();
278 
280  }
281 
282  T* _get() {
283  if ( auto ptr = std::get_if<T*>(&_ptr) )
284  return *ptr;
285 
286  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
287  return (*ptr).get();
288 
290  }
291 
292  const T* _safeGet() const {
293  assert(_ptr.index() != std::variant_npos);
294 
295  if ( auto ptr = _get() )
296  return ptr;
297 
298  throw NullReference("attempt to access null reference");
299  }
300 
301  T* _safeGet() {
302  assert(_ptr.index() != std::variant_npos);
303 
304  if ( auto ptr = _get() )
305  return ptr;
306 
307  throw NullReference("attempt to access null reference");
308  }
309 
310  std::variant<std::shared_ptr<T>, T*> _ptr;
311 };
312 
320 template<typename T>
321 class StrongReference : public std::shared_ptr<T> {
322 public:
323  using Base = std::shared_ptr<T>;
324 
326  StrongReference() : Base() {}
327 
333  explicit StrongReference(T t) : Base(std::make_shared<T>(std::move(t))) {}
334 
340  explicit StrongReference(const ValueReference<T>& t) : Base(t.asSharedPtr()) {}
341 
343  explicit StrongReference(std::nullptr_t) {}
344 
349  StrongReference(const StrongReference& other) : Base(other) {}
350 
352  StrongReference(StrongReference&& other) noexcept : Base(std::move(other)) {}
353 
357  bool isNull() const { return this->get() == nullptr; }
358 
364 
369  void reset() { Base::operator=(nullptr); }
370 
376  const T& operator*() const {
377  _check();
378  return *this->get(); // NOLINT
379  }
380 
386  T& operator*() {
387  _check();
388  return *this->get(); // NOLINT
389  }
390 
396  const T* operator->() const {
397  _check();
398  return this->get();
399  }
400 
406  T* operator->() {
407  _check();
408  return this->get();
409  }
410 
412  explicit operator bool() const { return ! isNull(); }
413 
421  Base::operator=(std::make_shared<T>(std::move(other)));
422  return *this;
423  }
424 
431  Base::operator=(other.asSharedPtr());
432  return *this;
433  }
434 
437  Base::operator=(other);
438  return *this;
439  }
440 
443  Base::operator=(std::move(other));
444  return *this;
445  }
446 
448  StrongReference& operator=(std::nullptr_t) noexcept {
449  Base::reset();
450  return *this;
451  }
452 
453 private:
454  void _check() const {
455  if ( ! *this )
456  throw NullReference("attempt to access null reference");
457  }
458 };
459 
469 template<typename T>
470 class WeakReference : public std::weak_ptr<T> {
471 public:
472  using Base = std::weak_ptr<T>;
473 
475  WeakReference() = default;
476 
482  explicit WeakReference(const ValueReference<T>& t) : Base(t.asSharedPtr()) {}
483 
489  explicit WeakReference(const StrongReference<T>& t) : Base(t) {}
490 
492  explicit WeakReference(std::nullptr_t) {}
493 
498  WeakReference(const WeakReference& other) = default;
499 
501  WeakReference(WeakReference&& other) noexcept = default;
502 
504  ~WeakReference() = default;
505 
507  bool isNull() const { return this->lock() == nullptr; }
508 
513  bool isExpired() const {
514  auto is_default = ! this->owner_before(Base{}) && ! Base{}.owner_before(*this);
515  return this->expired() && ! is_default;
516  }
517 
522  const T* get() const { return this->lock().get(); }
523 
528  ValueReference<T> derefAsValue() const { return ValueReference<T>(this->lock()); }
529 
531  void reset() { Base::reset(); }
532 
539  const T& operator*() const {
540  _check();
541  return *this->lock();
542  }
543 
550  T& operator*() {
551  _check();
552  return *this->lock();
553  }
554 
561  const T* operator->() const {
562  _check();
563  return this->lock().get();
564  }
565 
572  T* operator->() {
573  _check();
574  return this->lock().get();
575  }
576 
578  explicit operator bool() const { return ! isNull(); }
579 
586  Base::operator=(other.asSharedPtr());
587  return *this;
588  }
589 
596  Base::operator=(other);
597  return *this;
598  }
599 
601  WeakReference& operator=(std::nullptr_t) noexcept {
602  Base::reset();
603  return *this;
604  }
605 
608  Base::operator=(other);
609  return *this;
610  }
611 
613  WeakReference& operator=(WeakReference&& other) noexcept {
614  Base::operator=(std::move(other));
615  return *this;
616  }
617 
618 private:
619  void _check() const {
620  if ( isExpired() )
621  throw ExpiredReference("attempt to access expired reference");
622 
623  if ( isNull() )
624  throw NullReference("attempt to access null reference");
625  }
626 };
627 
634 public:
636  StrongReferenceGeneric() = default;
637 
639  template<typename T>
641 
646  template<typename T>
647  T* as() const {
648  if ( _ptr.empty() )
649  return nullptr;
650 
651  try {
652  return hilti::rt::any_cast<StrongReference<T>>(_ptr).get();
653  } catch ( const hilti::rt::bad_any_cast& ) {
654  throw IllegalReference("invalid target type");
655  }
656  }
657 
659  void reset() { _ptr.clear(); }
660 
661 private:
662  hilti::rt::any _ptr;
663 };
664 
665 namespace reference {
666 
671 template<typename T, typename... Args>
672 StrongReference<T> make_strong(Args&&... args) {
673  return StrongReference<T>(T(std::forward<Args>(args)...));
674 }
675 
680 template<typename T, typename... Args>
681 ValueReference<T> make_value(Args&&... args) {
682  return ValueReference<T>(T(std::forward<Args>(args)...));
683 }
684 
685 } // namespace reference
686 
687 namespace detail::adl {
688 
689 template<typename T>
690 inline std::string to_string(const StrongReference<T>& x, adl::tag /*unused*/) {
691  return x ? hilti::rt::to_string(*x) : "Null";
692 }
693 
694 template<typename T>
695 inline std::string to_string(const WeakReference<T>& x, adl::tag /*unused*/) {
696  if ( x.isExpired() )
697  return "<expired ref>";
698 
699  if ( x.isNull() )
700  return "Null";
701 
702  return hilti::rt::to_string(*x);
703 }
704 
705 template<typename T>
706 inline std::string to_string(const ValueReference<T>& x, adl::tag /*unused*/) {
707  return hilti::rt::to_string(*x);
708 }
709 
710 } // namespace detail::adl
711 
712 // String specialization
713 
714 template<>
715 inline std::string detail::to_string_for_print<StrongReference<std::string>>(const StrongReference<std::string>& x) {
716  return x ? hilti::rt::to_string_for_print(*x) : "Null";
717 }
718 
719 template<>
720 inline std::string detail::to_string_for_print<WeakReference<std::string>>(const WeakReference<std::string>& x) {
721  if ( x.isExpired() )
722  return "<expired ref>";
723 
724  if ( x.isNull() )
725  return "Null";
726 
728 }
729 
730 template<>
731 inline std::string detail::to_string_for_print<ValueReference<std::string>>(const ValueReference<std::string>& x) {
733 }
734 
735 // Bytes specialization
736 
737 template<>
738 inline std::string detail::to_string_for_print<StrongReference<Bytes>>(const StrongReference<Bytes>& x) {
739  return x ? escapeBytes((*x).str(), false) : "Null";
740 }
741 
742 template<>
743 inline std::string detail::to_string_for_print<WeakReference<Bytes>>(const WeakReference<Bytes>& x) {
744  if ( x.isExpired() )
745  return "<expired ref>";
746 
747  if ( x.isNull() )
748  return "Null";
749 
750  return escapeBytes((*x).str(), false);
751 }
752 
753 template<>
754 inline std::string detail::to_string_for_print<ValueReference<Bytes>>(const ValueReference<Bytes>& x) {
755  return escapeBytes((*x).str(), false);
756 }
757 
758 template<typename T>
759 inline std::ostream& operator<<(std::ostream& out, const StrongReference<T>& x) {
760  out << to_string(x);
761  return out;
762 }
763 
764 template<typename T>
765 inline std::ostream& operator<<(std::ostream& out, const ValueReference<T>& x) {
766  out << to_string(x);
767  return out;
768 }
769 
770 template<typename T>
771 inline std::ostream& operator<<(std::ostream& out, const WeakReference<T>& x) {
772  out << to_string(x);
773  return out;
774 }
775 
776 } // namespace hilti::rt
T & operator*()
Definition: reference.h:550
ValueReference(T t)
Definition: reference.h:61
WeakReference(const ValueReference< T > &t)
Definition: reference.h:482
std::string to_string(T &&x)
Definition: extension-points.h:26
std::string to_string_for_print(const T &x)
Definition: extension-points.h:45
std::shared_ptr< T > asSharedPtr() const
Definition: reference.h:121
void reset()
Definition: reference.h:369
bool operator==(const ValueReference< T > &other) const
Definition: reference.h:181
const T * operator->() const
Definition: reference.h:166
StrongReference(const ValueReference< T > &t)
Definition: reference.h:340
Definition: reference.h:633
Definition: reference.h:470
const T * operator->() const
Definition: reference.h:396
ValueReference & operator=(T other)
Definition: reference.h:213
WeakReference & operator=(const ValueReference< T > &other)
Definition: reference.h:585
Definition: any.h:7
T & operator*()
Definition: reference.h:159
Definition: optional.h:79
ValueReference()
Definition: reference.h:53
WeakReference & operator=(WeakReference &&other) noexcept
Definition: reference.h:613
StrongReference()
Definition: reference.h:326
bool operator!=(const T &other) const
Definition: reference.h:205
T * operator->()
Definition: reference.h:173
void cannot_be_reached() __attribute__((noreturn))
Definition: util.cc:52
bool operator!=(const ValueReference< T > &other) const
Definition: reference.h:197
StrongReference(T t)
Definition: reference.h:333
const T & operator*() const
Definition: reference.h:376
ValueReference(std::shared_ptr< T > t)
Definition: reference.h:73
StrongReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:448
StrongReference & operator=(const StrongReference &other)
Definition: reference.h:436
ValueReference(const ValueReference &other)
Definition: reference.h:79
ValueReference< T > derefAsValue() const
Definition: reference.h:528
void reset()
Definition: reference.h:531
T * as() const
Definition: reference.h:647
T & operator*()
Definition: reference.h:386
T * operator->()
Definition: reference.h:572
Definition: reference.h:321
const T & operator*() const
Definition: reference.h:539
void reset()
Definition: reference.h:659
StrongReference(StrongReference &&other) noexcept
Definition: reference.h:352
WeakReference(std::nullptr_t)
Definition: reference.h:492
StrongReference(const StrongReference &other)
Definition: reference.h:349
Definition: reference.h:47
WeakReference & operator=(const WeakReference &other)
Definition: reference.h:607
T * operator->()
Definition: reference.h:406
std::enable_shared_from_this< T > Controllable
Definition: reference.h:20
bool isExpired() const
Definition: reference.h:513
~ValueReference()
Definition: reference.h:90
StrongReference & operator=(T other)
Definition: reference.h:420
WeakReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:601
void reset()
Definition: reference.h:145
ValueReference & operator=(const ValueReference &other)
Definition: reference.h:224
bool operator==(const T &other) const
Definition: reference.h:189
const T & operator*() const
Definition: reference.h:152
WeakReference(const StrongReference< T > &t)
Definition: reference.h:489
bool isNull() const
Definition: reference.h:97
StrongReference & operator=(const ValueReference< T > &other)
Definition: reference.h:430
StrongReferenceGeneric(StrongReference< T > x)
Definition: reference.h:640
ValueReference< T > derefAsValue() const
Definition: reference.h:363
bool isNull() const
Definition: reference.h:507
const T * operator->() const
Definition: reference.h:561
ValueReference & operator=(ValueReference &&other) noexcept
Definition: reference.h:237
StrongReference & operator=(StrongReference &&other) noexcept
Definition: reference.h:442
StrongReference(std::nullptr_t)
Definition: reference.h:343
WeakReference & operator=(const StrongReference< T > &other)
Definition: reference.h:595
bool isNull() const
Definition: reference.h:357