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 
180  operator const T&() const { return *_safeGet(); }
181 
188  bool operator==(const ValueReference<T>& other) const { return *_safeGet() == *other._safeGet(); }
189 
196  bool operator==(const T& other) const { return *_safeGet() == other; }
197 
204  bool operator!=(const ValueReference<T>& other) const { return *_safeGet() != *other._safeGet(); }
205 
212  bool operator!=(const T& other) const { return *_safeGet() != other; }
213 
221  *_safeGet() = std::move(other);
222  return *this;
223  }
224 
232  if ( &other != this )
233  *_safeGet() = *other._safeGet();
234 
235  return *this;
236  }
237 
245  if ( &other != this ) {
246  // We can't move the actual value as other references may be
247  // referring to it.
248  *_safeGet() = *other._safeGet();
249  other._ptr = nullptr;
250  }
251 
252  return *this;
253  }
254 
260  ValueReference& operator=(std::shared_ptr<T> other) noexcept {
261  if ( _get() != other.get() )
262  _ptr = std::move(other);
263 
264  return *this;
265  }
266 
274  static ValueReference self(T* t) {
275  static_assert(std::is_base_of<Controllable<T>, T>::value);
276  return ValueReference(t);
277  }
278 
279 private:
289  explicit ValueReference(T* t) : _ptr(t) { static_assert(std::is_base_of<Controllable<T>, T>::value); }
290 
291  const T* _get() const {
292  if ( auto ptr = std::get_if<T*>(&_ptr) )
293  return *ptr;
294 
295  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
296  return (*ptr).get();
297 
299  }
300 
301  T* _get() {
302  if ( auto ptr = std::get_if<T*>(&_ptr) )
303  return *ptr;
304 
305  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
306  return (*ptr).get();
307 
309  }
310 
311  const T* _safeGet() const {
312  assert(_ptr.index() != std::variant_npos);
313 
314  if ( auto ptr = _get() )
315  return ptr;
316 
317  throw NullReference("attempt to access null reference");
318  }
319 
320  T* _safeGet() {
321  assert(_ptr.index() != std::variant_npos);
322 
323  if ( auto ptr = _get() )
324  return ptr;
325 
326  throw NullReference("attempt to access null reference");
327  }
328 
329  std::variant<std::shared_ptr<T>, T*> _ptr;
330 };
331 
339 template<typename T>
340 class StrongReference : public std::shared_ptr<T> {
341 public:
342  using Base = std::shared_ptr<T>;
343 
345  StrongReference() : Base() {}
346 
353 
359  explicit StrongReference(T t) : Base(std::make_shared<T>(std::move(t))) {}
360 
362  explicit StrongReference(std::nullptr_t) {}
363 
368  StrongReference(const StrongReference& other) : Base(other) {}
369 
371  StrongReference(StrongReference&& other) noexcept : Base(std::move(other)) {}
372 
376  bool isNull() const { return this->get() == nullptr; }
377 
383 
388  void reset() { Base::operator=(nullptr); }
389 
395  const T& operator*() const {
396  _check();
397  return *this->get(); // NOLINT
398  }
399 
405  T& operator*() {
406  _check();
407  return *this->get(); // NOLINT
408  }
409 
415  const T* operator->() const {
416  _check();
417  return this->get();
418  }
419 
425  T* operator->() {
426  _check();
427  return this->get();
428  }
429 
431  explicit operator bool() const { return ! isNull(); }
432 
440  Base::operator=(std::make_shared<T>(std::move(other)));
441  return *this;
442  }
443 
450  Base::operator=(other.asSharedPtr());
451  return *this;
452  }
453 
456  Base::operator=(other);
457  return *this;
458  }
459 
462  Base::operator=(std::move(other));
463  return *this;
464  }
465 
467  StrongReference& operator=(std::nullptr_t) noexcept {
468  Base::reset();
469  return *this;
470  }
471 
472 private:
473  void _check() const {
474  if ( ! *this )
475  throw NullReference("attempt to access null reference");
476  }
477 };
478 
488 template<typename T>
489 class WeakReference : public std::weak_ptr<T> {
490 public:
491  using Base = std::weak_ptr<T>;
492 
494  WeakReference() = default;
495 
501  explicit WeakReference(const ValueReference<T>& t) : Base(t.asSharedPtr()) {}
502 
508  explicit WeakReference(const StrongReference<T>& t) : Base(t) {}
509 
511  explicit WeakReference(std::nullptr_t) {}
512 
517  WeakReference(const WeakReference& other) = default;
518 
520  WeakReference(WeakReference&& other) noexcept = default;
521 
523  ~WeakReference() = default;
524 
526  bool isNull() const { return this->lock() == nullptr; }
527 
532  bool isExpired() const {
533  auto is_default = ! this->owner_before(Base{}) && ! Base{}.owner_before(*this);
534  return this->expired() && ! is_default;
535  }
536 
541  const T* get() const { return this->lock().get(); }
542 
547  ValueReference<T> derefAsValue() const { return ValueReference<T>(this->lock()); }
548 
550  void reset() { Base::reset(); }
551 
558  const T& operator*() const {
559  _check();
560  return *this->lock();
561  }
562 
569  T& operator*() {
570  _check();
571  return *this->lock();
572  }
573 
580  const T* operator->() const {
581  _check();
582  return this->lock().get();
583  }
584 
591  T* operator->() {
592  _check();
593  return this->lock().get();
594  }
595 
597  explicit operator bool() const { return ! isNull(); }
598 
605  Base::operator=(other.asSharedPtr());
606  return *this;
607  }
608 
615  Base::operator=(other);
616  return *this;
617  }
618 
620  WeakReference& operator=(std::nullptr_t) noexcept {
621  Base::reset();
622  return *this;
623  }
624 
627  Base::operator=(other);
628  return *this;
629  }
630 
632  WeakReference& operator=(WeakReference&& other) noexcept {
633  Base::operator=(std::move(other));
634  return *this;
635  }
636 
637 private:
638  void _check() const {
639  if ( isExpired() )
640  throw ExpiredReference("attempt to access expired reference");
641 
642  if ( isNull() )
643  throw NullReference("attempt to access null reference");
644  }
645 };
646 
653 public:
655  StrongReferenceGeneric() = default;
656 
658  template<typename T>
660 
665  template<typename T>
666  T* as() const {
667  if ( _ptr.empty() )
668  return nullptr;
669 
670  try {
671  return hilti::rt::any_cast<StrongReference<T>>(_ptr).get();
672  } catch ( const hilti::rt::bad_any_cast& ) {
673  throw IllegalReference("invalid target type");
674  }
675  }
676 
678  void reset() { _ptr.clear(); }
679 
680 private:
681  hilti::rt::any _ptr;
682 };
683 
684 namespace reference {
685 
690 template<typename T, typename... Args>
691 StrongReference<T> make_strong(Args&&... args) {
692  return StrongReference<T>(T(std::forward<Args>(args)...));
693 }
694 
699 template<typename T, typename... Args>
700 ValueReference<T> make_value(Args&&... args) {
701  return ValueReference<T>(T(std::forward<Args>(args)...));
702 }
703 
704 } // namespace reference
705 
706 namespace detail::adl {
707 
708 template<typename T>
709 inline std::string to_string(const StrongReference<T>& x, adl::tag /*unused*/) {
710  return x ? hilti::rt::to_string(*x) : "Null";
711 }
712 
713 template<typename T>
714 inline std::string to_string(const WeakReference<T>& x, adl::tag /*unused*/) {
715  if ( x.isExpired() )
716  return "<expired ref>";
717 
718  if ( x.isNull() )
719  return "Null";
720 
721  return hilti::rt::to_string(*x);
722 }
723 
724 template<typename T>
725 inline std::string to_string(const ValueReference<T>& x, adl::tag /*unused*/) {
726  return hilti::rt::to_string(*x);
727 }
728 
729 } // namespace detail::adl
730 
731 // String specialization
732 
733 template<>
734 inline std::string detail::to_string_for_print<StrongReference<std::string>>(const StrongReference<std::string>& x) {
735  return x ? hilti::rt::to_string_for_print(*x) : "Null";
736 }
737 
738 template<>
739 inline std::string detail::to_string_for_print<WeakReference<std::string>>(const WeakReference<std::string>& x) {
740  if ( x.isExpired() )
741  return "<expired ref>";
742 
743  if ( x.isNull() )
744  return "Null";
745 
747 }
748 
749 template<>
750 inline std::string detail::to_string_for_print<ValueReference<std::string>>(const ValueReference<std::string>& x) {
752 }
753 
754 // Bytes specialization
755 
756 template<>
757 inline std::string detail::to_string_for_print<StrongReference<Bytes>>(const StrongReference<Bytes>& x) {
758  return x ? escapeBytes((*x).str(), false) : "Null";
759 }
760 
761 template<>
762 inline std::string detail::to_string_for_print<WeakReference<Bytes>>(const WeakReference<Bytes>& x) {
763  if ( x.isExpired() )
764  return "<expired ref>";
765 
766  if ( x.isNull() )
767  return "Null";
768 
769  return escapeBytes((*x).str(), false);
770 }
771 
772 template<>
773 inline std::string detail::to_string_for_print<ValueReference<Bytes>>(const ValueReference<Bytes>& x) {
774  return escapeBytes((*x).str(), false);
775 }
776 
777 template<typename T>
778 inline std::ostream& operator<<(std::ostream& out, const StrongReference<T>& x) {
779  out << to_string(x);
780  return out;
781 }
782 
783 template<typename T>
784 inline std::ostream& operator<<(std::ostream& out, const ValueReference<T>& x) {
785  out << to_string(x);
786  return out;
787 }
788 
789 template<typename T>
790 inline std::ostream& operator<<(std::ostream& out, const WeakReference<T>& x) {
791  out << to_string(x);
792  return out;
793 }
794 
795 } // namespace hilti::rt
T & operator*()
Definition: reference.h:569
ValueReference(T t)
Definition: reference.h:61
WeakReference(const ValueReference< T > &t)
Definition: reference.h:501
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:388
bool operator==(const ValueReference< T > &other) const
Definition: reference.h:188
const T * operator->() const
Definition: reference.h:166
StrongReference(const ValueReference< T > &t)
Definition: reference.h:352
Definition: reference.h:652
Definition: reference.h:489
const T * operator->() const
Definition: reference.h:415
ValueReference & operator=(T other)
Definition: reference.h:220
WeakReference & operator=(const ValueReference< T > &other)
Definition: reference.h:604
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:632
StrongReference()
Definition: reference.h:345
bool operator!=(const T &other) const
Definition: reference.h:212
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:204
StrongReference(T t)
Definition: reference.h:359
const T & operator*() const
Definition: reference.h:395
ValueReference(std::shared_ptr< T > t)
Definition: reference.h:73
StrongReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:467
ValueReference & operator=(std::shared_ptr< T > other) noexcept
Definition: reference.h:260
StrongReference & operator=(const StrongReference &other)
Definition: reference.h:455
ValueReference(const ValueReference &other)
Definition: reference.h:79
ValueReference< T > derefAsValue() const
Definition: reference.h:547
void reset()
Definition: reference.h:550
T * as() const
Definition: reference.h:666
T & operator*()
Definition: reference.h:405
T * operator->()
Definition: reference.h:591
Definition: reference.h:340
const T & operator*() const
Definition: reference.h:558
void reset()
Definition: reference.h:678
StrongReference(StrongReference &&other) noexcept
Definition: reference.h:371
WeakReference(std::nullptr_t)
Definition: reference.h:511
StrongReference(const StrongReference &other)
Definition: reference.h:368
Definition: reference.h:47
WeakReference & operator=(const WeakReference &other)
Definition: reference.h:626
T * operator->()
Definition: reference.h:425
std::enable_shared_from_this< T > Controllable
Definition: reference.h:20
bool isExpired() const
Definition: reference.h:532
~ValueReference()
Definition: reference.h:90
StrongReference & operator=(T other)
Definition: reference.h:439
WeakReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:620
void reset()
Definition: reference.h:145
ValueReference & operator=(const ValueReference &other)
Definition: reference.h:231
bool operator==(const T &other) const
Definition: reference.h:196
const T & operator*() const
Definition: reference.h:152
WeakReference(const StrongReference< T > &t)
Definition: reference.h:508
bool isNull() const
Definition: reference.h:97
StrongReference & operator=(const ValueReference< T > &other)
Definition: reference.h:449
StrongReferenceGeneric(StrongReference< T > x)
Definition: reference.h:659
ValueReference< T > derefAsValue() const
Definition: reference.h:382
bool isNull() const
Definition: reference.h:526
const T * operator->() const
Definition: reference.h:580
ValueReference & operator=(ValueReference &&other) noexcept
Definition: reference.h:244
StrongReference & operator=(StrongReference &&other) noexcept
Definition: reference.h:461
StrongReference(std::nullptr_t)
Definition: reference.h:362
WeakReference & operator=(const StrongReference< T > &other)
Definition: reference.h:614
bool isNull() const
Definition: reference.h:376