wxMaxima
Loading...
Searching...
No Matches
CellPtr.h
Go to the documentation of this file.
1// -*- mode: c++; c-file-style: "linux"; c-basic-offset: 2; indent-tabs-mode: nil -*-
2//
3// Copyright (C) 2020 Kuba Ober <kuba@bertec.com>
4//
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15//
16// You should have received a copy of the GNU General Public License
17// along with this program; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19//
20// SPDX-License-Identifier: GPL-2.0+
21
55#ifndef CELLPTR_H
56#define CELLPTR_H
57
58#include <wx/debug.h>
59#include <wx/log.h>
60#include <utility>
61#include <cstddef>
62#include <cstdint>
63#include <cinttypes>
64#include <memory>
65#include <type_traits>
66
68#define CELLPTR_CAST_TO_PTR 1
69
71#ifndef CELLPTR_COUNT_INSTANCES
72#define CELLPTR_COUNT_INSTANCES 0
73#endif
74
76#ifndef CELLPTR_LOG_REFS
77#define CELLPTR_LOG_REFS 0
78#endif
79
81#ifndef CELLPTR_LOG_INSTANCES
82#define CELLPTR_LOG_INSTANCES 0
83#endif
84
86#ifndef CELLPTR_LOG_METHOD
87#define CELLPTR_LOG_METHOD wxLogDebug
88#endif
89
90class CellPtrBase;
91
97{
98 class ControlBlock final
99 {
101 Observed *m_object = {};
103 unsigned int m_refCount = 0;
105 static size_t m_instanceCount;
106
107#if CELLPTR_LOG_REFS
108 void LogConstruct(const Observed *) const;
109 void LogRef(const CellPtrBase *) const;
110 void LogDeref(const CellPtrBase *) const;
111 void LogDestruct() const;
112#else
113 static void LogConstruct(const Observed *) {}
114 static void LogRef(const CellPtrBase *) {}
115 static void LogDeref(const CellPtrBase *) {}
116 static void LogDestruct() {}
117#endif
118
119 public:
120 explicit ControlBlock(Observed *object) : m_object(object)
121 {
122#if CELLPTR_COUNT_INSTANCES
123 ++m_instanceCount;
124#endif
125 LogConstruct(object);
126 wxASSERT(object);
127 }
128 ~ControlBlock() {
129#if CELLPTR_COUNT_INSTANCES
130 --m_instanceCount;
131#endif
132 LogDestruct();
133 }
134 ControlBlock(const ControlBlock &) = delete;
135 void operator=(const ControlBlock &) = delete;
136
137 void reset() noexcept { m_object = nullptr; }
138 inline Observed *Get() const noexcept { return m_object; }
139
141 ControlBlock *Ref(const CellPtrBase *cellptr)
142 {
143 LogRef(cellptr);
144 ++m_refCount;
145 return this;
146 }
147
150 bool Deref(const CellPtrBase *cellptr)
151 {
152 LogDeref(cellptr);
153 wxASSERT(m_refCount >= 1);
154 return --m_refCount;
155 }
156
157 static size_t GetLiveInstanceCount() { return m_instanceCount; }
158 };
159
160 static_assert(alignof(ControlBlock) >= 4, "Observed::ControlBlock doesn't have minimum viable alignment");
161
168 class CellPtrImplPointer final
169 {
170 friend void swap(CellPtrImplPointer &a, CellPtrImplPointer &b) noexcept;
171 uintptr_t m_ptr = {};
172 enum Tag : uintptr_t {
173 to_Observed = 0,
174 to_ControlBlock = 1,
175 to_CellPtrBase = 2,
176 to_MASK = 3,
177 to_MASK_OUT = uintptr_t(-intptr_t(to_MASK + 1)),
178 };
179 // TODO: Does this make sense? "| to_Observed" is "| 0". Is this a bug or is
180 // it meant as a readable zero?
181 static uintptr_t ReprFor(Observed *ptr) noexcept
182 { return reinterpret_cast<uintptr_t>(ptr) | to_Observed; }
183 static uintptr_t ReprFor(ControlBlock *ptr) noexcept
184 { return reinterpret_cast<uintptr_t>(ptr) | to_ControlBlock; }
185 static uintptr_t ReprFor(CellPtrBase *ptr) noexcept
186 { return reinterpret_cast<uintptr_t>(ptr) | to_CellPtrBase; }
187 public:
188 constexpr CellPtrImplPointer() noexcept {}
189 constexpr CellPtrImplPointer(const CellPtrImplPointer &) noexcept = default;
190 // cppcheck-suppress noExplicitConstructor
191 constexpr CellPtrImplPointer(decltype(nullptr)) noexcept {}
192 // cppcheck-suppress noExplicitConstructor
193 CellPtrImplPointer(Observed *ptr) noexcept : m_ptr(ReprFor(ptr)) {}
194 // cppcheck-suppress noExplicitConstructor
195 CellPtrImplPointer(ControlBlock *ptr) noexcept : m_ptr(ReprFor(ptr)) {}
196 // cppcheck-suppress noExplicitConstructor
197 CellPtrImplPointer(CellPtrBase *ptr) noexcept : m_ptr(ReprFor(ptr)) {}
198
199 constexpr CellPtrImplPointer &operator=(decltype(nullptr)) noexcept { m_ptr = {}; return *this; }
200 constexpr CellPtrImplPointer &operator=(CellPtrImplPointer o) noexcept { m_ptr = o.m_ptr; return *this; }
201 CellPtrImplPointer &operator=(Observed *ptr) noexcept { m_ptr = ReprFor(ptr); return *this; }
202 CellPtrImplPointer &operator=(ControlBlock *ptr) noexcept { m_ptr = ReprFor(ptr); return *this; }
203 CellPtrImplPointer &operator=(CellPtrBase *ptr) noexcept { m_ptr = ReprFor(ptr); return *this; }
204
205 constexpr explicit inline operator bool() const noexcept { return m_ptr != 0; }
206 constexpr inline bool HasObserved() const noexcept { return (m_ptr & to_MASK) == to_Observed; }
207 constexpr inline bool HasControlBlock() const noexcept { return (m_ptr & to_MASK) == to_ControlBlock; }
208 constexpr inline bool HasCellPtrBase() const noexcept { return (m_ptr & to_MASK) == to_CellPtrBase; }
209 constexpr inline auto GetObserved() const noexcept { static_assert((to_Observed & to_MASK_OUT) == 0, "");
210 return HasObserved() ? reinterpret_cast<Observed *> (m_ptr) : nullptr; }
211 constexpr inline auto GetControlBlock() const noexcept { return HasControlBlock() ? reinterpret_cast<ControlBlock *>(m_ptr & to_MASK_OUT) : nullptr; }
212 constexpr inline auto GetCellPtrBase() const noexcept { return HasCellPtrBase() ? reinterpret_cast<CellPtrBase *> (m_ptr & to_MASK_OUT) : nullptr; }
213
214 constexpr inline auto GetImpl() const noexcept { return m_ptr; }
215 inline auto CastAsObserved() const noexcept { return reinterpret_cast<Observed *>(m_ptr); }
216 inline auto CastAsControlBlock() const noexcept { return reinterpret_cast<ControlBlock *>(m_ptr & to_MASK_OUT); }
217 };
218
219 friend void swap(CellPtrImplPointer &a, CellPtrImplPointer &b) noexcept;
220 friend class CellPtrBase;
221 static size_t m_instanceCount;
222
231 CellPtrImplPointer m_ptr;
232
233 Observed(const Observed &) = delete;
234 void operator=(const Observed &) = delete;
236 void OnEndOfLife() const noexcept;
237
238#if CELLPTR_LOG_REFS
239 void LogRef(const CellPtrBase *) const;
240 void LogDeref(const CellPtrBase *) const;
241#else
242 static void LogRef(const CellPtrBase *) {}
243 static void LogDeref(const CellPtrBase *) {}
244#endif
245
246protected:
247 Observed() noexcept
248#if CELLPTR_COUNT_INSTANCES
249 { ++m_instanceCount; }
250#else
251 = default;
252#endif
253 ~Observed()
254 {
255 if (m_ptr) OnEndOfLife();
256#if CELLPTR_COUNT_INSTANCES
257 --m_instanceCount;
258#endif
259 }
260
261public:
262 static size_t GetLiveInstanceCount() { return m_instanceCount; }
263 static size_t GetLiveControlBlockInstanceCount() { return ControlBlock::GetLiveInstanceCount(); }
264 bool IsNull() const { return !m_ptr.GetImpl(); }
265 bool HasControlBlock() const { return m_ptr.GetControlBlock(); }
266 bool HasOneCellPtr() const { return m_ptr.GetCellPtrBase(); }
267};
268
269inline void swap(Observed::CellPtrImplPointer &a, Observed::CellPtrImplPointer &b) noexcept
270{ std::swap(a.m_ptr, b.m_ptr); }
271
272class Cell;
273class GroupCell;
274
284// cppcheck-suppress ctuOneDefinitionRuleViolation
286{
287 using CellPtrImplPointer = Observed::CellPtrImplPointer;
288 using ControlBlock = Observed::ControlBlock;
289 static size_t m_instanceCount;
290
299 mutable CellPtrImplPointer m_ptr;
300
302 void Ref(Observed *obj);
303
305 void Deref() noexcept;
306
309 decltype(nullptr) DerefControlBlock() const noexcept;
310
311#if CELLPTR_LOG_INSTANCES
312 void LogConstruction(Observed *obj) const;
313 void LogMove(const CellPtrBase &o) const;
314 void LogAssignment(const CellPtrBase &o) const;
315 void LogDestruction() const;
316#else
317 static inline void LogConstruction(Observed *) {}
318 static inline void LogMove(const CellPtrBase &) {}
319 static inline void LogAssignment(const CellPtrBase &) {}
320 static inline void LogDestruction() {}
321#endif
322
323protected:
324 explicit CellPtrBase(Observed *obj = nullptr) noexcept
325 {
326#if CELLPTR_COUNT_INSTANCES
327 ++m_instanceCount;
328#endif
329 if (obj) Ref(obj);
330 LogConstruction(obj);
331 }
332
333 CellPtrBase(const CellPtrBase &o) noexcept : CellPtrBase(o.base_get()) {}
334
335 CellPtrBase(CellPtrBase &&o) noexcept
336 {
337#if CELLPTR_COUNT_INSTANCES
338 ++m_instanceCount;
339#endif
340 LogMove(o);
341 using namespace std;
342
343 auto *thisObserved = m_ptr.GetObserved();
344 if (thisObserved)
345 {
346 wxASSERT(thisObserved->m_ptr.GetCellPtrBase() == this);
347 thisObserved->LogDeref(this);
348 thisObserved->LogRef(&o);
349 thisObserved->m_ptr = &o;
350 }
351
352 auto *otherObserved = o.m_ptr.GetObserved();
353 if (otherObserved)
354 {
355 wxASSERT(otherObserved->m_ptr.GetCellPtrBase() == &o);
356 otherObserved->LogDeref(&o);
357 otherObserved->LogRef(this);
358 otherObserved->m_ptr = this;
359 }
360
361 swap(m_ptr, o.m_ptr);
362 }
363
364 ~CellPtrBase() noexcept
365 {
366#if CELLPTR_COUNT_INSTANCES
367 --m_instanceCount;
368#endif
369 LogDestruction();
370 Deref();
371 }
372
373 CellPtrBase &operator=(const CellPtrBase &o) noexcept
374 {
375 base_reset(o.base_get());
376 return *this;
377 }
378
379 CellPtrBase &operator=(CellPtrBase &&o) noexcept
380 {
381 LogAssignment(o);
382 using namespace std;
383 // In sole-pointer mode the Observed holds a raw back-pointer to the *one*
384 // CellPtr that observes it (see the class invariant). Swapping m_ptr alone
385 // would leave those back-pointers aimed at the wrong CellPtr, so a later
386 // Deref() trips "backPtr == this". Move them along with the swap, exactly as
387 // the move constructor does. (In control-block / null mode GetObserved() is
388 // null and this is a no-op, leaving the plain swap.)
389 auto *thisObserved = m_ptr.GetObserved();
390 auto *otherObserved = o.m_ptr.GetObserved();
391 if (thisObserved)
392 {
393 wxASSERT(thisObserved->m_ptr.GetCellPtrBase() == this);
394 thisObserved->LogDeref(this);
395 thisObserved->LogRef(&o);
396 thisObserved->m_ptr = &o;
397 }
398 if (otherObserved)
399 {
400 wxASSERT(otherObserved->m_ptr.GetCellPtrBase() == &o);
401 otherObserved->LogDeref(&o);
402 otherObserved->LogRef(this);
403 otherObserved->m_ptr = this;
404 }
405 swap(m_ptr, o.m_ptr);
406 return *this;
407 }
408
409 inline Observed *base_get() const noexcept
410 {
411 // Warning: This function is CRITICAL to the performance of wxMaxima
412 // as a whole!
413 //
414 // The common hot path that iterates cells via the m_nextToDraw uses
415 // this function and is intimately tied to its performance. Small
416 // changes here can cause performance regressions - or small performance
417 // improvements.
418 //
419 // If you change anything, do before- and after- measurements to verify
420 // that whatever improvement you sought is in fact achieved. Changes that
421 // don't measurably improve performance are discouraged.
422
423 // The common path, meant to be hot: if we point directly at the observed object,
424 // just return that. This is also where null is returned if the pointer is null.
425 // `HasObserved()` is a simple bitmask test, and `CastAsObserved` is a binary
426 // NO-OP.
427
428 if (m_ptr.HasObserved())
429 return m_ptr.CastAsObserved();
430
431 // Otherwise, we must be pointing to a control block: get the pointed-to
432 // observed from the control block. Since such use case is meant to be
433 // rare, the overhead of pointer chasing (one extra layer of indirection)
434 // is acceptable.
435
436 auto *const observed = m_ptr.CastAsControlBlock()->Get();
437 if (observed)
438 return observed;
439
440 // We have a control block, but the observed object is gone: we dereference
441 // the zombie control block, to deallocate it as soon as possible, and we
442 // reset ourselves to null. This happens only once per observed object, and
443 // subsequent calls will go via the common path.
444
445 return DerefControlBlock(); // returns null - allows a tail call optimization
446 }
447
448 void base_reset(Observed *obj = nullptr) noexcept;
449
450public:
451 template <typename U>
452 static bool constexpr is_pointer() {
453 return std::is_same<U, decltype(nullptr)>::value
454 || (std::is_pointer<U>::value && std::is_convertible<U, Observed*>::value);
455 }
456
457 explicit operator bool() const noexcept { return base_get(); }
458
459 inline void reset() noexcept { base_reset(); }
460
462 auto cmpPointers(const CellPtrBase &o) const noexcept { return m_ptr.GetImpl() - o.m_ptr.GetImpl(); }
463
465 auto cmpObjects(const CellPtrBase &o) const noexcept { return base_get() - o.base_get(); }
466
468 auto cmpObjects(const Observed *o) const noexcept { return base_get() - o; }
469
470 static size_t GetLiveInstanceCount() noexcept { return m_instanceCount; }
471
472 bool IsNull() const { return !m_ptr.GetImpl(); }
473 bool HasOneObserved() const { return m_ptr.GetObserved(); }
474 bool HasControlBlock() const { return m_ptr.GetControlBlock(); }
475};
476
477static_assert(alignof(Observed) >= 4, "Observed doesn't have minimum viable alignment");
478static_assert(alignof(CellPtrBase) >= 4, "CellPtrBase doesn't have minimum viable alignment");
479
509template <typename T>
510class CellPtr final : public CellPtrBase
511{
512 template <typename U>
513 // cppcheck-suppress duplInheritedMember
514 static bool constexpr is_pointer() {
515 return std::is_same<U, decltype(nullptr)>::value
516 || (std::is_pointer<U>::value && std::is_convertible<U, pointer>::value);
517 }
518public:
519 using value_type = T;
520 using pointer = T*;
521 using const_pointer = const T*;
522 using reference = T&;
523
524 CellPtr() noexcept = default;
525
526 // Observers
527 //
528 pointer get() const noexcept;
529 inline reference operator*() const noexcept { return *get(); }
530 inline pointer operator->() const noexcept { return get(); }
531
532#if CELLPTR_CAST_TO_PTR
533 operator pointer() const noexcept { return get(); }
534#endif
535
536 template <typename PtrT, typename std::enable_if<std::is_pointer<PtrT>::value, bool>::type = true>
537 PtrT CastAs() const noexcept;
538
539 // Operations with NULL and integers in general
540 //
541 explicit CellPtr(int) = delete;
542 explicit CellPtr(void *) = delete;
543
544 // Operations with nullptr_t
545 //
546 // cppcheck-suppress duplInheritedMember
547 void reset() noexcept { base_reset(); }
548 explicit CellPtr(decltype(nullptr)) noexcept {}
549 CellPtr &operator=(decltype(nullptr)) noexcept { base_reset(); return *this; }
550 bool operator==(decltype(nullptr)) const noexcept { return !static_cast<bool>(*this); }
551 bool operator!=(decltype(nullptr)) const noexcept { return static_cast<bool>(*this); }
552
553 // Operations with convertible-to-pointer types
554 //
555 template <typename U, typename std::enable_if<is_pointer<U>(), bool>::type = true>
556 explicit CellPtr(U obj) noexcept : CellPtrBase(obj) {}
557
558 template <typename U, typename std::enable_if<is_pointer<U>(), bool>::type = true>
559 CellPtr &operator=(U obj) noexcept
560 {
561 base_reset(obj);
562 return *this;
563 }
564
565 template <typename U, typename std::enable_if<is_pointer<U>(), bool>::type = true>
566 void reset(U obj) noexcept
567 { base_reset(obj); }
568 // Operations with compatible CellPtrs
569 //
570 CellPtr(CellPtr &o) noexcept : CellPtrBase(o) {}
571 CellPtr(CellPtr &&o) noexcept : CellPtrBase(std::move(o)) {}
572 CellPtr &operator=(const CellPtr &o) noexcept { CellPtrBase::operator=(o); return *this; }
573 CellPtr &operator=(CellPtr &&o) noexcept { CellPtrBase::operator=(std::move(o)); return *this; }
574
575 template <typename U,
576 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
577 // cppcheck-suppress noExplicitConstructor
578 CellPtr(CellPtr<U> &&o) noexcept : CellPtrBase(o) {}
579
580 template <typename U,
581 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
582 // cppcheck-suppress noExplicitConstructor
583 CellPtr(const CellPtr<U> &o) noexcept : CellPtrBase(o.get()) {}
584
585 template <typename U,
586 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
587 CellPtr &operator=(CellPtr<U> &&o) noexcept
588 {
589 CellPtrBase::operator=(o);
590 return *this;
591 }
592 template <typename U,
593 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
594 CellPtr &operator=(const CellPtr<U> &o) noexcept
595 {
596 CellPtrBase::operator=(o);
597 return *this;
598 }
599
600#if !CELLPTR_CAST_TO_PTR
601 template <typename U,
602 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
603 bool operator==(const CellPtr<U> &ptr) const noexcept { return cmpControlBlocks(ptr) == 0; }
604 template <typename U,
605 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
606 bool operator!=(const CellPtr<U> &ptr) const noexcept { return cmpControlBlocks(ptr) != 0; }
607 template <typename U,
608 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
609 bool operator<(const CellPtr<U> &ptr) const noexcept { return cmpObjects(ptr) < 0; }
610#endif
611
612 // Operations with compatible unique_ptr
613 //
614 template <typename U, typename Del>
615 explicit CellPtr(std::unique_ptr<U, Del> &&) = delete;
616
617 template <typename U, typename Del,
618 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
619 explicit CellPtr(const std::unique_ptr<U, Del> &ptr) noexcept : CellPtrBase(ptr.get()) {}
620
621 template <typename U, typename Del>
622 CellPtr &operator=(std::unique_ptr<U, Del> &&) = delete;
623
624 template <typename U, typename Del,
625 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
626 CellPtr &operator=(const std::unique_ptr<U, Del> &o) noexcept
627 { return *this = o.get(); }
628};
629
630//
631
632template <typename T> typename
633CellPtr<T>::pointer CellPtr<T>::get() const noexcept { return static_cast<pointer>(base_get()); }
634
639template <>
641
642//
643
644template <typename T, typename U>
645bool operator==(const CellPtr<T> &left, const CellPtr<U> &right) noexcept { return left.cmpPointers(right) == 0; }
646
647#if !CELLPTR_CAST_TO_PTR
648template <typename T, typename U,
649 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
650bool operator==(U left, const CellPtr<T> &right) noexcept { return right.cmpObjects(left) == 0; }
651#endif
652
653template <typename T, typename U,
654 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
655bool operator==(const CellPtr<T> &left, U right) noexcept { return left.cmpObjects(right) == 0; }
656
657template <typename T, typename U>
658bool operator!=(const CellPtr<T> &left, const CellPtr<U> &right) noexcept { return left.cmpPointers(right) != 0; }
659
660#if !CELLPTR_CAST_TO_PTR
661template <typename T, typename U,
662 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
663bool operator!=(U left, const CellPtr<T> &right) noexcept { return right.cmpObjects(left) != 0; }
664#endif
665
666template <typename T, typename U,
667 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
668bool operator!=(const CellPtr<T> &left, U right) noexcept { return left.cmpObjects(right) != 0; }
669
670template <typename T, typename U>
671bool operator<(const CellPtr<T> &left, const CellPtr<U> &right) noexcept { return left.cmpObjects(right) < 0; }
672
673#if !CELLPTR_CAST_TO_PTR
674template <typename T, typename U,
675 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
676bool operator<(U left, const CellPtr<T> &right) noexcept { return right.cmpObjects(left) > 0; }
677#endif
678
679template <typename T, typename U,
680 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
681bool operator<(const CellPtr<T> &left, U right) noexcept { return left.cmpObjects(right) < 0; }
682
683//
684
687template<typename Derived, typename Base>
688std::unique_ptr<Derived> static_unique_ptr_cast(std::unique_ptr<Base>&& p) noexcept
689{
690 auto d = static_cast<Derived *>(p.release());
691 return std::unique_ptr<Derived>(d);
692 // Note: We don't move the deleter, since it's not special.
693}
694
697template<typename Derived, typename Base>
698std::unique_ptr<Derived> dynamic_unique_ptr_cast(std::unique_ptr<Base>&& p) noexcept
699{
700 auto d = dynamic_cast<Derived *>(p.get());
701 if (d)
702 (void) p.release();
703 return std::unique_ptr<Derived>(std::move(d));
704 // Note: We don't move the deleter, since it's not special.
705}
706
707#endif // CELLPTR_H
std::unique_ptr< Derived > static_unique_ptr_cast(std::unique_ptr< Base > &&p) noexcept
A cast for unique pointers, used to downcast to a derived type iff we're certain the cell is indeed o...
Definition: CellPtr.h:688
std::unique_ptr< Derived > dynamic_unique_ptr_cast(std::unique_ptr< Base > &&p) noexcept
A cast for unique pointers, used to downcast to a derived type in a type-safe manner.
Definition: CellPtr.h:698
An implementation detail for the type-specific templated cell pointers.
Definition: CellPtr.h:286
auto cmpPointers(const CellPtrBase &o) const noexcept
This is exactly like the spaceship operator in C++20.
Definition: CellPtr.h:462
void base_reset(Observed *obj=nullptr) noexcept
Definition: CellPtr.cpp:155
auto cmpObjects(const CellPtrBase &o) const noexcept
This is the spaceship operator acting on pointed-to objects.
Definition: CellPtr.h:465
auto cmpObjects(const Observed *o) const noexcept
This is the spaceship operator acting on pointed-to objects.
Definition: CellPtr.h:468
A weak non-owning pointer that becomes null whenever the observed object is destroyed.
Definition: CellPtr.h:511
The base class all cell types the worksheet can consist of are derived from.
Definition: Cell.h:141
A cell grouping input (and, if there is one, also the output) cell to a foldable item.
Definition: GroupCell.h:87
Objects deriving from this class can be observed by the CellPtr.
Definition: CellPtr.h:97
A class that is inherited from the external class Test.
Definition: tag.cpp:5