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 
262  static ValueReference self(T* t) {
263  static_assert(std::is_base_of<Controllable<T>, T>::value);
264  return ValueReference(t);
265  }
266 
267 private:
277  explicit ValueReference(T* t) : _ptr(t) { static_assert(std::is_base_of<Controllable<T>, T>::value); }
278 
279  const T* _get() const {
280  if ( auto ptr = std::get_if<T*>(&_ptr) )
281  return *ptr;
282 
283  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
284  return (*ptr).get();
285 
287  }
288 
289  T* _get() {
290  if ( auto ptr = std::get_if<T*>(&_ptr) )
291  return *ptr;
292 
293  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
294  return (*ptr).get();
295 
297  }
298 
299  const T* _safeGet() const {
300  assert(_ptr.index() != std::variant_npos);
301 
302  if ( auto ptr = _get() )
303  return ptr;
304 
305  throw NullReference("attempt to access null reference");
306  }
307 
308  T* _safeGet() {
309  assert(_ptr.index() != std::variant_npos);
310 
311  if ( auto ptr = _get() )
312  return ptr;
313 
314  throw NullReference("attempt to access null reference");
315  }
316 
317  std::variant<std::shared_ptr<T>, T*> _ptr;
318 };
319 
327 template<typename T>
328 class StrongReference : public std::shared_ptr<T> {
329 public:
330  using Base = std::shared_ptr<T>;
331 
333  StrongReference() : Base() {}
334 
340  explicit StrongReference(T t) : Base(std::make_shared<T>(std::move(t))) {}
341 
347  explicit StrongReference(const ValueReference<T>& t) : Base(t.asSharedPtr()) {}
348 
350  explicit StrongReference(std::nullptr_t) {}
351 
356  StrongReference(const StrongReference& other) : Base(other) {}
357 
359  StrongReference(StrongReference&& other) noexcept : Base(std::move(other)) {}
360 
364  bool isNull() const { return this->get() == nullptr; }
365 
371 
376  void reset() { Base::operator=(nullptr); }
377 
383  const T& operator*() const {
384  _check();
385  return *this->get(); // NOLINT
386  }
387 
393  T& operator*() {
394  _check();
395  return *this->get(); // NOLINT
396  }
397 
403  const T* operator->() const {
404  _check();
405  return this->get();
406  }
407 
413  T* operator->() {
414  _check();
415  return this->get();
416  }
417 
419  explicit operator bool() const { return ! isNull(); }
420 
428  Base::operator=(std::make_shared<T>(std::move(other)));
429  return *this;
430  }
431 
438  Base::operator=(other.asSharedPtr());
439  return *this;
440  }
441 
444  Base::operator=(other);
445  return *this;
446  }
447 
450  Base::operator=(std::move(other));
451  return *this;
452  }
453 
455  StrongReference& operator=(std::nullptr_t) noexcept {
456  Base::reset();
457  return *this;
458  }
459 
460 private:
461  void _check() const {
462  if ( ! *this )
463  throw NullReference("attempt to access null reference");
464  }
465 };
466 
476 template<typename T>
477 class WeakReference : public std::weak_ptr<T> {
478 public:
479  using Base = std::weak_ptr<T>;
480 
482  WeakReference() = default;
483 
489  explicit WeakReference(const ValueReference<T>& t) : Base(t.asSharedPtr()) {}
490 
496  explicit WeakReference(const StrongReference<T>& t) : Base(t) {}
497 
499  explicit WeakReference(std::nullptr_t) {}
500 
505  WeakReference(const WeakReference& other) = default;
506 
508  WeakReference(WeakReference&& other) noexcept = default;
509 
511  ~WeakReference() = default;
512 
514  bool isNull() const { return this->lock() == nullptr; }
515 
520  bool isExpired() const {
521  auto is_default = ! this->owner_before(Base{}) && ! Base{}.owner_before(*this);
522  return this->expired() && ! is_default;
523  }
524 
529  const T* get() const { return this->lock().get(); }
530 
535  ValueReference<T> derefAsValue() const { return ValueReference<T>(this->lock()); }
536 
538  void reset() { Base::reset(); }
539 
546  const T& operator*() const {
547  _check();
548  return *this->lock();
549  }
550 
557  T& operator*() {
558  _check();
559  return *this->lock();
560  }
561 
568  const T* operator->() const {
569  _check();
570  return this->lock().get();
571  }
572 
579  T* operator->() {
580  _check();
581  return this->lock().get();
582  }
583 
585  explicit operator bool() const { return ! isNull(); }
586 
593  Base::operator=(other.asSharedPtr());
594  return *this;
595  }
596 
603  Base::operator=(other);
604  return *this;
605  }
606 
608  WeakReference& operator=(std::nullptr_t) noexcept {
609  Base::reset();
610  return *this;
611  }
612 
615  Base::operator=(other);
616  return *this;
617  }
618 
620  WeakReference& operator=(WeakReference&& other) noexcept {
621  Base::operator=(std::move(other));
622  return *this;
623  }
624 
625 private:
626  void _check() const {
627  if ( isExpired() )
628  throw ExpiredReference("attempt to access expired reference");
629 
630  if ( isNull() )
631  throw NullReference("attempt to access null reference");
632  }
633 };
634 
641 public:
643  StrongReferenceGeneric() = default;
644 
646  template<typename T>
648 
653  template<typename T>
654  T* as() const {
655  if ( _ptr.empty() )
656  return nullptr;
657 
658  try {
659  return hilti::rt::any_cast<StrongReference<T>>(_ptr).get();
660  } catch ( const hilti::rt::bad_any_cast& ) {
661  throw IllegalReference("invalid target type");
662  }
663  }
664 
666  void reset() { _ptr.clear(); }
667 
668 private:
669  hilti::rt::any _ptr;
670 };
671 
672 namespace reference {
673 
678 template<typename T, typename... Args>
679 StrongReference<T> make_strong(Args&&... args) {
680  return StrongReference<T>(T(std::forward<Args>(args)...));
681 }
682 
687 template<typename T, typename... Args>
688 ValueReference<T> make_value(Args&&... args) {
689  return ValueReference<T>(T(std::forward<Args>(args)...));
690 }
691 
692 } // namespace reference
693 
694 namespace detail::adl {
695 
696 template<typename T>
697 inline std::string to_string(const StrongReference<T>& x, adl::tag /*unused*/) {
698  return x ? hilti::rt::to_string(*x) : "Null";
699 }
700 
701 template<typename T>
702 inline std::string to_string(const WeakReference<T>& x, adl::tag /*unused*/) {
703  if ( x.isExpired() )
704  return "<expired ref>";
705 
706  if ( x.isNull() )
707  return "Null";
708 
709  return hilti::rt::to_string(*x);
710 }
711 
712 template<typename T>
713 inline std::string to_string(const ValueReference<T>& x, adl::tag /*unused*/) {
714  return hilti::rt::to_string(*x);
715 }
716 
717 } // namespace detail::adl
718 
719 // String specialization
720 
721 template<>
722 inline std::string detail::to_string_for_print<StrongReference<std::string>>(const StrongReference<std::string>& x) {
723  return x ? hilti::rt::to_string_for_print(*x) : "Null";
724 }
725 
726 template<>
727 inline std::string detail::to_string_for_print<WeakReference<std::string>>(const WeakReference<std::string>& x) {
728  if ( x.isExpired() )
729  return "<expired ref>";
730 
731  if ( x.isNull() )
732  return "Null";
733 
735 }
736 
737 template<>
738 inline std::string detail::to_string_for_print<ValueReference<std::string>>(const ValueReference<std::string>& x) {
740 }
741 
742 // Bytes specialization
743 
744 template<>
745 inline std::string detail::to_string_for_print<StrongReference<Bytes>>(const StrongReference<Bytes>& x) {
746  return x ? escapeBytes((*x).str(), false) : "Null";
747 }
748 
749 template<>
750 inline std::string detail::to_string_for_print<WeakReference<Bytes>>(const WeakReference<Bytes>& x) {
751  if ( x.isExpired() )
752  return "<expired ref>";
753 
754  if ( x.isNull() )
755  return "Null";
756 
757  return escapeBytes((*x).str(), false);
758 }
759 
760 template<>
761 inline std::string detail::to_string_for_print<ValueReference<Bytes>>(const ValueReference<Bytes>& x) {
762  return escapeBytes((*x).str(), false);
763 }
764 
765 template<typename T>
766 inline std::ostream& operator<<(std::ostream& out, const StrongReference<T>& x) {
767  out << to_string(x);
768  return out;
769 }
770 
771 template<typename T>
772 inline std::ostream& operator<<(std::ostream& out, const ValueReference<T>& x) {
773  out << to_string(x);
774  return out;
775 }
776 
777 template<typename T>
778 inline std::ostream& operator<<(std::ostream& out, const WeakReference<T>& x) {
779  out << to_string(x);
780  return out;
781 }
782 
783 } // namespace hilti::rt
T & operator*()
Definition: reference.h:557
ValueReference(T t)
Definition: reference.h:61
WeakReference(const ValueReference< T > &t)
Definition: reference.h:489
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:376
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:347
Definition: reference.h:640
Definition: reference.h:477
const T * operator->() const
Definition: reference.h:403
ValueReference & operator=(T other)
Definition: reference.h:220
WeakReference & operator=(const ValueReference< T > &other)
Definition: reference.h:592
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:620
StrongReference()
Definition: reference.h:333
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:340
const T & operator*() const
Definition: reference.h:383
ValueReference(std::shared_ptr< T > t)
Definition: reference.h:73
StrongReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:455
StrongReference & operator=(const StrongReference &other)
Definition: reference.h:443
ValueReference(const ValueReference &other)
Definition: reference.h:79
ValueReference< T > derefAsValue() const
Definition: reference.h:535
void reset()
Definition: reference.h:538
T * as() const
Definition: reference.h:654
T & operator*()
Definition: reference.h:393
T * operator->()
Definition: reference.h:579
Definition: reference.h:328
const T & operator*() const
Definition: reference.h:546
void reset()
Definition: reference.h:666
StrongReference(StrongReference &&other) noexcept
Definition: reference.h:359
WeakReference(std::nullptr_t)
Definition: reference.h:499
StrongReference(const StrongReference &other)
Definition: reference.h:356
Definition: reference.h:47
WeakReference & operator=(const WeakReference &other)
Definition: reference.h:614
T * operator->()
Definition: reference.h:413
std::enable_shared_from_this< T > Controllable
Definition: reference.h:20
bool isExpired() const
Definition: reference.h:520
~ValueReference()
Definition: reference.h:90
StrongReference & operator=(T other)
Definition: reference.h:427
WeakReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:608
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:496
bool isNull() const
Definition: reference.h:97
StrongReference & operator=(const ValueReference< T > &other)
Definition: reference.h:437
StrongReferenceGeneric(StrongReference< T > x)
Definition: reference.h:647
ValueReference< T > derefAsValue() const
Definition: reference.h:370
bool isNull() const
Definition: reference.h:514
const T * operator->() const
Definition: reference.h:568
ValueReference & operator=(ValueReference &&other) noexcept
Definition: reference.h:244
StrongReference & operator=(StrongReference &&other) noexcept
Definition: reference.h:449
StrongReference(std::nullptr_t)
Definition: reference.h:350
WeakReference & operator=(const StrongReference< T > &other)
Definition: reference.h:602
bool isNull() const
Definition: reference.h:364