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 // Every Cell's m_group is a CellPtr<GroupCell>, and GetGroup() -- called
415 // throughout layout, drawing and hit-testing -- goes through this
416 // function, so it is intimately tied to overall performance. Small
417 // changes here can cause performance regressions - or small performance
418 // improvements. (The draw list used to be a second CellPtr, m_nextToDraw,
419 // present on every cell; it's computed on the fly now instead, see
420 // CellIterators.h, so this is no longer its hot path too.)
421 //
422 // If you change anything, do before- and after- measurements to verify
423 // that whatever improvement you sought is in fact achieved. Changes that
424 // don't measurably improve performance are discouraged.
425
426 // The common path, meant to be hot: if we point directly at the observed object,
427 // just return that. This is also where null is returned if the pointer is null.
428 // `HasObserved()` is a simple bitmask test, and `CastAsObserved` is a binary
429 // NO-OP.
430
431 if (m_ptr.HasObserved())
432 return m_ptr.CastAsObserved();
433
434 // Otherwise, we must be pointing to a control block: get the pointed-to
435 // observed from the control block. Since such use case is meant to be
436 // rare, the overhead of pointer chasing (one extra layer of indirection)
437 // is acceptable.
438
439 auto *const observed = m_ptr.CastAsControlBlock()->Get();
440 if (observed)
441 return observed;
442
443 // We have a control block, but the observed object is gone: we dereference
444 // the zombie control block, to deallocate it as soon as possible, and we
445 // reset ourselves to null. This happens only once per observed object, and
446 // subsequent calls will go via the common path.
447
448 return DerefControlBlock(); // returns null - allows a tail call optimization
449 }
450
451 void base_reset(Observed *obj = nullptr) noexcept;
452
456 static int CmpAddresses(const void *a, const void *b) noexcept {
457 const auto ia = reinterpret_cast<uintptr_t>(a);
458 const auto ib = reinterpret_cast<uintptr_t>(b);
459 return (ia > ib) - (ia < ib);
460 }
461
462public:
463 template <typename U>
464 static bool constexpr is_pointer() {
465 return std::is_same<U, decltype(nullptr)>::value
466 || (std::is_pointer<U>::value && std::is_convertible<U, Observed*>::value);
467 }
468
469 explicit operator bool() const noexcept { return base_get(); }
470
471 inline void reset() noexcept { base_reset(); }
472
474 auto cmpPointers(const CellPtrBase &o) const noexcept { return m_ptr.GetImpl() - o.m_ptr.GetImpl(); }
475
487 auto cmpObjects(const CellPtrBase &o) const noexcept {
488 return CmpAddresses(base_get(), o.base_get());
489 }
490
492 auto cmpObjects(const Observed *o) const noexcept {
493 return CmpAddresses(base_get(), o);
494 }
495
496 static size_t GetLiveInstanceCount() noexcept { return m_instanceCount; }
497
498 bool IsNull() const { return !m_ptr.GetImpl(); }
499 bool HasOneObserved() const { return m_ptr.GetObserved(); }
500 bool HasControlBlock() const { return m_ptr.GetControlBlock(); }
501};
502
503static_assert(alignof(Observed) >= 4, "Observed doesn't have minimum viable alignment");
504static_assert(alignof(CellPtrBase) >= 4, "CellPtrBase doesn't have minimum viable alignment");
505
537template <typename T>
538class CellPtr final : public CellPtrBase
539{
540 template <typename U>
541 // cppcheck-suppress duplInheritedMember
542 static bool constexpr is_pointer() {
543 return std::is_same<U, decltype(nullptr)>::value
544 || (std::is_pointer<U>::value && std::is_convertible<U, pointer>::value);
545 }
546public:
547 using value_type = T;
548 using pointer = T*;
549 using const_pointer = const T*;
550 using reference = T&;
551
552 CellPtr() noexcept = default;
553
554 // Observers
555 //
556 pointer get() const noexcept;
557 inline reference operator*() const noexcept { return *get(); }
558 inline pointer operator->() const noexcept { return get(); }
559
560#if CELLPTR_CAST_TO_PTR
561 operator pointer() const noexcept { return get(); }
562#endif
563
564 template <typename PtrT, typename std::enable_if<std::is_pointer<PtrT>::value, bool>::type = true>
565 PtrT CastAs() const noexcept;
566
567 // Operations with NULL and integers in general
568 //
569 explicit CellPtr(int) = delete;
570 explicit CellPtr(void *) = delete;
571
572 // Operations with nullptr_t
573 //
574 // cppcheck-suppress duplInheritedMember
575 void reset() noexcept { base_reset(); }
576 explicit CellPtr(decltype(nullptr)) noexcept {}
577 CellPtr &operator=(decltype(nullptr)) noexcept { base_reset(); return *this; }
578 bool operator==(decltype(nullptr)) const noexcept { return !static_cast<bool>(*this); }
579 bool operator!=(decltype(nullptr)) const noexcept { return static_cast<bool>(*this); }
580
581 // Operations with convertible-to-pointer types
582 //
583 template <typename U, typename std::enable_if<is_pointer<U>(), bool>::type = true>
584 explicit CellPtr(U obj) noexcept : CellPtrBase(obj) {}
585
586 template <typename U, typename std::enable_if<is_pointer<U>(), bool>::type = true>
587 CellPtr &operator=(U obj) noexcept
588 {
589 base_reset(obj);
590 return *this;
591 }
592
593 template <typename U, typename std::enable_if<is_pointer<U>(), bool>::type = true>
594 void reset(U obj) noexcept
595 { base_reset(obj); }
596 // Operations with compatible CellPtrs
597 //
598 CellPtr(CellPtr &o) noexcept : CellPtrBase(o) {}
599 CellPtr(CellPtr &&o) noexcept : CellPtrBase(std::move(o)) {}
600 CellPtr &operator=(const CellPtr &o) noexcept { CellPtrBase::operator=(o); return *this; }
601 CellPtr &operator=(CellPtr &&o) noexcept { CellPtrBase::operator=(std::move(o)); return *this; }
602
603 template <typename U,
604 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
605 // cppcheck-suppress noExplicitConstructor
606 CellPtr(CellPtr<U> &&o) noexcept : CellPtrBase(o) {}
607
608 template <typename U,
609 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
610 // cppcheck-suppress noExplicitConstructor
611 CellPtr(const CellPtr<U> &o) noexcept : CellPtrBase(o.get()) {}
612
613 template <typename U,
614 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
615 CellPtr &operator=(CellPtr<U> &&o) noexcept
616 {
617 CellPtrBase::operator=(o);
618 return *this;
619 }
620 template <typename U,
621 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
622 CellPtr &operator=(const CellPtr<U> &o) noexcept
623 {
624 CellPtrBase::operator=(o);
625 return *this;
626 }
627
628#if !CELLPTR_CAST_TO_PTR
629 template <typename U,
630 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
631 bool operator==(const CellPtr<U> &ptr) const noexcept { return cmpControlBlocks(ptr) == 0; }
632 template <typename U,
633 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
634 bool operator!=(const CellPtr<U> &ptr) const noexcept { return cmpControlBlocks(ptr) != 0; }
635 template <typename U,
636 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
637 bool operator<(const CellPtr<U> &ptr) const noexcept { return cmpObjects(ptr) < 0; }
638#endif
639
640 // Operations with compatible unique_ptr
641 //
642 template <typename U, typename Del>
643 explicit CellPtr(std::unique_ptr<U, Del> &&) = delete;
644
645 template <typename U, typename Del,
646 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
647 explicit CellPtr(const std::unique_ptr<U, Del> &ptr) noexcept : CellPtrBase(ptr.get()) {}
648
649 template <typename U, typename Del>
650 CellPtr &operator=(std::unique_ptr<U, Del> &&) = delete;
651
652 template <typename U, typename Del,
653 typename std::enable_if<std::is_convertible<typename std::add_pointer<U>::type, pointer>::value, bool>::type = true>
654 CellPtr &operator=(const std::unique_ptr<U, Del> &o) noexcept
655 { return *this = o.get(); }
656};
657
658//
659
660template <typename T> typename
661CellPtr<T>::pointer CellPtr<T>::get() const noexcept { return static_cast<pointer>(base_get()); }
662
667template <>
669
670//
671
672template <typename T, typename U>
673bool operator==(const CellPtr<T> &left, const CellPtr<U> &right) noexcept { return left.cmpPointers(right) == 0; }
674
675#if !CELLPTR_CAST_TO_PTR
676template <typename T, typename U,
677 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
678bool operator==(U left, const CellPtr<T> &right) noexcept { return right.cmpObjects(left) == 0; }
679#endif
680
681template <typename T, typename U,
682 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
683bool operator==(const CellPtr<T> &left, U right) noexcept { return left.cmpObjects(right) == 0; }
684
685template <typename T, typename U>
686bool operator!=(const CellPtr<T> &left, const CellPtr<U> &right) noexcept { return left.cmpPointers(right) != 0; }
687
688#if !CELLPTR_CAST_TO_PTR
689template <typename T, typename U,
690 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
691bool operator!=(U left, const CellPtr<T> &right) noexcept { return right.cmpObjects(left) != 0; }
692#endif
693
694template <typename T, typename U,
695 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
696bool operator!=(const CellPtr<T> &left, U right) noexcept { return left.cmpObjects(right) != 0; }
697
698template <typename T, typename U>
699bool operator<(const CellPtr<T> &left, const CellPtr<U> &right) noexcept { return left.cmpObjects(right) < 0; }
700
701#if !CELLPTR_CAST_TO_PTR
702template <typename T, typename U,
703 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
704bool operator<(U left, const CellPtr<T> &right) noexcept { return right.cmpObjects(left) > 0; }
705#endif
706
707template <typename T, typename U,
708 typename std::enable_if<CellPtrBase::is_pointer<U>(), bool>::type = true>
709bool operator<(const CellPtr<T> &left, U right) noexcept { return left.cmpObjects(right) < 0; }
710
711//
712
715template<typename Derived, typename Base>
716std::unique_ptr<Derived> static_unique_ptr_cast(std::unique_ptr<Base>&& p) noexcept
717{
718 auto d = static_cast<Derived *>(p.release());
719 return std::unique_ptr<Derived>(d);
720 // Note: We don't move the deleter, since it's not special.
721}
722
725template<typename Derived, typename Base>
726std::unique_ptr<Derived> dynamic_unique_ptr_cast(std::unique_ptr<Base>&& p) noexcept
727{
728 auto d = dynamic_cast<Derived *>(p.get());
729 if (d)
730 (void) p.release();
731 return std::unique_ptr<Derived>(std::move(d));
732 // Note: We don't move the deleter, since it's not special.
733}
734
735#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:716
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:726
An implementation detail for the type-specific templated cell pointers.
Definition: CellPtr.h:286
static int CmpAddresses(const void *a, const void *b) noexcept
A total order over two (possibly unrelated) addresses, usable with < 0 / == 0 / > 0....
Definition: CellPtr.h:456
auto cmpPointers(const CellPtrBase &o) const noexcept
This is exactly like the spaceship operator in C++20.
Definition: CellPtr.h:474
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:487
auto cmpObjects(const Observed *o) const noexcept
This is the spaceship operator acting on pointed-to objects.
Definition: CellPtr.h:492
A weak non-owning pointer that becomes null whenever the observed object is destroyed.
Definition: CellPtr.h:539
The base class all cell types the worksheet can consist of are derived from.
Definition: Cell.h:148
A cell grouping input (and, if there is one, also the output) cell to a foldable item.
Definition: GroupCell.h:88
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