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 
243  if ( &other != this ) {
244  // We can't move the actual value as other references may be
245  // referring to it.
246  *_get() = *other._get();
247 
248  // Some implementations for `std::variant` do not have a `noexpect`
249  // move assignment operator.
250  try {
251  other._ptr = nullptr;
252  } catch ( ... ) {
254  }
255  }
256 
257  return *this;
258  }
259 
265  ValueReference& operator=(std::shared_ptr<T> other) noexcept {
266  if ( _get() != other.get() )
267  _ptr = std::move(other);
268 
269  return *this;
270  }
271 
279  static ValueReference self(T* t) {
280  static_assert(std::is_base_of<Controllable<T>, T>::value);
281  return ValueReference(t);
282  }
283 
284 private:
294  explicit ValueReference(T* t) : _ptr(t) { static_assert(std::is_base_of<Controllable<T>, T>::value); }
295 
296  const T* _get() const noexcept {
297  if ( auto ptr = std::get_if<T*>(&_ptr) )
298  return *ptr;
299 
300  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
301  return (*ptr).get();
302 
304  }
305 
306  T* _get() noexcept {
307  if ( auto ptr = std::get_if<T*>(&_ptr) )
308  return *ptr;
309 
310  if ( auto ptr = std::get_if<std::shared_ptr<T>>(&_ptr) )
311  return (*ptr).get();
312 
314  }
315 
316  const T* _safeGet() const {
317  assert(_ptr.index() != std::variant_npos);
318 
319  if ( auto ptr = _get() )
320  return ptr;
321 
322  throw NullReference("attempt to access null reference");
323  }
324 
325  T* _safeGet() {
326  assert(_ptr.index() != std::variant_npos);
327 
328  if ( auto ptr = _get() )
329  return ptr;
330 
331  throw NullReference("attempt to access null reference");
332  }
333 
334  std::variant<std::shared_ptr<T>, T*> _ptr;
335 };
336 
344 template<typename T>
345 class StrongReference : public std::shared_ptr<T> {
346 public:
347  using Base = std::shared_ptr<T>;
348 
350  StrongReference() : Base() {}
351 
358 
364  explicit StrongReference(T t) : Base(std::make_shared<T>(std::move(t))) {}
365 
367  explicit StrongReference(std::nullptr_t) {}
368 
373  StrongReference(const StrongReference& other) : Base(other) {}
374 
376  StrongReference(StrongReference&& other) noexcept : Base(std::move(other)) {}
377 
381  bool isNull() const { return this->get() == nullptr; }
382 
388 
393  void reset() { Base::operator=(nullptr); }
394 
400  const T& operator*() const {
401  _check();
402  return *this->get(); // NOLINT
403  }
404 
410  T& operator*() {
411  _check();
412  return *this->get(); // NOLINT
413  }
414 
420  const T* operator->() const {
421  _check();
422  return this->get();
423  }
424 
430  T* operator->() {
431  _check();
432  return this->get();
433  }
434 
436  explicit operator bool() const { return ! isNull(); }
437 
445  Base::operator=(std::make_shared<T>(std::move(other)));
446  return *this;
447  }
448 
455  Base::operator=(other.asSharedPtr());
456  return *this;
457  }
458 
461  if ( &other == this )
462  return *this;
463 
464  Base::operator=(other);
465  return *this;
466  }
467 
470  Base::operator=(std::move(other));
471  return *this;
472  }
473 
475  StrongReference& operator=(std::nullptr_t) noexcept {
476  Base::reset();
477  return *this;
478  }
479 
480 private:
481  void _check() const {
482  if ( ! *this )
483  throw NullReference("attempt to access null reference");
484  }
485 };
486 
496 template<typename T>
497 class WeakReference : public std::weak_ptr<T> {
498 public:
499  using Base = std::weak_ptr<T>;
500 
502  WeakReference() = default;
503 
509  explicit WeakReference(const ValueReference<T>& t) : Base(t.asSharedPtr()) {}
510 
516  explicit WeakReference(const StrongReference<T>& t) : Base(t) {}
517 
519  explicit WeakReference(std::nullptr_t) {}
520 
525  WeakReference(const WeakReference& other) = default;
526 
528  WeakReference(WeakReference&& other) noexcept = default;
529 
531  ~WeakReference() = default;
532 
534  bool isNull() const { return this->lock() == nullptr; }
535 
540  bool isExpired() const {
541  auto is_default = ! this->owner_before(Base{}) && ! Base{}.owner_before(*this);
542  return this->expired() && ! is_default;
543  }
544 
549  const T* get() const { return this->lock().get(); }
550 
555  ValueReference<T> derefAsValue() const { return ValueReference<T>(this->lock()); }
556 
558  void reset() { Base::reset(); }
559 
566  const T& operator*() const {
567  _check();
568  return *this->lock();
569  }
570 
577  T& operator*() {
578  _check();
579  return *this->lock();
580  }
581 
588  const T* operator->() const {
589  _check();
590  return this->lock().get();
591  }
592 
599  T* operator->() {
600  _check();
601  return this->lock().get();
602  }
603 
605  explicit operator bool() const { return ! isNull(); }
606 
613  Base::operator=(other.asSharedPtr());
614  return *this;
615  }
616 
623  Base::operator=(other);
624  return *this;
625  }
626 
628  WeakReference& operator=(std::nullptr_t) noexcept {
629  Base::reset();
630  return *this;
631  }
632 
635  if ( &other == this )
636  return *this;
637 
638  Base::operator=(other);
639  return *this;
640  }
641 
643  WeakReference& operator=(WeakReference&& other) noexcept {
644  Base::operator=(std::move(other));
645  return *this;
646  }
647 
648 private:
649  void _check() const {
650  if ( isExpired() )
651  throw ExpiredReference("attempt to access expired reference");
652 
653  if ( isNull() )
654  throw NullReference("attempt to access null reference");
655  }
656 };
657 
664 public:
666  StrongReferenceGeneric() = default;
667 
669  template<typename T>
671 
676  template<typename T>
677  T* as() const {
678  if ( _ptr.empty() )
679  return nullptr;
680 
681  try {
682  return hilti::rt::any_cast<StrongReference<T>>(_ptr).get();
683  } catch ( const hilti::rt::bad_any_cast& ) {
684  throw IllegalReference("invalid target type");
685  }
686  }
687 
689  void reset() { _ptr.clear(); }
690 
691 private:
692  hilti::rt::any _ptr;
693 };
694 
695 namespace reference {
696 
701 template<typename T, typename... Args>
702 StrongReference<T> make_strong(Args&&... args) {
703  return StrongReference<T>(T(std::forward<Args>(args)...));
704 }
705 
710 template<typename T, typename... Args>
711 ValueReference<T> make_value(Args&&... args) {
712  return ValueReference<T>(T(std::forward<Args>(args)...));
713 }
714 
715 } // namespace reference
716 
717 namespace detail::adl {
718 
719 template<typename T>
720 inline std::string to_string(const StrongReference<T>& x, adl::tag /*unused*/) {
721  return x ? hilti::rt::to_string(*x) : "Null";
722 }
723 
724 template<typename T>
725 inline std::string to_string(const WeakReference<T>& x, adl::tag /*unused*/) {
726  if ( x.isExpired() )
727  return "<expired ref>";
728 
729  if ( x.isNull() )
730  return "Null";
731 
732  return hilti::rt::to_string(*x);
733 }
734 
735 template<typename T>
736 inline std::string to_string(const ValueReference<T>& x, adl::tag /*unused*/) {
737  return hilti::rt::to_string(*x);
738 }
739 
740 } // namespace detail::adl
741 
742 // String specialization
743 
744 template<>
745 inline std::string detail::to_string_for_print<StrongReference<std::string>>(const StrongReference<std::string>& x) {
746  return x ? hilti::rt::to_string_for_print(*x) : "Null";
747 }
748 
749 template<>
750 inline std::string detail::to_string_for_print<WeakReference<std::string>>(const WeakReference<std::string>& x) {
751  if ( x.isExpired() )
752  return "<expired ref>";
753 
754  if ( x.isNull() )
755  return "Null";
756 
758 }
759 
760 template<>
761 inline std::string detail::to_string_for_print<ValueReference<std::string>>(const ValueReference<std::string>& x) {
763 }
764 
765 // Bytes specialization
766 
767 template<>
768 inline std::string detail::to_string_for_print<StrongReference<Bytes>>(const StrongReference<Bytes>& x) {
769  return x ? escapeBytes((*x).str(), false) : "Null";
770 }
771 
772 template<>
773 inline std::string detail::to_string_for_print<WeakReference<Bytes>>(const WeakReference<Bytes>& x) {
774  if ( x.isExpired() )
775  return "<expired ref>";
776 
777  if ( x.isNull() )
778  return "Null";
779 
780  return escapeBytes((*x).str(), false);
781 }
782 
783 template<>
784 inline std::string detail::to_string_for_print<ValueReference<Bytes>>(const ValueReference<Bytes>& x) {
785  return escapeBytes((*x).str(), false);
786 }
787 
788 template<typename T>
789 inline std::ostream& operator<<(std::ostream& out, const StrongReference<T>& x) {
790  out << to_string(x);
791  return out;
792 }
793 
794 template<typename T>
795 inline std::ostream& operator<<(std::ostream& out, const ValueReference<T>& x) {
796  out << to_string(x);
797  return out;
798 }
799 
800 template<typename T>
801 inline std::ostream& operator<<(std::ostream& out, const WeakReference<T>& x) {
802  out << to_string(x);
803  return out;
804 }
805 
806 } // namespace hilti::rt
T & operator*()
Definition: reference.h:577
ValueReference(T t)
Definition: reference.h:61
WeakReference(const ValueReference< T > &t)
Definition: reference.h:509
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:393
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:357
Definition: reference.h:663
Definition: reference.h:497
const T * operator->() const
Definition: reference.h:420
ValueReference & operator=(T other)
Definition: reference.h:220
WeakReference & operator=(const ValueReference< T > &other)
Definition: reference.h:612
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:643
StrongReference()
Definition: reference.h:350
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:364
const T & operator*() const
Definition: reference.h:400
ValueReference(std::shared_ptr< T > t)
Definition: reference.h:73
StrongReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:475
ValueReference & operator=(std::shared_ptr< T > other) noexcept
Definition: reference.h:265
StrongReference & operator=(const StrongReference &other)
Definition: reference.h:460
ValueReference(const ValueReference &other)
Definition: reference.h:79
ValueReference< T > derefAsValue() const
Definition: reference.h:555
void reset()
Definition: reference.h:558
T * as() const
Definition: reference.h:677
T & operator*()
Definition: reference.h:410
T * operator->()
Definition: reference.h:599
Definition: reference.h:345
const T & operator*() const
Definition: reference.h:566
void reset()
Definition: reference.h:689
StrongReference(StrongReference &&other) noexcept
Definition: reference.h:376
WeakReference(std::nullptr_t)
Definition: reference.h:519
StrongReference(const StrongReference &other)
Definition: reference.h:373
Definition: reference.h:47
WeakReference & operator=(const WeakReference &other)
Definition: reference.h:634
T * operator->()
Definition: reference.h:430
std::enable_shared_from_this< T > Controllable
Definition: reference.h:20
bool isExpired() const
Definition: reference.h:540
~ValueReference()
Definition: reference.h:90
StrongReference & operator=(T other)
Definition: reference.h:444
WeakReference & operator=(std::nullptr_t) noexcept
Definition: reference.h:628
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:516
bool isNull() const
Definition: reference.h:97
StrongReference & operator=(const ValueReference< T > &other)
Definition: reference.h:454
StrongReferenceGeneric(StrongReference< T > x)
Definition: reference.h:670
ValueReference< T > derefAsValue() const
Definition: reference.h:387
bool isNull() const
Definition: reference.h:534
const T * operator->() const
Definition: reference.h:588
ValueReference & operator=(ValueReference &&other) noexcept
Definition: reference.h:242
StrongReference & operator=(StrongReference &&other) noexcept
Definition: reference.h:469
StrongReference(std::nullptr_t)
Definition: reference.h:367
WeakReference & operator=(const StrongReference< T > &other)
Definition: reference.h:622
bool isNull() const
Definition: reference.h:381